File: System\Net\Http\HttpTelemetry.cs
Web Access
Project: src\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Text;
using System.Threading;
 
namespace System.Net.Http
{
    [EventSource(Name = "System.Net.Http")]
    internal sealed partial class HttpTelemetry : EventSource
    {
        public static readonly HttpTelemetry Log = new HttpTelemetry();
 
        public static class Keywords
        {
            public const EventKeywords RequestFailedDetailed = (EventKeywords)1;
        }
 
        private long _startedRequests;
        private long _stoppedRequests;
        private long _failedRequests;
 
        private long _openedHttp11Connections;
        private long _openedHttp20Connections;
        private long _openedHttp30Connections;
 
        // NOTE
        // - The 'Start' and 'Stop' suffixes on the following event names have special meaning in EventSource. They
        //   enable creating 'activities'.
        //   For more information, take a look at the following blog post:
        //   https://blogs.msdn.microsoft.com/vancem/2015/09/14/exploring-eventsource-activity-correlation-and-causation-features/
        // - A stop event's event id must be next one after its start event.
 
        [Event(1, Level = EventLevel.Informational)]
        private void RequestStart(string scheme, string host, int port, string pathAndQuery, byte versionMajor, byte versionMinor, HttpVersionPolicy versionPolicy)
        {
            Interlocked.Increment(ref _startedRequests);
            if (!GlobalHttpSettings.DiagnosticsHandler.DisableUriRedaction)
            {
                int queryIndex = pathAndQuery.IndexOf('?');
                if (queryIndex >= 0 && queryIndex < (pathAndQuery.Length - 1))
                {
                    pathAndQuery = $"{pathAndQuery.AsSpan(0, queryIndex + 1)}*";
                }
            }
            WriteEvent(eventId: 1, scheme, host, port, pathAndQuery, versionMajor, versionMinor, versionPolicy);
        }
 
        [NonEvent]
        public void RequestStart(HttpRequestMessage request)
        {
            Debug.Assert(request.RequestUri != null && request.RequestUri.IsAbsoluteUri);
 
            RequestStart(
                request.RequestUri.Scheme,
                request.RequestUri.IdnHost,
                request.RequestUri.Port,
                request.RequestUri.PathAndQuery,
                (byte)request.Version.Major,
                (byte)request.Version.Minor,
                request.VersionPolicy);
        }
 
        [NonEvent]
        public void RequestStop(HttpResponseMessage? response)
        {
            RequestStop(response is null ? -1 : (int)response.StatusCode);
        }
 
        [Event(2, Level = EventLevel.Informational, Version = 1)]
        private void RequestStop(int statusCode)
        {
            Interlocked.Increment(ref _stoppedRequests);
            WriteEvent(eventId: 2, statusCode);
        }
 
        [NonEvent]
        public void RequestFailed(Exception exception)
        {
            Interlocked.Increment(ref _failedRequests);
 
            if (IsEnabled(EventLevel.Error, EventKeywords.None))
            {
                RequestFailed(exceptionMessage: exception.Message);
 
                if (IsEnabled(EventLevel.Error, Keywords.RequestFailedDetailed))
                {
                    RequestFailedDetailed(exception: exception.ToString());
                }
            }
        }
 
        [Event(3, Level = EventLevel.Error, Version = 1)]
        private void RequestFailed(string exceptionMessage)
        {
            WriteEvent(eventId: 3, exceptionMessage);
        }
 
        [NonEvent]
        private void ConnectionEstablished(byte versionMajor, byte versionMinor, long connectionId, string scheme, string host, int port, IPEndPoint? remoteEndPoint)
        {
            string? remoteAddress = remoteEndPoint?.Address?.ToString();
            ConnectionEstablished(versionMajor, versionMinor, connectionId, scheme, host, port, remoteAddress);
        }
 
        [Event(4, Level = EventLevel.Informational, Version = 1)]
        private void ConnectionEstablished(byte versionMajor, byte versionMinor, long connectionId, string scheme, string host, int port, string? remoteAddress)
        {
            WriteEvent(eventId: 4, versionMajor, versionMinor, connectionId, scheme, host, port, remoteAddress);
        }
 
        [Event(5, Level = EventLevel.Informational, Version = 1)]
        private void ConnectionClosed(byte versionMajor, byte versionMinor, long connectionId)
        {
            WriteEvent(eventId: 5, versionMajor, versionMinor, connectionId);
        }
 
        [Event(6, Level = EventLevel.Informational)]
        private void RequestLeftQueue(double timeOnQueueMilliseconds, byte versionMajor, byte versionMinor)
        {
            WriteEvent(eventId: 6, timeOnQueueMilliseconds, versionMajor, versionMinor);
        }
 
