File: System\Net\Http\SocketsHttpHandler\Metrics\ConnectionMetrics.cs
Web Access
Project: src\runtime\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.Collections.Generic;
using System.Diagnostics;

namespace System.Net.Http.Metrics
{
    internal sealed class ConnectionMetrics
    {
        private readonly SocketsHttpHandlerMetrics _metrics;
        private readonly bool _openConnectionsEnabled;
        private readonly string _protocolVersionTag;
        private readonly string _schemeTag;
        private readonly string _hostTag;
        private readonly int _portTag;
        private readonly string? _peerAddressTag;
        private bool _currentlyIdle;

        public ConnectionMetrics(SocketsHttpHandlerMetrics metrics, string protocolVersion, string scheme, string host, int port, string? peerAddress)
        {
            _metrics = metrics;
            _openConnectionsEnabled = _metrics.OpenConnections.Enabled;
            _protocolVersionTag = protocolVersion;
            _schemeTag = scheme;
            _hostTag = host;
            _portTag = port;
            _peerAddressTag = peerAddress;
        }

        // TagList is a huge struct, so we avoid storing it in a field to reduce the amount we allocate on the heap.
        private TagList GetTags()
        {
            TagList tags = default;

            tags.Add("network.protocol.version", _protocolVersionTag);
            tags.Add("url.scheme", _schemeTag);
            tags.Add("server.address", _hostTag);
            tags.Add("server.port", DiagnosticsHelper.GetBoxedInt32(_portTag));

            if (_peerAddressTag is not null)
            {
                tags.Add("network.peer.address", _peerAddressTag);
            }

            return tags;
        }

        private OpenConnectionsTagKey CreateTagKey(bool idle) =>
            new OpenConnectionsTagKey(_protocolVersionTag, _schemeTag, _hostTag, _portTag, idle, _peerAddressTag);

        public void ConnectionEstablished()
        {
            if (_openConnectionsEnabled)
            {
                lock (this)
                {
                    _currentlyIdle = true;
                    _metrics.OpenConnectionsTracker.Increment(CreateTagKey(idle: true));
                }
            }
        }

        public void ConnectionClosed(long durationMs)
        {
            if (_metrics.ConnectionDuration.Enabled)
            {
                _metrics.ConnectionDuration.Record(durationMs / 1000d, GetTags());
            }

            if (_openConnectionsEnabled)
            {
                lock (this)
                {
                    _metrics.OpenConnectionsTracker.Decrement(CreateTagKey(idle: _currentlyIdle));
                }
            }
        }

        public void IdleStateChanged(bool idle)
        {
            if (_openConnectionsEnabled && _currentlyIdle != idle)
            {
                lock (this)
                {
                    _currentlyIdle = idle;
                    _metrics.OpenConnectionsTracker.Decrement(CreateTagKey(idle: !idle));
                    _metrics.OpenConnectionsTracker.Increment(CreateTagKey(idle: idle));
                }
            }
        }
    }
}