< Summary

Information
Class: System.Net.Http.DiagnosticsHelper
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\DiagnosticsHelper.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 71
Coverable lines: 71
Total lines: 123
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 56
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
GetMethodTag(...)0%220%
GetProtocolVersionString(...)0%12120%
GetServerAddress(...)0%880%
TryGetErrorType(...)0%24240%
GetBoxedInt32(...)0%440%
GetErrorStatusCodeString(...)0%660%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\DiagnosticsHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.Metrics;
 7using System.Threading;
 8
 9namespace System.Net.Http
 10{
 11    internal static class DiagnosticsHelper
 12    {
 13        // OTel bucket boundary recommendation for 'http.request.duration':
 14        // https://github.com/open-telemetry/semantic-conventions/blob/release/v1.23.x/docs/http/http-metrics.md#metric-
 15        // We are using the same boundaries for durations which are not expected to be longer than an HTTP request.
 016        public static InstrumentAdvice<double> ShortHistogramAdvice { get; } = new()
 017        {
 018            HistogramBucketBoundaries = [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10]
 019        };
 20
 21        internal static KeyValuePair<string, object?> GetMethodTag(HttpMethod method, out bool isUnknownMethod)
 022        {
 23            // Return canonical names for known methods and "_OTHER" for unknown ones.
 024            HttpMethod? known = HttpMethod.GetKnownMethod(method.Method);
 025            isUnknownMethod = known is null;
 026            return new KeyValuePair<string, object?>("http.request.method", isUnknownMethod ? "_OTHER" : known!.Method);
 027        }
 28
 029        internal static string GetProtocolVersionString(Version httpVersion) => (httpVersion.Major, httpVersion.Minor) s
 030        {
 031            (1, 0) => "1.0",
 032            (1, 1) => "1.1",
 033            (2, 0) => "2",
 034            (3, 0) => "3",
 035            _ => httpVersion.ToString()
 036        };
 37
 38        // Picks the value of the 'server.address' tag following rules specified in
 39        // https://github.com/open-telemetry/semantic-conventions/blob/728e5d1/docs/http/http-spans.md#http-client-span
 40        // When there is no proxy, we need to prioritize the contents of the Host header.
 41        // Note that this is a best-effort guess, e.g. we are not checking if proxy.GetProxy(uri) returns null.
 42        public static string GetServerAddress(HttpRequestMessage request, IWebProxy? proxy)
 043        {
 044            Debug.Assert(request.RequestUri is not null);
 045            if ((proxy is null || proxy.IsBypassed(request.RequestUri)) && request.HasHeaders && request.Headers.Host is
 046            {
 047                return HttpUtilities.ParseHostNameFromHeader(hostHeader);
 48            }
 49
 050            return request.RequestUri.IdnHost;
 051        }
 52
 53        public static bool TryGetErrorType(HttpResponseMessage? response, Exception? exception, out string? errorType)
 054        {
 055            if (response is not null)
 056            {
 057                int statusCode = (int)response.StatusCode;
 58
 59                // In case the status code indicates a client or a server error, return the string representation of the
 60                // See the paragraph Status and the definition of 'error.type' in
 61                // https://github.com/open-telemetry/semantic-conventions/blob/release/v1.23.x/docs/http/http-spans.md#S
 062                if (statusCode >= 400 && statusCode <= 599)
 063                {
 064                    errorType = GetErrorStatusCodeString(statusCode);
 065                    return true;
 66                }
 067            }
 68
 069            if (exception is null)
 070            {
 071                errorType = null;
 072                return false;
 73            }
 74
 075            Debug.Assert(Enum.GetValues<HttpRequestError>().Length == 12, "We need to extend the mapping in case new val
 076            errorType = (exception as HttpRequestException)?.HttpRequestError switch
 077            {
 078                HttpRequestError.NameResolutionError => "name_resolution_error",
 079                HttpRequestError.ConnectionError => "connection_error",
 080                HttpRequestError.SecureConnectionError => "secure_connection_error",
 081                HttpRequestError.HttpProtocolError => "http_protocol_error",
 082                HttpRequestError.ExtendedConnectNotSupported => "extended_connect_not_supported",
 083                HttpRequestError.VersionNegotiationError => "version_negotiation_error",
 084                HttpRequestError.UserAuthenticationError => "user_authentication_error",
 085                HttpRequestError.ProxyTunnelError => "proxy_tunnel_error",
 086                HttpRequestError.InvalidResponse => "invalid_response",
 087                HttpRequestError.ResponseEnded => "response_ended",
 088                HttpRequestError.ConfigurationLimitExceeded => "configuration_limit_exceeded",
 089
 090                // Fall back to the exception type name in case of HttpRequestError.Unknown or when exception is not an 
 091                _ => exception.GetType().FullName!
 092            };
 093            return true;
 094        }
 95
 96        private static object[]? s_boxedStatusCodes;
 97        private static string[]? s_statusCodeStrings;
 98
 99#pragma warning disable CA1859 // we explicitly box here
 100        // Returns a pooled object if 'value' is between 0-512,
 101        // saving allocations for standard HTTP status codes and small port tag values.
 102        public static object GetBoxedInt32(int value)
 0103        {
 0104            object[] boxes = LazyInitializer.EnsureInitialized(ref s_boxedStatusCodes, static () => new object[512]);
 105
 0106            return (uint)value < (uint)boxes.Length
 0107                ? boxes[value] ??= value
 0108                : value;
 0109        }
 110#pragma warning restore
 111
 112        private static string GetErrorStatusCodeString(int statusCode)
 0113        {
 0114            Debug.Assert(statusCode >= 400 && statusCode <= 599);
 115
 0116            string[] strings = LazyInitializer.EnsureInitialized(ref s_statusCodeStrings, static () => new string[200]);
 0117            int index = statusCode - 400;
 0118            return (uint)index < (uint)strings.Length
 0119                ? strings[index] ??= statusCode.ToString()
 0120                : statusCode.ToString();
 0121        }
 122    }
 123}