Skip to content

Commit

Permalink
Adjust the DNS lookup duration metric (#93254)
Browse files Browse the repository at this point in the history
  • Loading branch information
antonfirsov authored Oct 12, 2023
1 parent 8f54e89 commit 946c524
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 25 deletions.
34 changes: 19 additions & 15 deletions src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ public static string GetHostName()
{
name = NameResolutionPal.GetHostName();
}
catch when (LogFailure(string.Empty, startingTimestamp))
catch (Exception ex) when (LogFailure(string.Empty, startingTimestamp, ex))
{
Debug.Fail("LogFailure should return false");
throw;
}

NameResolutionTelemetry.Log.AfterResolution(string.Empty, startingTimestamp, successful: true);
NameResolutionTelemetry.Log.AfterResolution(string.Empty, startingTimestamp);

if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, name);
return name;
Expand Down Expand Up @@ -394,13 +394,13 @@ private static object GetHostEntryOrAddressesCore(string hostName, bool justAddr
Aliases = aliases
};
}
catch when (LogFailure(hostName, startingTimestamp))
catch (Exception ex) when (LogFailure(hostName, startingTimestamp, ex))
{
Debug.Fail("LogFailure should return false");
throw;
}

NameResolutionTelemetry.Log.AfterResolution(hostName, startingTimestamp, successful: true);
NameResolutionTelemetry.Log.AfterResolution(hostName, startingTimestamp);

return result;
}
Expand Down Expand Up @@ -434,13 +434,13 @@ private static object GetHostEntryOrAddressesCore(IPAddress address, bool justAd
}
Debug.Assert(name != null);
}
catch when (LogFailure(address, startingTimestamp))
catch (Exception ex) when (LogFailure(address, startingTimestamp, ex))
{
Debug.Fail("LogFailure should return false");
throw;
}

NameResolutionTelemetry.Log.AfterResolution(address, startingTimestamp, successful: true);
NameResolutionTelemetry.Log.AfterResolution(address, startingTimestamp);

// Do the forward lookup to get the IPs for that host name
startingTimestamp = NameResolutionTelemetry.Log.BeforeResolution(name);
Expand All @@ -464,13 +464,13 @@ private static object GetHostEntryOrAddressesCore(IPAddress address, bool justAd
AddressList = addresses
};
}
catch when (LogFailure(name, startingTimestamp))
catch (Exception ex) when (LogFailure(name, startingTimestamp, ex))
{
Debug.Fail("LogFailure should return false");
throw;
}

NameResolutionTelemetry.Log.AfterResolution(name, startingTimestamp, successful: true);
NameResolutionTelemetry.Log.AfterResolution(name, startingTimestamp);

// One of three things happened:
// 1. Success.
Expand Down Expand Up @@ -577,7 +577,7 @@ private static Task GetHostEntryOrAddressesCoreAsync(string hostName, bool justR
}

private static Task<T>? GetAddrInfoWithTelemetryAsync<T>(string hostName, bool justAddresses, AddressFamily addressFamily, CancellationToken cancellationToken)
where T : class
where T : class
{
long startingTimestamp = Stopwatch.GetTimestamp();
Task? task = NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses, addressFamily, cancellationToken);
Expand All @@ -594,15 +594,19 @@ private static Task GetHostEntryOrAddressesCoreAsync(string hostName, bool justR
static async Task<T> CompleteAsync(Task task, string hostName, long startingTimestamp)
{
_ = NameResolutionTelemetry.Log.BeforeResolution(hostName);
T? result = null;
Exception? exception = null;
try
{
result = await ((Task<T>)task).ConfigureAwait(false);
return result;
return await ((Task<T>)task).ConfigureAwait(false);
}
catch (Exception ex)
{
exception = ex;
throw;
}
finally
{
NameResolutionTelemetry.Log.AfterResolution(hostName, startingTimestamp, successful: result is not null);
NameResolutionTelemetry.Log.AfterResolution(hostName, startingTimestamp, exception);
}
}
}
Expand All @@ -627,9 +631,9 @@ private static void ValidateHostName(string hostName)
}
}