        [Event(7, Level = EventLevel.Informational, Version = 1)]
        public void RequestHeadersStart(long connectionId)
        {
            WriteEvent(eventId: 7, connectionId);
        }
 
        [Event(8, Level = EventLevel.Informational)]
        public void RequestHeadersStop()
        {
            WriteEvent(eventId: 8);
        }
 
        [Event(9, Level = EventLevel.Informational)]
        public void RequestContentStart()
        {
            WriteEvent(eventId: 9);
        }
 
        [Event(10, Level = EventLevel.Informational)]
        public void RequestContentStop(long contentLength)
        {
            WriteEvent(eventId: 10, contentLength);
        }
 
        [Event(11, Level = EventLevel.Informational)]
        public void ResponseHeadersStart()
        {
            WriteEvent(eventId: 11);
        }
 
        [Event(12, Level = EventLevel.Informational, Version = 1)]
        public void ResponseHeadersStop(int statusCode)
        {
            WriteEvent(eventId: 12, statusCode);
        }
 
        [Event(13, Level = EventLevel.Informational)]
        public void ResponseContentStart()
        {
            WriteEvent(eventId: 13);
        }
 
        [Event(14, Level = EventLevel.Informational)]
        public void ResponseContentStop()
        {
            WriteEvent(eventId: 14);
        }
 
        [Event(15, Level = EventLevel.Error, Keywords = Keywords.RequestFailedDetailed)]
        private void RequestFailedDetailed(string exception)
        {
            WriteEvent(eventId: 15, exception);
        }
 
        [Event(16, Level = EventLevel.Informational)]
        public void Redirect(string redirectUri)
        {
            WriteEvent(eventId: 16, redirectUri);
        }
 
        [NonEvent]
        public void Redirect(Uri redirectUri)
        {
            if (!GlobalHttpSettings.DiagnosticsHandler.DisableUriRedaction)
            {
                string pathAndQuery = redirectUri.PathAndQuery;
                int queryIndex = pathAndQuery.IndexOf('?');
                if (queryIndex >= 0 && queryIndex < (pathAndQuery.Length - 1))
                {
                    UriBuilder uriBuilder = new UriBuilder(redirectUri)
                    {
                        Query = "*"
                    };
                    redirectUri = uriBuilder.Uri;
                }
            }
            Redirect(redirectUri.AbsoluteUri);
        }
 
        [NonEvent]
        public void Http11ConnectionEstablished(long connectionId, string scheme, string host, int port, IPEndPoint? remoteEndPoint)
        {
            Interlocked.Increment(ref _openedHttp11Connections);
            ConnectionEstablished(versionMajor: 1, versionMinor: 1, connectionId, scheme, host, port, remoteEndPoint);
        }
 
        [NonEvent]
        public void Http11ConnectionClosed(long connectionId)
        {
            long count = Interlocked.Decrement(ref _openedHttp11Connections);
            Debug.Assert(count >= 0);
            ConnectionClosed(versionMajor: 1, versionMinor: 1, connectionId);
        }
 
        [NonEvent]
        public void Http20ConnectionEstablished(long connectionId, string scheme, string host, int port, IPEndPoint? remoteEndPoint)
        {
            Interlocked.Increment(ref _openedHttp20Connections);
            ConnectionEstablished(versionMajor: 2, versionMinor: 0, connectionId, scheme, host, port, remoteEndPoint);
        }
 
        [NonEvent]
        public void Http20ConnectionClosed(long connectionId)
        {
            long count = Interlocked.Decrement(ref _openedHttp20Connections);
            Debug.Assert(count >= 0);
            ConnectionClosed(versionMajor: 2, versionMinor: 0, connectionId);
        }
 
        [NonEvent]
        public void Http30ConnectionEstablished(long connectionId, string scheme, string host, int port, IPEndPoint? remoteEndPoint)
        {
            Interlocked.Increment(ref _openedHttp30Connections);
            ConnectionEstablished(versionMajor: 3, versionMinor: 0, connectionId, scheme, host, port, remoteEndPoint);
        }
 
