File: System\Net\Sockets\SocketsTelemetry.cs
Web Access
Project: src\src\libraries\System.Net.Sockets\src\System.Net.Sockets.csproj (System.Net.Sockets)
// 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.Tracing;
using System.Threading;
 
namespace System.Net.Sockets
{
    [EventSource(Name = "System.Net.Sockets")]
    internal sealed class SocketsTelemetry : EventSource
    {
        private const string ActivitySourceName = "Experimental.System.Net.Sockets";
        private const string ConnectActivityName = ActivitySourceName + ".Connect";
        private static readonly ActivitySource s_connectActivitySource = new ActivitySource(ActivitySourceName);
 
        public static readonly SocketsTelemetry Log = new SocketsTelemetry();
 
        private PollingCounter? _currentOutgoingConnectAttemptsCounter;
        private PollingCounter? _outgoingConnectionsEstablishedCounter;
        private PollingCounter? _incomingConnectionsEstablishedCounter;
        private PollingCounter? _bytesReceivedCounter;
        private PollingCounter? _bytesSentCounter;
        private PollingCounter? _datagramsReceivedCounter;
        private PollingCounter? _datagramsSentCounter;
 
        private long _currentOutgoingConnectAttempts;
        private long _outgoingConnectionsEstablished;
        private long _incomingConnectionsEstablished;
        private long _bytesReceived;
        private long _bytesSent;
        private long _datagramsReceived;
        private long _datagramsSent;
 
        [Event(1, Level = EventLevel.Informational)]
        private void ConnectStart(string? address)
        {
            WriteEvent(eventId: 1, address);
        }
 
        [Event(2, Level = EventLevel.Informational)]
        private void ConnectStop()
        {
            if (IsEnabled(EventLevel.Informational, EventKeywords.All))
            {
                WriteEvent(eventId: 2);
            }
        }
 
        [Event(3, Level = EventLevel.Error)]
        private void ConnectFailed(SocketError error, string? exceptionMessage)
        {
            if (IsEnabled(EventLevel.Error, EventKeywords.All))
            {
                WriteEvent(eventId: 3, (int)error, exceptionMessage);
            }
        }
 
        [Event(4, Level = EventLevel.Informational)]
        private void AcceptStart(string? address)
        {
            WriteEvent(eventId: 4, address);
        }
 
        [Event(5, Level = EventLevel.Informational)]
        private void AcceptStop()
        {
            if (IsEnabled(EventLevel.Informational, EventKeywords.All))
            {
                WriteEvent(eventId: 5);
            }
        }
 
        [Event(6, Level = EventLevel.Error)]
        private void AcceptFailed(SocketError error, string? exceptionMessage)
        {
            if (IsEnabled(EventLevel.Error, EventKeywords.All))
            {
                WriteEvent(eventId: 6, (int)error, exceptionMessage);
            }
        }
 
