Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS #5197

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

#if !NET8_0_OR_GREATER
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Instrumentation.AspNetCore.Implementation;
#endif
Expand All @@ -27,13 +28,15 @@ public static MeterProviderBuilder AddAspNetCoreInstrumentation(
#if NET8_0_OR_GREATER
return builder.ConfigureMeters();
#else
// Note: Warm-up the status code and method mapping.
// Note: Warm-up the status code.
_ = TelemetryHelper.BoxedStatusCodes;
_ = RequestMethodHelper.KnownMethods;

builder.ConfigureServices(RequestMethodHelper.RegisterServices);

builder.AddMeter(HttpInMetricsListener.InstrumentationName);

builder.AddInstrumentation(new AspNetCoreMetrics());
builder.AddInstrumentation(sp => new AspNetCoreMetrics(
sp.GetRequiredService<RequestMethodHelper>()));

return builder;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation(
{
Guard.ThrowIfNull(builder);

// Note: Warm-up the status code and method mapping.
// Note: Warm-up the status code.
_ = TelemetryHelper.BoxedStatusCodes;
_ = RequestMethodHelper.KnownMethods;

name ??= Options.DefaultName;

Expand All @@ -64,6 +63,7 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation(
}

services.RegisterOptionsFactory(configuration => new AspNetCoreTraceInstrumentationOptions(configuration));
RequestMethodHelper.RegisterServices(services);
});

if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
Expand All @@ -76,10 +76,10 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation(

return builder.AddInstrumentation(sp =>
{
var options = sp.GetRequiredService<IOptionsMonitor<AspNetCoreTraceInstrumentationOptions>>().Get(name);

return new AspNetCoreInstrumentation(
new HttpInListener(options));
new HttpInListener(
sp.GetRequiredService<IOptionsMonitor<AspNetCoreTraceInstrumentationOptions>>().Get(name),
sp.GetRequiredService<RequestMethodHelper>()));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#if !NET8_0_OR_GREATER
using OpenTelemetry.Instrumentation.AspNetCore.Implementation;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Instrumentation.AspNetCore;

Expand All @@ -25,9 +26,9 @@ internal sealed class AspNetCoreMetrics : IDisposable

private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber;

internal AspNetCoreMetrics()
internal AspNetCoreMetrics(RequestMethodHelper requestMethodHelper)
{
var metricsListener = new HttpInMetricsListener("Microsoft.AspNetCore");
var metricsListener = new HttpInMetricsListener("Microsoft.AspNetCore", requestMethodHelper);
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(metricsListener, this.isEnabled, AspNetCoreInstrumentationEventSource.Log.UnknownErrorProcessingEvent);
this.diagnosticSourceSubscriber.Subscribe();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,18 @@ internal class HttpInListener : ListenerHandler
private readonly PropertyFetcher<string> beforeActionTemplateFetcher = new("Template");
#endif
private readonly AspNetCoreTraceInstrumentationOptions options;
private readonly RequestMethodHelper requestMethodHelper;

public HttpInListener(AspNetCoreTraceInstrumentationOptions options)
public HttpInListener(
AspNetCoreTraceInstrumentationOptions options,
RequestMethodHelper requestMethodHelper)
: base(DiagnosticSourceName)
{
Guard.ThrowIfNull(options);
Debug.Assert(options != null, "options was null");
Debug.Assert(requestMethodHelper != null, "requestMethodHelper was null");

this.options = options;
this.requestMethodHelper = requestMethodHelper;
}

public override void OnEventWritten(string name, object payload)
Expand Down Expand Up @@ -105,8 +110,7 @@ public void OnStartActivity(Activity activity, object payload)
// By this time, samplers have already run and
// activity.IsAllDataRequested populated accordingly.

HttpContext context = payload as HttpContext;
if (context == null)
if (payload is not HttpContext context)
{
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStartActivity), activity.OperationName);
return;
Expand Down Expand Up @@ -176,7 +180,7 @@ public void OnStartActivity(Activity activity, object payload)
#endif

var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/";
activity.DisplayName = GetDisplayName(request.Method);
activity.DisplayName = this.GetDisplayName(request.Method);

// see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md

Expand All @@ -196,7 +200,7 @@ public void OnStartActivity(Activity activity, object payload)
activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value);
}

RequestMethodHelper.SetHttpMethodTag(activity, request.Method);
this.requestMethodHelper.SetHttpMethodTag(activity, request.Method);

activity.SetTag(SemanticConventions.AttributeUrlScheme, request.Scheme);
activity.SetTag(SemanticConventions.AttributeUrlPath, path);
Expand Down Expand Up @@ -226,8 +230,7 @@ public void OnStopActivity(Activity activity, object payload)
{
if (activity.IsAllDataRequested)
{
HttpContext context = payload as HttpContext;
if (context == null)
if (payload is not HttpContext context)
{
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity), activity.OperationName);
return;
Expand All @@ -239,7 +242,7 @@ public void OnStopActivity(Activity activity, object payload)
var routePattern = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
if (!string.IsNullOrEmpty(routePattern))
{
activity.DisplayName = GetDisplayName(context.Request.Method, routePattern);
activity.DisplayName = this.GetDisplayName(context.Request.Method, routePattern);
activity.SetTag(SemanticConventions.AttributeHttpRoute, routePattern);
}
#endif
Expand Down Expand Up @@ -382,9 +385,9 @@ private static void AddGrpcAttributes(Activity activity, string grpcMethod, Http
}
}

private static string GetDisplayName(string httpMethod, string httpRoute = null)
private string GetDisplayName(string httpMethod, string httpRoute = null)
{
var normalizedMethod = RequestMethodHelper.GetNormalizedHttpMethod(httpMethod);
var normalizedMethod = this.requestMethodHelper.GetNormalizedHttpMethod(httpMethod);

return string.IsNullOrEmpty(httpRoute)
? normalizedMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,39 @@ internal sealed class HttpInMetricsListener : ListenerHandler

private static readonly Histogram<double> HttpServerRequestDuration = Meter.CreateHistogram<double>(HttpServerRequestDurationMetricName, "s", "Duration of HTTP server requests.");

internal HttpInMetricsListener(string name)
private readonly RequestMethodHelper requestMethodHelper;

internal HttpInMetricsListener(
string name,
RequestMethodHelper requestMethodHelper)
: base(name)
{
Debug.Assert(requestMethodHelper != null, "requestMethodHelper was null");

this.requestMethodHelper = requestMethodHelper;
}

public override void OnEventWritten(string name, object payload)
{
switch (name)
{
case OnUnhandledDiagnosticsExceptionEvent:
case OnUnhandledHostingExceptionEvent:
{
OnExceptionEventWritten(name, payload);
}

break;
case OnStopEvent:
{
this.OnStopEventWritten(name, payload);
}

break;
}
}

public static void OnExceptionEventWritten(string name, object payload)
private static 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))
Expand All @@ -68,12 +95,11 @@ static bool TryFetchHttpContext(object payload, out HttpContext ctx)
=> HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null;
}

