| File: Resolver\DnsResolver.Telemetry.cs | Web Access |
| Project: src\src\Libraries\Microsoft.Extensions.ServiceDiscovery.Dns\Microsoft.Extensions.ServiceDiscovery.Dns.csproj (Microsoft.Extensions.ServiceDiscovery.Dns) |
// 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.Metrics; namespace Microsoft.Extensions.ServiceDiscovery.Dns.Resolver; internal partial class DnsResolver { internal static class Telemetry { private static readonly Meter s_meter = new Meter("Microsoft.Extensions.ServiceDiscovery.Dns.Resolver"); private static readonly Histogram<double> s_queryDuration = s_meter.CreateHistogram<double>("query.duration", "ms", "DNS query duration"); private static bool IsEnabled() => s_queryDuration.Enabled; public static NameResolutionActivity StartNameResolution(string hostName, QueryType queryType, long startingTimestamp) { if (IsEnabled()) { return new NameResolutionActivity(hostName, queryType, startingTimestamp); } return default; } public static void StopNameResolution(string hostName, QueryType queryType, in NameResolutionActivity activity, object? answers, SendQueryError error, long endingTimestamp) { activity.Stop(answers, error, endingTimestamp, out TimeSpan duration); if (!IsEnabled()) { return; } var hostNameTag = KeyValuePair.Create("dns.question.name", (object?)hostName); var queryTypeTag = KeyValuePair.Create("dns.question.type", (object?)queryType); if (answers is not null) { s_queryDuration.Record(duration.TotalSeconds, hostNameTag, queryTypeTag); } else { var errorTypeTag = KeyValuePair.Create("error.type", (object?)error.ToString()); s_queryDuration.Record(duration.TotalSeconds, hostNameTag, queryTypeTag, errorTypeTag); } } } internal readonly struct NameResolutionActivity { private const string ActivitySourceName = "Microsoft.Extensions.ServiceDiscovery.Dns.Resolver"; private const string ActivityName = ActivitySourceName + ".Resolve"; private static readonly ActivitySource s_activitySource = new ActivitySource(ActivitySourceName); private readonly long _startingTimestamp; private readonly Activity? _activity; // null if activity is not started public NameResolutionActivity(string hostName, QueryType queryType, long startingTimestamp) { _startingTimestamp = startingTimestamp; _activity = s_activitySource.StartActivity(ActivityName, ActivityKind.Client); if (_activity is not null) { _activity.DisplayName = $"Resolving {hostName}"; if (_activity.IsAllDataRequested) { _activity.SetTag("dns.question.name", hostName); _activity.SetTag("dns.question.type", queryType.ToString()); } } } public void Stop(object? answers, SendQueryError error, long endingTimestamp, out TimeSpan duration) { duration = Stopwatch.GetElapsedTime(_startingTimestamp, endingTimestamp); if (_activity is null) { return; } if (_activity.IsAllDataRequested) { if (answers is not null) { static string[] ToStringHelper<T>(T[] array) => array.Select(a => a!.ToString()!).ToArray(); string[]? answersArray = answers switch { ServiceResult[] serviceResults => ToStringHelper(serviceResults), AddressResult[] addressResults => ToStringHelper(addressResults), _ => null }; Debug.Assert(answersArray is not null); _activity.SetTag("dns.answers", answersArray); } else { _activity.SetTag("error.type", error.ToString()); } } if (answers is null) { _activity.SetStatus(ActivityStatusCode.Error); } _activity.Stop(); } } }