        [NonEvent]
        public Activity? ConnectStart(SocketAddress address, ProtocolType protocolType, EndPoint endPoint, bool keepActivityCurrent)
        {
            Interlocked.Increment(ref _currentOutgoingConnectAttempts);
 
            if (IsEnabled(EventLevel.Informational, EventKeywords.All))
            {
                ConnectStart(address.ToString());
            }
 
            Activity? activity = null;
            if (s_connectActivitySource.HasListeners())
            {
                Activity? activityToReset = keepActivityCurrent ? Activity.Current : null;
                activity = s_connectActivitySource.StartActivity(ConnectActivityName);
                if (keepActivityCurrent)
                {
                    // Do not overwrite Activity.Current in the caller's ExecutionContext.
                    Activity.Current = activityToReset;
                }
            }
 
            if (activity is not null)
            {
                if (endPoint is IPEndPoint ipEndPoint)
                {
                    int port = ipEndPoint.Port;
                    activity.DisplayName = $"socket connect {ipEndPoint.Address}:{port}";
                    if (activity.IsAllDataRequested)
                    {
                        activity.SetTag("network.peer.address", ipEndPoint.Address.ToString());
                        activity.SetTag("network.peer.port", port);
                        activity.SetTag("network.type", ipEndPoint.AddressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
                        if (protocolType is ProtocolType.Tcp)
                        {
                            SetNetworkTransport(activity, "tcp");
                        }
                        else if (protocolType is ProtocolType.Udp)
                        {
                            SetNetworkTransport(activity, "udp");
                        }
                    }
                }
                else if (endPoint is UnixDomainSocketEndPoint udsEndPoint)
                {
                    string peerAddress = udsEndPoint.ToString();
                    activity.DisplayName = $"socket connect {peerAddress}";
 
                    if (activity.IsAllDataRequested)
                    {
                        activity.SetTag("network.peer.address", peerAddress);
                        SetNetworkTransport(activity, "unix");
                    }
                }
            }
 
            static void SetNetworkTransport(Activity activity, string transportType) => activity.SetTag("network.transport", transportType);
 
            return activity;
        }
 
        [NonEvent]
        public void AfterConnect(SocketError error, Activity? activity, string? exceptionMessage = null)
        {
            long newCount = Interlocked.Decrement(ref _currentOutgoingConnectAttempts);
            Debug.Assert(newCount >= 0);
 
            if (activity is not null)
            {
                if (error != SocketError.Success)
                {
                    activity.SetStatus(ActivityStatusCode.Error);
                    activity.SetTag("error.type", GetErrorType(error));
                }
 
                activity.Stop();
            }
 
            if (error == SocketError.Success)
            {
                Debug.Assert(exceptionMessage is null);
                Interlocked.Increment(ref _outgoingConnectionsEstablished);
            }
            else
            {
                ConnectFailed(error, exceptionMessage);
            }
 
            ConnectStop();
        }
 
        [NonEvent]
        public void AcceptStart(SocketAddress address)
        {
            if (IsEnabled(EventLevel.Informational, EventKeywords.All))
            {
                AcceptStart(address.ToString());
            }
        }
 
        [NonEvent]
        public void AcceptStart(EndPoint address)
        {
            if (IsEnabled(EventLevel.Informational, EventKeywords.All))
            {
                AcceptStart(address.Serialize().ToString());
            }
        }
 
        [NonEvent]
        public void AfterAccept(SocketError error, string? exceptionMessage = null)
        {
            if (error == SocketError.Success)
            {
                Debug.Assert(exceptionMessage is null);
                Interlocked.Increment(ref _incomingConnectionsEstablished);
            }
            else
            {
                AcceptFailed(error, exceptionMessage);
            }
 
            AcceptStop();
        }
 
        [NonEvent]
        public void BytesReceived(int count)
        {
            Debug.Assert(count >= 0);
            Interlocked.Add(ref _bytesReceived, count);
        }
 
        [NonEvent]
        public void BytesSent(int count)
        {
            Debug.Assert(count >= 0);
            Interlocked.Add(ref _bytesSent, count);
        }
 
        [NonEvent]
        public void DatagramReceived()
        {
            Interlocked.Increment(ref _datagramsReceived);
        }
 
        [NonEvent]
        public void DatagramSent()
        {
            Interlocked.Increment(ref _datagramsSent);
        }
 
        private static string GetErrorType(SocketError socketError) => socketError switch
        {
            // Common connect() errors expected to be seen:
            // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect#return-value
            // https://man7.org/linux/man-pages/man2/connect.2.html
            SocketError.NetworkDown => "network_down",
            SocketError.AddressAlreadyInUse => "address_already_in_use",
            SocketError.Interrupted => "interrupted",
            SocketError.InProgress => "in_progress",
            SocketError.AlreadyInProgress => "already_in_progress",
            SocketError.AddressNotAvailable => "address_not_available",
            SocketError.AddressFamilyNotSupported => "address_family_not_supported",
            SocketError.ConnectionRefused => "connection_refused",
            SocketError.Fault => "fault",
            SocketError.InvalidArgument => "invalid_argument",
            SocketError.IsConnected => "is_connected",
            SocketError.NetworkUnreachable => "network_unreachable",
            SocketError.HostUnreachable => "host_unreachable",
            SocketError.NoBufferSpaceAvailable => "no_buffer_space_available",
            SocketError.TimedOut => "timed_out",
            SocketError.AccessDenied => "access_denied",
            SocketError.ProtocolType => "protocol_type",
 
            _ => "_OTHER"
        };
 
        protected override void OnEventCommand(EventCommandEventArgs command)
        {
            if (command.Command == EventCommand.Enable)
            {
                // This is the convention for initializing counters in the RuntimeEventSource (lazily on the first enable command).
 
                _currentOutgoingConnectAttemptsCounter ??= new PollingCounter("current-outgoing-connect-attempts", this, () => Interlocked.Read(ref _currentOutgoingConnectAttempts))
                {
                    DisplayName = "Current Outgoing Connect Attempts",
                };
                _outgoingConnectionsEstablishedCounter ??= new PollingCounter("outgoing-connections-established", this, () => Interlocked.Read(ref _outgoingConnectionsEstablished))
                {
                    DisplayName = "Outgoing Connections Established",
                };
                _incomingConnectionsEstablishedCounter ??= new PollingCounter("incoming-connections-established", this, () => Interlocked.Read(ref _incomingConnectionsEstablished))
                {
                    DisplayName = "Incoming Connections Established",
                };
                _bytesReceivedCounter ??= new PollingCounter("bytes-received", this, () => Interlocked.Read(ref _bytesReceived))
                {
                    DisplayName = "Bytes Received",
                };
                _bytesSentCounter ??= new PollingCounter("bytes-sent", this, () => Interlocked.Read(ref _bytesSent))
                {
                    DisplayName = "Bytes Sent",
                };
                _datagramsReceivedCounter ??= new PollingCounter("datagrams-received", this, () => Interlocked.Read(ref _datagramsReceived))
                {
                    DisplayName = "Datagrams Received",
                };
                _datagramsSentCounter ??= new PollingCounter("datagrams-sent", this, () => Interlocked.Read(ref _datagramsSent))
                {
                    DisplayName = "Datagrams Sent",
                };
            }
        }
    }
}