public static void OnStopEventWritten(string name, object payload)
private void OnStopEventWritten(string name, object payload)
{
var context = payload as HttpContext;
if (context == null)
if (payload is not HttpContext context)
{
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(OnStopEventWritten), HttpServerRequestDurationMetricName);
AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(this.OnStopEventWritten), HttpServerRequestDurationMetricName);
return;
}

Expand All @@ -84,8 +110,9 @@ public static void OnStopEventWritten(string name, object payload)
tags.Add(new KeyValuePair<string, object>(SemanticConventions.AttributeUrlScheme, context.Request.Scheme));
tags.Add(new KeyValuePair<string, object>(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode)));

var httpMethod = RequestMethodHelper.GetNormalizedHttpMethod(context.Request.Method);
tags.Add(new KeyValuePair<string, object>(SemanticConventions.AttributeHttpRequestMethod, httpMethod));
tags.Add(new KeyValuePair<string, object>(
SemanticConventions.AttributeHttpRequestMethod,
this.requestMethodHelper.GetNormalizedHttpMethod(context.Request.Method)));

#if NET6_0_OR_GREATER
var route = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText;
Expand All @@ -104,24 +131,4 @@ public static void OnStopEventWritten(string name, object payload)
// TODO: Follow up with .NET team if we can continue to rely on this behavior.
HttpServerRequestDuration.Record(Activity.Current.Duration.TotalSeconds, tags);
}