        [NonEvent]
        public void Http30ConnectionClosed(long connectionId)
        {
            long count = Interlocked.Decrement(ref _openedHttp30Connections);
            Debug.Assert(count >= 0);
            ConnectionClosed(versionMajor: 3, versionMinor: 0, connectionId);
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Parameters to this method are primitive and are trimmer safe")]
        [NonEvent]
        private unsafe void WriteEvent(int eventId, string? arg1, string? arg2, int arg3, string? arg4, byte arg5, byte arg6, HttpVersionPolicy arg7)
        {
            arg1 ??= "";
            arg2 ??= "";
            arg4 ??= "";
 
            fixed (char* arg1Ptr = arg1)
            fixed (char* arg2Ptr = arg2)
            fixed (char* arg4Ptr = arg4)
            {
                const int NumEventDatas = 7;
                EventData* descrs = stackalloc EventData[NumEventDatas];
 
                descrs[0] = new EventData
                {
                    DataPointer = (IntPtr)(arg1Ptr),
                    Size = (arg1.Length + 1) * sizeof(char)
                };
                descrs[1] = new EventData
                {
                    DataPointer = (IntPtr)(arg2Ptr),
                    Size = (arg2.Length + 1) * sizeof(char)
                };
                descrs[2] = new EventData
                {
                    DataPointer = (IntPtr)(&arg3),
                    Size = sizeof(int)
                };
                descrs[3] = new EventData
                {
                    DataPointer = (IntPtr)(arg4Ptr),
                    Size = (arg4.Length + 1) * sizeof(char)
                };
                descrs[4] = new EventData
                {
                    DataPointer = (IntPtr)(&arg5),
                    Size = sizeof(byte)
                };
                descrs[5] = new EventData
                {
                    DataPointer = (IntPtr)(&arg6),
                    Size = sizeof(byte)
                };
                descrs[6] = new EventData
                {
                    DataPointer = (IntPtr)(&arg7),
                    Size = sizeof(HttpVersionPolicy)
                };
 
                WriteEventCore(eventId, NumEventDatas, descrs);
            }
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Parameters to this method are primitive and are trimmer safe")]
        [NonEvent]
        private unsafe void WriteEvent(int eventId, double arg1, byte arg2, byte arg3)
        {
            const int NumEventDatas = 3;
            EventData* descrs = stackalloc EventData[NumEventDatas];
 
            descrs[0] = new EventData
            {
                DataPointer = (IntPtr)(&arg1),
                Size = sizeof(double)
            };
            descrs[1] = new EventData
            {
                DataPointer = (IntPtr)(&arg2),
                Size = sizeof(byte)
            };
            descrs[2] = new EventData
            {
                DataPointer = (IntPtr)(&arg3),
                Size = sizeof(byte)
            };
 
            WriteEventCore(eventId, NumEventDatas, descrs);
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Parameters to this method are primitive and are trimmer safe")]
        [NonEvent]
        private unsafe void WriteEvent(int eventId, byte arg1, byte arg2, long arg3)
        {
            const int NumEventDatas = 3;
            EventData* descrs = stackalloc EventData[NumEventDatas];
 
            descrs[0] = new EventData
            {
                DataPointer = (IntPtr)(&arg1),
                Size = sizeof(byte)
            };
            descrs[1] = new EventData
            {
                DataPointer = (IntPtr)(&arg2),
                Size = sizeof(byte)
            };
            descrs[2] = new EventData
            {
                DataPointer = (IntPtr)(&arg3),
                Size = sizeof(long)
            };
 
            WriteEventCore(eventId, NumEventDatas, descrs);
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
           Justification = "Parameters to this method are primitive and are trimmer safe")]
        [NonEvent]
        private unsafe void WriteEvent(int eventId, byte arg1, byte arg2, long arg3, string? arg4, string arg5, int arg6, string? arg7)
        {
            arg4 ??= "";
            arg5 ??= "";
            arg7 ??= "";
 
            fixed (char* arg4Ptr = arg4)
            fixed (char* arg5Ptr = arg5)
            fixed (char* arg7Ptr = arg7)
            {
                const int NumEventDatas = 7;
                EventData* descrs = stackalloc EventData[NumEventDatas];
 
                descrs[0] = new EventData
                {
                    DataPointer = (IntPtr)(&arg1),
                    Size = sizeof(byte)
                };
                descrs[1] = new EventData
                {
                    DataPointer = (IntPtr)(&arg2),
                    Size = sizeof(byte)
                };
                descrs[2] = new EventData
                {
                    DataPointer = (IntPtr)(&arg3),
                    Size = sizeof(long)
                };
                descrs[3] = new EventData
                {
                    DataPointer = (IntPtr)arg4Ptr,
                    Size = (arg4.Length + 1) * sizeof(char)
                };
                descrs[4] = new EventData
                {
                    DataPointer = (IntPtr)arg5Ptr,
                    Size = (arg5.Length + 1) * sizeof(char)
                };
                descrs[5] = new EventData
                {
                    DataPointer = (IntPtr)(&arg6),
                    Size = sizeof(int)
                };
                descrs[6] = new EventData
                {
                    DataPointer = (IntPtr)arg7Ptr,
                    Size = (arg7.Length + 1) * sizeof(char)
                };
 
                WriteEventCore(eventId, NumEventDatas, descrs);
            }
        }
    }
}