From 15fd6ff198a664d498edc56f674b97bed3035652 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 18 Oct 2023 09:02:18 -0700 Subject: [PATCH 01/16] draft --- .../AspNetCoreMetrics.cs | 2 + .../Implementation/HttpInMetricsListener.cs | 49 +++++++++++++++++++ src/Shared/SemanticConventions.cs | 1 + 3 files changed, 52 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs index 6c527187d91..ad001bc9937 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs @@ -35,6 +35,8 @@ internal sealed class AspNetCoreMetrics : IDisposable "Microsoft.AspNetCore.Hosting.HttpRequestIn", "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start", "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop", + "Microsoft.AspNetCore.Diagnostics.UnhandledException", + "Microsoft.AspNetCore.Hosting.UnhandledException", }; private readonly Func isEnabled = (eventName, _, _) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 9a8b35040c6..b03e18535a1 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -15,6 +15,7 @@ // using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Metrics; using Microsoft.AspNetCore.Http; #if NET6_0_OR_GREATER @@ -30,8 +31,11 @@ internal sealed class HttpInMetricsListener : ListenerHandler internal const string HttpServerDurationMetricName = "http.server.duration"; internal const string HttpServerRequestDurationMetricName = "http.server.request.duration"; + internal const string OnUnhandledHostingExceptionEvent = "Microsoft.AspNetCore.Hosting.UnhandledException"; + internal const string OnUnHandledDiagnosticsExceptionEvent = "Microsoft.AspNetCore.Diagnostics.UnhandledException"; private const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop"; private const string EventName = "OnStopActivity"; + private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -63,6 +67,11 @@ internal HttpInMetricsListener(string name, Meter meter, AspNetCoreMetricsInstru public override void OnEventWritten(string name, object payload) { + if (name == OnUnHandledDiagnosticsExceptionEvent || name == OnUnhandledHostingExceptionEvent) + { + this.OnExceptionEventWritten(name, payload); + } + if (name == OnStopEvent) { if (this.emitOldAttributes) @@ -77,6 +86,32 @@ public override void OnEventWritten(string name, object payload) } } + public void OnExceptionEventWritten(string name, object payload) + { + var activity = Activity.Current; + + // We need to use reflection here as the payload type is not a defined public type. + if (!TryFetchException(payload, out Exception exc)) + { + AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnExceptionEventWritten), activity.OperationName); + return; + } + + if (activity != null) + { + activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); + } + + // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 + // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 + // this makes sure that top-level properties on the payload object are always preserved. +#if NET6_0_OR_GREATER + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The event source guarantees that top level properties are preserved")] +#endif + static bool TryFetchException(object payload, out Exception exc) + => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; + } + public void OnEventWritten_Old(string name, object payload) { var context = payload as HttpContext; @@ -152,6 +187,7 @@ public void OnEventWritten_Old(string name, object payload) public void OnEventWritten_New(string name, object payload) { + var activity = Activity.Current; var context = payload as HttpContext; if (context == null) { @@ -196,6 +232,19 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); } #endif + if (SpanHelper.ResolveSpanStatusForHttpStatusCode(activity.Kind, context.Response.StatusCode) == ActivityStatusCode.Error) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); + } + else + { + var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); + if (errorType != null) + { + tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); + } + } + if (this.options.Enrich != null) { try diff --git a/src/Shared/SemanticConventions.cs b/src/Shared/SemanticConventions.cs index 3f0ce13a20b..98d92334dc7 100644 --- a/src/Shared/SemanticConventions.cs +++ b/src/Shared/SemanticConventions.cs @@ -110,6 +110,7 @@ internal static class SemanticConventions public const string AttributeExceptionType = "exception.type"; public const string AttributeExceptionMessage = "exception.message"; public const string AttributeExceptionStacktrace = "exception.stacktrace"; + public const string AttributeErrorType = "error.type"; // v1.21.0 // https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md From fbe3eb547f19488c4bcee73c2f72482e4c468309 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 24 Oct 2023 13:53:10 -0700 Subject: [PATCH 02/16] set error.type for asp.net core --- .../Implementation/HttpInListener.cs | 5 ++ .../Implementation/HttpInMetricsListener.cs | 65 ++++++++++--------- ...ollectionsIsAccordingToTheSpecTests_New.cs | 5 +- .../MetricTests.cs | 49 ++++++++++---- 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 3b25d781ee2..de58a68370f 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -414,6 +414,11 @@ public void OnException(Activity activity, object payload) return; } + if (this.emitNewAttributes) + { + activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); + } + if (this.options.RecordException) { activity.RecordException(exc); diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 0b1f9be691e..303e0abd1a4 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -33,11 +33,11 @@ internal sealed class HttpInMetricsListener : ListenerHandler internal const string HttpServerRequestDurationMetricName = "http.server.request.duration"; internal const string OnUnhandledHostingExceptionEvent = "Microsoft.AspNetCore.Hosting.UnhandledException"; - internal const string OnUnHandledDiagnosticsExceptionEvent = "Microsoft.AspNetCore.Diagnostics.UnhandledException"; + internal const string OnUnhandledDiagnosticsExceptionEvent = "Microsoft.AspNetCore.Diagnostics.UnhandledException"; private const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop"; private const string EventName = "OnStopActivity"; - private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); private const string NetworkProtocolName = "http"; + private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -69,22 +69,32 @@ internal HttpInMetricsListener(string name, Meter meter, AspNetCoreMetricsInstru public override void OnEventWritten(string name, object payload) { - if (name == OnUnHandledDiagnosticsExceptionEvent || name == OnUnhandledHostingExceptionEvent) + switch (name) { - this.OnExceptionEventWritten(name, payload); - } - - if (name == OnStopEvent) - { - if (this.emitOldAttributes) - { - this.OnEventWritten_Old(name, payload); - } - - if (this.emitNewAttributes) - { - this.OnEventWritten_New(name, payload); - } + case OnUnhandledDiagnosticsExceptionEvent: + case OnUnhandledHostingExceptionEvent: + { + if (this.emitNewAttributes) + { + this.OnExceptionEventWritten(name, payload); + } + } + + break; + case OnStopEvent: + { + if (this.emitOldAttributes) + { + this.OnEventWritten_Old(name, payload); + } + + if (this.emitNewAttributes) + { + this.OnEventWritten_New(name, payload); + } + } + + break; } } @@ -99,10 +109,7 @@ public void OnExceptionEventWritten(string name, object payload) return; } - if (activity != null) - { - activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); - } + activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -196,17 +203,13 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); } #endif - if (SpanHelper.ResolveSpanStatusForHttpStatusCode(activity.Kind, context.Response.StatusCode) == ActivityStatusCode.Error) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); - } - else + + // check for error.type on activity + // TODO: check to see if this can be optimized by first checking the status code + var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); + if (errorType != null) { - var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); - if (errorType != null) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); - } + tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); } // We are relying here on ASP.NET Core to set duration before writing the stop event. diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs index ee51eba3c47..cf66df98ee7 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests_New.cs @@ -39,8 +39,8 @@ public IncomingRequestsCollectionsIsAccordingToTheSpecTests_New(WebApplicationFa } [Theory] - [InlineData("/api/values", null, "user-agent", 503, "503")] - [InlineData("/api/values", "?query=1", null, 503, null)] + [InlineData("/api/values", null, "user-agent", 200, null)] + [InlineData("/api/values", "?query=1", null, 200, null)] [InlineData("/api/exception", null, null, 503, null)] [InlineData("/api/exception", null, null, 503, null, true)] public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( @@ -123,6 +123,7 @@ public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( if (statusCode == 503) { Assert.Equal(ActivityStatusCode.Error, activity.Status); + Assert.Equal("System.Exception", activity.GetTagValue(SemanticConventions.AttributeErrorType)); } else { diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index ff11b5e1225..72c4b25d013 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -185,8 +185,10 @@ public async Task ValidateNet8RateLimitingMetricsAsync() } #endif - [Fact] - public async Task RequestMetricIsCaptured_New() + [Theory] + [InlineData("/api/values/2", "api/Values/{id}", null, 200)] + [InlineData("/api/Error", "api/Error", "System.Exception", 500)] + public async Task RequestMetricIsCaptured_New(string api, string expectedRoute, string expectedErrorType, int expectedStatusCode) { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) @@ -207,11 +209,15 @@ public async Task RequestMetricIsCaptured_New() }) .CreateClient()) { - using var response1 = await client.GetAsync("/api/values").ConfigureAwait(false); - using var response2 = await client.GetAsync("/api/values/2").ConfigureAwait(false); - - response1.EnsureSuccessStatusCode(); - response2.EnsureSuccessStatusCode(); + try + { + using var response = await client.GetAsync(api).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + } + catch + { + // ignore error. + } } // We need to let End callback execute as it is executed AFTER response was returned. @@ -229,12 +235,14 @@ public async Task RequestMetricIsCaptured_New() Assert.Equal("s", metric.Unit); var metricPoints = GetMetricPoints(metric); - Assert.Equal(2, metricPoints.Count); + Assert.Single(metricPoints); AssertMetricPoints_New( metricPoints: metricPoints, - expectedRoutes: new List { "api/Values", "api/Values/{id}" }, - expectedTagsCount: 6); + expectedRoutes: new List { expectedRoute }, + expectedErrorType, + expectedStatusCode, + expectedTagsCount: expectedErrorType == null ? 6 : 7); } #if !NET8_0_OR_GREATER @@ -354,6 +362,8 @@ public async Task RequestMetricIsCaptured_Dup() AssertMetricPoints_New( metricPoints: metricPoints, expectedRoutes: new List { "api/Values", "api/Values/{id}" }, + null, + 200, expectedTagsCount: 6); } #endif @@ -380,6 +390,8 @@ private static List GetMetricPoints(Metric metric) private static void AssertMetricPoints_New( List metricPoints, List expectedRoutes, + string expectedErrorType, + int expectedStatusCode, int expectedTagsCount) { // Assert that one MetricPoint exists for each ExpectedRoute @@ -400,7 +412,7 @@ private static void AssertMetricPoints_New( if (metricPoint.HasValue) { - AssertMetricPoint_New(metricPoint.Value, expectedRoute, expectedTagsCount); + AssertMetricPoint_New(metricPoint.Value, expectedRoute, expectedStatusCode, expectedErrorType, expectedTagsCount); } else { @@ -444,6 +456,8 @@ private static void AssertMetricPoints_Old( private static KeyValuePair[] AssertMetricPoint_New( MetricPoint metricPoint, string expectedRoute = "api/Values", + int expectedStatusCode = 200, + string expectedErrorType = null, int expectedTagsCount = StandardTagsCount) { var count = metricPoint.GetHistogramCount(); @@ -464,7 +478,7 @@ private static KeyValuePair[] AssertMetricPoint_New( var method = new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, "GET"); var scheme = new KeyValuePair(SemanticConventions.AttributeUrlScheme, "http"); - var statusCode = new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, 200); + var statusCode = new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, expectedStatusCode); var flavor = new KeyValuePair(SemanticConventions.AttributeNetworkProtocolVersion, "1.1"); var route = new KeyValuePair(SemanticConventions.AttributeHttpRoute, expectedRoute); Assert.Contains(method, attributes); @@ -473,6 +487,17 @@ private static KeyValuePair[] AssertMetricPoint_New( Assert.Contains(flavor, attributes); Assert.Contains(route, attributes); + if (expectedErrorType != null) + { +#if NET8_0_OR_GREATER + // Expected to change in next release + // https://github.com/dotnet/aspnetcore/issues/51029 + var errorType = new KeyValuePair("exception.type", expectedErrorType); +#else + var errorType = new KeyValuePair(SemanticConventions.AttributeErrorType, expectedErrorType); +#endif + } + // Inspect Histogram Bounds var histogramBuckets = metricPoint.GetHistogramBuckets(); var histogramBounds = new List(); From 1d03f34bccb2f4766659be7ee6d369f3146230eb Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 24 Oct 2023 14:05:20 -0700 Subject: [PATCH 03/16] rmv using --- .../Implementation/HttpInMetricsListener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 303e0abd1a4..6dacc520f89 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -16,10 +16,10 @@ #if !NET8_0_OR_GREATER using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Metrics; using Microsoft.AspNetCore.Http; #if NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Routing; #endif using OpenTelemetry.Trace; From e240c6bba8f9a6488c37c84ac33dd7c22772e21a Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 11:25:15 -0700 Subject: [PATCH 04/16] refactor --- .../Implementation/HttpInMetricsListener.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 7c98194d270..58a2d63ab66 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -39,6 +39,8 @@ internal sealed class HttpInMetricsListener : ListenerHandler private const string EventName = "OnStopActivity"; private const string NetworkProtocolName = "http"; private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); + private static readonly PropertyFetcher HttpContextPropertyFetcher = new("HttpContext"); + private static readonly object ErrorTypekey = "error.type"; private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -101,16 +103,14 @@ public override void OnEventWritten(string name, object payload) public void OnExceptionEventWritten(string name, object payload) { - var activity = Activity.Current; - // We need to use reflection here as the payload type is not a defined public type. - if (!TryFetchException(payload, out Exception exc)) + if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) { AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnExceptionEventWritten), activity.OperationName); return; } - activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); + ctx.Items.Add(ErrorTypekey, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -120,11 +120,19 @@ public void OnExceptionEventWritten(string name, object payload) #endif static bool TryFetchException(object payload, out Exception exc) => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; + +#if NET6_0_OR_GREATER + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The event source guarantees that top level properties are preserved")] +#endif + static bool TryFetchHttpContext(object payload, out HttpContext ctx) + => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; } public void OnEventWritten_Old(string name, object payload) { var context = payload as HttpContext; + context.Items.TryGetValue(name, out var item); + if (context == null) { AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), EventName, HttpServerDurationMetricName); @@ -214,10 +222,7 @@ public void OnEventWritten_New(string name, object payload) } #endif - // check for error.type on activity - // TODO: check to see if this can be optimized by first checking the status code - var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); - if (errorType != null) + if (context.Items.TryGetValue(ErrorTypekey, out var errorType)) { tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); } From f6a8e42c972efa4851050c7ea2efc4b0cb430daf Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 11:34:33 -0700 Subject: [PATCH 05/16] changelog --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 8 ++++++++ .../Implementation/HttpInMetricsListener.cs | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 72f9147b46e..c0c1ee49347 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -18,6 +18,14 @@ `http` or `http/dup`. ([#5001](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5001)) +* An additional attribute `error.type` will be added to activity and +`http.server.request.duration` metric when the request results in unhandled +exception. The attribute value will be set to full name of exception type. + +The attribute will only be added when `OTEL_SEMCONV_STABILITY_OPT_IN` +environment variable is set to `http` or `http/dup`. +([#4986](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4986)) + ## 1.6.0-beta.2 Released 2023-Oct-26 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 58a2d63ab66..06df5d3ed6d 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -106,7 +106,7 @@ public void OnExceptionEventWritten(string name, object payload) // We need to use reflection here as the payload type is not a defined public type. if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnExceptionEventWritten), activity.OperationName); + AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnExceptionEventWritten), HttpServerDurationMetricName); return; } @@ -180,7 +180,6 @@ public void OnEventWritten_Old(string name, object payload) public void OnEventWritten_New(string name, object payload) { - var activity = Activity.Current; var context = payload as HttpContext; if (context == null) { From 34419b56ce1615382ac6d59bc9f6346e075e09a1 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 11:37:11 -0700 Subject: [PATCH 06/16] align --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index c0c1ee49347..4091d5b9f1f 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -22,9 +22,9 @@ `http.server.request.duration` metric when the request results in unhandled exception. The attribute value will be set to full name of exception type. -The attribute will only be added when `OTEL_SEMCONV_STABILITY_OPT_IN` -environment variable is set to `http` or `http/dup`. -([#4986](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4986)) + The attribute will only be added when `OTEL_SEMCONV_STABILITY_OPT_IN` + environment variable is set to `http` or `http/dup`. + ([#4986](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4986)) ## 1.6.0-beta.2 From 6a173ab8ef5a445738220322120c5a15ab753cee Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 11:59:51 -0700 Subject: [PATCH 07/16] property name --- .../Implementation/HttpInMetricsListener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 06df5d3ed6d..53c106541a7 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -39,7 +39,7 @@ internal sealed class HttpInMetricsListener : ListenerHandler private const string EventName = "OnStopActivity"; private const string NetworkProtocolName = "http"; private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); - private static readonly PropertyFetcher HttpContextPropertyFetcher = new("HttpContext"); + private static readonly PropertyFetcher HttpContextPropertyFetcher = new("httpContext"); private static readonly object ErrorTypekey = "error.type"; private readonly Meter meter; From 386573db21b08f9cfc277f266cb3b831977ad468 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 13:42:26 -0700 Subject: [PATCH 08/16] rmv unused code --- .../Implementation/HttpInMetricsListener.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 53c106541a7..3fd0b61f2ca 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -131,7 +131,6 @@ static bool TryFetchHttpContext(object payload, out HttpContext ctx) public void OnEventWritten_Old(string name, object payload) { var context = payload as HttpContext; - context.Items.TryGetValue(name, out var item); if (context == null) { From a84e7a95f2faa2bd6f6fceb46c712d839d21278d Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 13:47:13 -0700 Subject: [PATCH 09/16] address comment --- .../Implementation/HttpInMetricsListener.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 3fd0b61f2ca..11769676de2 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -40,7 +40,7 @@ internal sealed class HttpInMetricsListener : ListenerHandler private const string NetworkProtocolName = "http"; private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); private static readonly PropertyFetcher HttpContextPropertyFetcher = new("httpContext"); - private static readonly object ErrorTypekey = "error.type"; + private static readonly object ErrorTypeKey = "error.type"; private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -110,7 +110,7 @@ public void OnExceptionEventWritten(string name, object payload) return; } - ctx.Items.Add(ErrorTypekey, exc.GetType().FullName); + ctx.Items.Add(ErrorTypeKey, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -220,7 +220,7 @@ public void OnEventWritten_New(string name, object payload) } #endif - if (context.Items.TryGetValue(ErrorTypekey, out var errorType)) + if (context.Items.TryGetValue(ErrorTypeKey, out var errorType)) { tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); } From 2350dc7c4324d1fa6fdac0017df0211d3b358f66 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 1 Nov 2023 13:52:57 -0700 Subject: [PATCH 10/16] fix description --- .../Implementation/HttpInMetricsListener.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 11769676de2..6df3e9820e8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -116,13 +116,13 @@ public void OnExceptionEventWritten(string name, object payload) // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 // this makes sure that top-level properties on the payload object are always preserved. #if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The event source guarantees that top level properties are preserved")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] #endif static bool TryFetchException(object payload, out Exception exc) => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; #if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The event source guarantees that top level properties are preserved")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] #endif static bool TryFetchHttpContext(object payload, out HttpContext ctx) => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; From 2f4439887b6ecd5e558c005b93f093227c8d739c Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 2 Nov 2023 14:02:57 -0700 Subject: [PATCH 11/16] switch to activity tag from context.items --- .../Implementation/HttpInMetricsListener.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 6df3e9820e8..52034073cf4 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -104,13 +104,13 @@ public override void OnEventWritten(string name, object payload) public void OnExceptionEventWritten(string name, object payload) { // We need to use reflection here as the payload type is not a defined public type. - if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) + if (!TryFetchException(payload, out Exception exc)) { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnExceptionEventWritten), HttpServerDurationMetricName); + AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(this.OnExceptionEventWritten), HttpServerDurationMetricName); return; } - ctx.Items.Add(ErrorTypeKey, exc.GetType().FullName); + Activity.Current.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -120,12 +120,6 @@ public void OnExceptionEventWritten(string name, object payload) #endif static bool TryFetchException(object payload, out Exception exc) => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; - -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] -#endif - static bool TryFetchHttpContext(object payload, out HttpContext ctx) - => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; } public void OnEventWritten_Old(string name, object payload) @@ -179,6 +173,7 @@ public void OnEventWritten_Old(string name, object payload) public void OnEventWritten_New(string name, object payload) { + var activity = Activity.Current; var context = payload as HttpContext; if (context == null) { @@ -219,8 +214,8 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); } #endif - - if (context.Items.TryGetValue(ErrorTypeKey, out var errorType)) + var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); + if (errorType != null) { tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); } @@ -228,6 +223,6 @@ public void OnEventWritten_New(string name, object payload) // We are relying here on ASP.NET Core to set duration before writing the stop event. // https://github.com/dotnet/aspnetcore/blob/d6fa351048617ae1c8b47493ba1abbe94c3a24cf/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L449 // TODO: Follow up with .NET team if we can continue to rely on this behavior. - this.httpServerRequestDuration.Record(Activity.Current.Duration.TotalSeconds, tags); + this.httpServerRequestDuration.Record(activity.Duration.TotalSeconds, tags); } } From d9a2b19b0c0ac9eaa658f930c2ae577b7f22ec90 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 2 Nov 2023 14:47:40 -0700 Subject: [PATCH 12/16] use activity custom property --- .../Implementation/HttpInMetricsListener.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 52034073cf4..616d1fdaa09 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -39,8 +39,6 @@ internal sealed class HttpInMetricsListener : ListenerHandler private const string EventName = "OnStopActivity"; private const string NetworkProtocolName = "http"; private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); - private static readonly PropertyFetcher HttpContextPropertyFetcher = new("httpContext"); - private static readonly object ErrorTypeKey = "error.type"; private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -110,7 +108,7 @@ public void OnExceptionEventWritten(string name, object payload) return; } - Activity.Current.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); + Activity.Current.SetCustomProperty(SemanticConventions.AttributeErrorType, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -214,7 +212,7 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); } #endif - var errorType = activity.GetTagValue(SemanticConventions.AttributeErrorType); + var errorType = activity.GetCustomProperty(SemanticConventions.AttributeErrorType); if (errorType != null) { tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); From ef2bdd38f979282be81d84ca56b2bca1e3901065 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 2 Nov 2023 14:54:58 -0700 Subject: [PATCH 13/16] rmv default param --- .../MetricTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index 28cfe30cadf..34c7fb8eb78 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -488,7 +488,7 @@ private static void AssertMetricPoints_New( if (metricPoint.HasValue) { - AssertMetricPoint_New(metricPoint.Value, expectedRoute, expectedStatusCode, expectedErrorType, expectedTagsCount); + AssertMetricPoint_New(metricPoint.Value, expectedStatusCode, expectedRoute, expectedErrorType, expectedTagsCount); } else { @@ -531,8 +531,8 @@ private static void AssertMetricPoints_Old( private static KeyValuePair[] AssertMetricPoint_New( MetricPoint metricPoint, + int expectedStatusCode, string expectedRoute = "api/Values", - int expectedStatusCode = 200, string expectedErrorType = null, int expectedTagsCount = StandardTagsCount) { From cd73339e3937fc4bb77f62e0f4c6803e456f4a64 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 2 Nov 2023 15:15:12 -0700 Subject: [PATCH 14/16] refactor test --- .../MetricTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index 34c7fb8eb78..d74ca86b523 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -532,9 +532,9 @@ private static void AssertMetricPoints_Old( private static KeyValuePair[] AssertMetricPoint_New( MetricPoint metricPoint, int expectedStatusCode, - string expectedRoute = "api/Values", - string expectedErrorType = null, - int expectedTagsCount = StandardTagsCount) + string expectedRoute, + string expectedErrorType, + int expectedTagsCount) { var count = metricPoint.GetHistogramCount(); var sum = metricPoint.GetHistogramSum(); From 9777eacd96b80b6deed9f3b2f9bf00102a2ae658 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 2 Nov 2023 16:10:07 -0700 Subject: [PATCH 15/16] assert --- .../MetricTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index d74ca86b523..12c77462eb7 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -572,6 +572,7 @@ private static KeyValuePair[] AssertMetricPoint_New( #else var errorType = new KeyValuePair(SemanticConventions.AttributeErrorType, expectedErrorType); #endif + Assert.Contains(errorType, attributes); } // Inspect Histogram Bounds From a36104c15d5c6bf55e08f86a31c7357d5b1e59cf Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 3 Nov 2023 10:14:28 -0700 Subject: [PATCH 16/16] switch back to ctx.items --- .../Implementation/HttpInMetricsListener.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 616d1fdaa09..5d8db405158 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -39,6 +39,8 @@ internal sealed class HttpInMetricsListener : ListenerHandler private const string EventName = "OnStopActivity"; private const string NetworkProtocolName = "http"; private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); + private static readonly PropertyFetcher HttpContextPropertyFetcher = new("HttpContext"); + private static readonly object ErrorTypeHttpContextItemsKey = new(); private readonly Meter meter; private readonly AspNetCoreMetricsInstrumentationOptions options; @@ -102,13 +104,13 @@ public override void OnEventWritten(string name, object payload) public void OnExceptionEventWritten(string name, object payload) { // We need to use reflection here as the payload type is not a defined public type. - if (!TryFetchException(payload, out Exception exc)) + if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) { AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(this.OnExceptionEventWritten), HttpServerDurationMetricName); return; } - Activity.Current.SetCustomProperty(SemanticConventions.AttributeErrorType, exc.GetType().FullName); + ctx.Items.Add(ErrorTypeHttpContextItemsKey, exc.GetType().FullName); // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 @@ -118,6 +120,11 @@ public void OnExceptionEventWritten(string name, object payload) #endif static bool TryFetchException(object payload, out Exception exc) => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; +#if NET6_0_OR_GREATER + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] +#endif + static bool TryFetchHttpContext(object payload, out HttpContext ctx) + => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; } public void OnEventWritten_Old(string name, object payload) @@ -171,7 +178,6 @@ public void OnEventWritten_Old(string name, object payload) public void OnEventWritten_New(string name, object payload) { - var activity = Activity.Current; var context = payload as HttpContext; if (context == null) { @@ -212,8 +218,7 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); } #endif - var errorType = activity.GetCustomProperty(SemanticConventions.AttributeErrorType); - if (errorType != null) + if (context.Items.TryGetValue(ErrorTypeHttpContextItemsKey, out var errorType)) { tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); } @@ -221,6 +226,6 @@ public void OnEventWritten_New(string name, object payload) // We are relying here on ASP.NET Core to set duration before writing the stop event. // https://github.com/dotnet/aspnetcore/blob/d6fa351048617ae1c8b47493ba1abbe94c3a24cf/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L449 // TODO: Follow up with .NET team if we can continue to rely on this behavior. - this.httpServerRequestDuration.Record(activity.Duration.TotalSeconds, tags); + this.httpServerRequestDuration.Record(Activity.Current.Duration.TotalSeconds, tags); } }