public override void OnEventWritten(string name, object payload)
{
switch (name)
{
case OnUnhandledDiagnosticsExceptionEvent:
case OnUnhandledHostingExceptionEvent:
{
OnExceptionEventWritten(name, payload);
}

break;
case OnStopEvent:
{
OnStopEventWritten(name, payload);
}

break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

using OpenTelemetry.Instrumentation.Http.Implementation;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Instrumentation.Http;

Expand Down Expand Up @@ -31,11 +32,9 @@ internal sealed class HttpClientInstrumentation : IDisposable
private readonly Func<string, object, object, bool> isEnabledNet7OrGreater = (eventName, _, _)
=> !ExcludedDiagnosticSourceEventsNet7OrGreater.Contains(eventName);

/// <summary>
/// Initializes a new instance of the <see cref="HttpClientInstrumentation"/> class.
/// </summary>
/// <param name="options">Configuration options for HTTP client instrumentation.</param>
public HttpClientInstrumentation(HttpClientTraceInstrumentationOptions options)
public HttpClientInstrumentation(
HttpClientTraceInstrumentationOptions options,
RequestMethodHelper requestMethodHelper)
{
// For .NET7.0 activity will be created using activitySource.
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs
Expand All @@ -45,11 +44,17 @@ public HttpClientInstrumentation(HttpClientTraceInstrumentationOptions options)
// so that the sampler's decision is respected.
if (HttpHandlerDiagnosticListener.IsNet7OrGreater)
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), this.isEnabledNet7OrGreater, HttpInstrumentationEventSource.Log.UnknownErrorProcessingEvent);
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
new HttpHandlerDiagnosticListener(options, requestMethodHelper),
this.isEnabledNet7OrGreater,
HttpInstrumentationEventSource.Log.UnknownErrorProcessingEvent);
}
else
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), this.isEnabled, HttpInstrumentationEventSource.Log.UnknownErrorProcessingEvent);
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
new HttpHandlerDiagnosticListener(options, requestMethodHelper),
this.isEnabled,
HttpInstrumentationEventSource.Log.UnknownErrorProcessingEvent);
}

this.diagnosticSourceSubscriber.Subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

#if !NET8_0_OR_GREATER
using Microsoft.Extensions.DependencyInjection;
#if !NETFRAMEWORK
using OpenTelemetry.Instrumentation.Http;
#endif
Expand Down Expand Up @@ -32,16 +33,26 @@ public static MeterProviderBuilder AddHttpClientInstrumentation(
.AddMeter("System.Net.Http")
.AddMeter("System.Net.NameResolution");
#else
// Note: Warm-up the status code and method mapping.
// Note: Warm-up the status code.
_ = TelemetryHelper.BoxedStatusCodes;
_ = RequestMethodHelper.KnownMethods;

builder.ConfigureServices(RequestMethodHelper.RegisterServices);

#if NETFRAMEWORK
builder.AddMeter(HttpWebRequestActivitySource.MeterName);

if (builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder)
{
deferredMeterProviderBuilder.Configure((sp, builder) =>
{
HttpWebRequestActivitySource.RequestMethodHelper = sp.GetRequiredService<RequestMethodHelper>();
});
}
#else
builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName);

builder.AddInstrumentation(new HttpClientMetrics());
builder.AddInstrumentation(sp => new HttpClientMetrics(
sp.GetRequiredService<RequestMethodHelper>()));
#endif
return builder;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ public static TracerProviderBuilder AddHttpClientInstrumentation(
{
Guard.ThrowIfNull(builder);

// Note: Warm-up the status code and method mapping.
// Note: Warm-up the status code.
_ = TelemetryHelper.BoxedStatusCodes;
_ = RequestMethodHelper.KnownMethods;

name ??= Options.DefaultName;

Expand All @@ -59,6 +58,8 @@ public static TracerProviderBuilder AddHttpClientInstrumentation(
{
services.Configure(name, configureHttpClientTraceInstrumentationOptions);
}

RequestMethodHelper.RegisterServices(services);
});

#if NETFRAMEWORK
Expand All @@ -68,19 +69,18 @@ public static TracerProviderBuilder AddHttpClientInstrumentation(
{
deferredTracerProviderBuilder.Configure((sp, builder) =>
{
var options = sp.GetRequiredService<IOptionsMonitor<HttpClientTraceInstrumentationOptions>>().Get(name);

HttpWebRequestActivitySource.TracingOptions = options;
HttpWebRequestActivitySource.TracingOptions = sp.GetRequiredService<IOptionsMonitor<HttpClientTraceInstrumentationOptions>>().Get(name);
HttpWebRequestActivitySource.RequestMethodHelper = sp.GetRequiredService<RequestMethodHelper>();
});
}
#else
AddHttpClientInstrumentationSource(builder);

builder.AddInstrumentation(sp =>
{
var options = sp.GetRequiredService<IOptionsMonitor<HttpClientTraceInstrumentationOptions>>().Get(name);

return new HttpClientInstrumentation(options);
return new HttpClientInstrumentation(
sp.GetRequiredService<IOptionsMonitor<HttpClientTraceInstrumentationOptions>>().Get(name),
sp.GetRequiredService<RequestMethodHelper>());
});
#endif
return builder;
Expand Down
Loading