private static bool LogFailure(object hostNameOrAddress, long? startingTimestamp)
private static bool LogFailure(object hostNameOrAddress, long? startingTimestamp, Exception exception)
{
NameResolutionTelemetry.Log.AfterResolution(hostNameOrAddress, startingTimestamp, successful: false);
NameResolutionTelemetry.Log.AfterResolution(hostNameOrAddress, startingTimestamp, exception);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,35 @@ internal static class NameResolutionMetrics
private static readonly Meter s_meter = new("System.Net.NameResolution");

private static readonly Histogram<double> s_lookupDuration = s_meter.CreateHistogram<double>(
name: "dns.lookups.duration",
name: "dns.lookup.duration",
unit: "s",
description: "Measures the time taken to perform a DNS lookup.");

public static bool IsEnabled() => s_lookupDuration.Enabled;

public static void AfterResolution(TimeSpan duration, string hostName)
public static void AfterResolution(TimeSpan duration, string hostName, Exception? exception)
{
s_lookupDuration.Record(duration.TotalSeconds, KeyValuePair.Create("dns.question.name", (object?)hostName));
var hostNameTag = KeyValuePair.Create("dns.question.name", (object?)hostName);

if (exception is null)
{
s_lookupDuration.Record(duration.TotalSeconds, hostNameTag);
}
else
{
var errorTypeTag = KeyValuePair.Create("error.type", (object?)GetErrorType(exception));
s_lookupDuration.Record(duration.TotalSeconds, hostNameTag, errorTypeTag);
}
}

private static string GetErrorType(Exception exception) => (exception as SocketException)?.SocketErrorCode switch
{
SocketError.HostNotFound => "host_not_found",
SocketError.TryAgain => "try_again",
SocketError.AddressFamilyNotSupported => "address_family_not_supported",
SocketError.NoRecovery => "no_recovery",

_ => exception.GetType().FullName!
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public long BeforeResolution(object hostNameOrAddress)
}

[NonEvent]
public void AfterResolution(object hostNameOrAddress, long? startingTimestamp, bool successful)
public void AfterResolution(object hostNameOrAddress, long? startingTimestamp, Exception? exception = null)
{
Debug.Assert(startingTimestamp.HasValue);
if (startingTimestamp == 0)
Expand All @@ -99,7 +99,7 @@ public void AfterResolution(object hostNameOrAddress, long? startingTimestamp, b

if (IsEnabled(EventLevel.Informational, EventKeywords.None))
{
if (!successful)
if (exception is not null)
{
ResolutionFailed();
}
Expand All @@ -110,7 +110,7 @@ public void AfterResolution(object hostNameOrAddress, long? startingTimestamp, b

if (NameResolutionMetrics.IsEnabled())
{
NameResolutionMetrics.AfterResolution(duration, GetHostnameFromStateObject(hostNameOrAddress));
NameResolutionMetrics.AfterResolution(duration, GetHostnameFromStateObject(hostNameOrAddress), exception);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace System.Net.NameResolution.Tests
{
public class MetricsTest
{
private const string DnsLookupDuration = "dns.lookups.duration";
private const string DnsLookupDuration = "dns.lookup.duration";

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static void ResolveValidHostName_MetricsRecorded()
Expand Down Expand Up @@ -57,17 +57,26 @@ public static async Task ResolveInvalidHostName_MetricsRecorded()
Assert.ThrowsAny<SocketException>(() => Dns.EndGetHostEntry(Dns.BeginGetHostEntry(InvalidHostName, null, null)));
Assert.ThrowsAny<SocketException>(() => Dns.EndGetHostAddresses(Dns.BeginGetHostAddresses(InvalidHostName, null, null)));

double[] measurements = GetMeasurementsForHostname(recorder, InvalidHostName);
double[] measurements = GetMeasurementsForHostname(recorder, InvalidHostName, "host_not_found");

Assert.Equal(6, measurements.Length);
Assert.All(measurements, m => Assert.True(m > double.Epsilon));
}

private static double[] GetMeasurementsForHostname(InstrumentRecorder<double> recorder, string hostname)
private static double[] GetMeasurementsForHostname(InstrumentRecorder<double> recorder, string hostname, string? expectedErrorType = null)
{
return recorder
.GetMeasurements()
.Where(m => m.Tags.ToArray().Any(t => t.Key == "dns.question.name" && t.Value is string hostnameTag && hostnameTag == hostname))
.Where(m =>
{
KeyValuePair<string, object?>[] tags = m.Tags.ToArray();
if (!tags.Any(t => t.Key == "dns.question.name" && t.Value is string hostnameTag && hostnameTag == hostname))
{
return false;
}
string? actualErrorType = tags.FirstOrDefault(t => t.Key == "error.type").Value as string;
return expectedErrorType == actualErrorType;
})
.Select(m => m.Value)
.ToArray();
}
Expand Down

0 comments on commit 946c524

Please sign in to comment.