This is an Instrumentation Library, which instruments ASP.NET and collect metrics and traces about incoming web requests.
Note
This component is based on the OpenTelemetry semantic conventions for metrics and traces. These conventions are Experimental, and hence, this package is a pre-release. Until a stable version is released, there can be breaking changes. You can track the progress from milestones.
Add a reference to the
OpenTelemetry.Instrumentation.AspNet
package. Also, add any other instrumentations & exporters you will need.
dotnet add package OpenTelemetry.Instrumentation.AspNet
OpenTelemetry.Instrumentation.AspNet
requires adding an additional HttpModule
to your web server. This additional HttpModule is shipped as part of
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule
which is implicitly brought by OpenTelemetry.Instrumentation.AspNet
. The
following shows changes required to your Web.config
when using IIS web server.
<system.webServer>
<modules>
<add
name="TelemetryHttpModule"
type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule,
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule"
preCondition="integratedMode,managedHandler" />
</modules>
</system.webServer>
ASP.NET instrumentation must be enabled at application startup. This is
typically done in the Global.asax.cs
.
The following example demonstrates adding ASP.NET instrumentation with the
extension method .AddAspNetInstrumentation()
on TracerProviderBuilder
to
an application. This example also sets up
the OTLP (OpenTelemetry Protocol) exporter, which requires adding the package
OpenTelemetry.Exporter.OpenTelemetryProtocol
to the application.
using OpenTelemetry;
using OpenTelemetry.Trace;
public class WebApiApplication : HttpApplication
{
private TracerProvider tracerProvider;
protected void Application_Start()
{
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation()
.AddOtlpExporter()
.Build();
}
protected void Application_End()
{
this.tracerProvider?.Dispose();
}
}
The following example demonstrates adding ASP.NET instrumentation with the
extension method .AddAspNetInstrumentation()
on MeterProviderBuilder
to
an application. This example also sets up
the OTLP (OpenTelemetry Protocol) exporter, which requires adding the package
OpenTelemetry.Exporter.OpenTelemetryProtocol
to the application.
using OpenTelemetry;
using OpenTelemetry.Metrics;
public class WebApiApplication : HttpApplication
{
private MeterProvider meterProvider;
protected void Application_Start()
{
this.meterProvider = Sdk.CreateMeterProviderBuilder()
.AddAspNetInstrumentation()
.AddOtlpExporter()
.Build();
}
protected void Application_End()
{
this.meterProvider?.Dispose();
}
}
The instrumentation is implemented based on metrics semantic conventions. Currently, the instrumentation supports the following metric.
Name | Instrument Type | Unit | Description |
---|---|---|---|
http.server.request.duration |
Histogram | s |
Duration of HTTP server requests. |
This instrumentation can be configured to change the default behavior by using
AspNetTraceInstrumentationOptions
, which allows configuring Filter
as explained
below.
This instrumentation by default collects all the incoming http requests. It
allows filtering of requests by using the Filter
function in
AspNetTraceInstrumentationOptions
. This defines the condition for allowable
requests. The Filter receives the HttpContext
of the incoming request, and
does not collect telemetry about the request if the Filter returns false or
throws exception.
The following code snippet shows how to use Filter
to only allow GET requests.
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation(
(options) => options.Filter =
(httpContext) =>
{
// only collect telemetry about HTTP GET requests
return httpContext.Request.HttpMethod.Equals("GET");
})
.Build();
It is important to note that this Filter
option is specific to this
instrumentation. OpenTelemetry has a concept of a
Sampler,
and the Filter
option does the filtering before the Sampler is invoked.
This option allows one to enrich the activity with additional information from
the raw HttpRequest
, HttpResponse
objects. The Enrich
action is called
only when activity.IsAllDataRequested
is true
. It contains the activity
itself (which can be enriched), the name of the event, and the actual raw
object. For event name "OnStartActivity", the actual object will be
HttpRequest
. For event name "OnStopActivity", the actual object will be
HttpResponse
The following code snippet shows how to add additional tags using Enrich
.
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetInstrumentation((options) => options.Enrich
= (activity, eventName, rawObject) =>
{
if (eventName.Equals("OnStartActivity"))
{
if (rawObject is HttpRequest httpRequest)
{
activity.SetTag("physicalPath", httpRequest.PhysicalPath);
}
}
else if (eventName.Equals("OnStopActivity"))
{
if (rawObject is HttpResponse httpResponse)
{
activity.SetTag("responseType", httpResponse.ContentType);
}
}
})
.Build();
Processor,
is the general extensibility point to add additional properties to any activity.
The Enrich
option is specific to this instrumentation, and is provided to get
access to HttpRequest
and HttpResponse
.
This instrumentation automatically sets Activity Status to Error if an unhandled
exception is thrown. Additionally, RecordException
feature may be turned on,
to store the exception to the Activity itself as ActivityEvent.
This instrumentation can be configured to change the default behavior by using
AspNetMetricsInstrumentationOptions
as explained below.
This option allows one to enrich the metric with additional information from
the HttpContext
. The Enrich
action is always called unless the metric was
filtered. The callback allows for modifying the tag list. If the callback
throws an exception the metric will still be recorded.
this.meterProvider = Sdk.CreateMeterProviderBuilder()
.AddAspNetInstrumentation(options => options.Enrich =
(HttpContext context, ref TagList tags) =>
{
// Add request content type to the metric tags.
if (!string.IsNullOrEmpty(context.Request.ContentType))
{
tags.Add("custom.content.type", context.Request.ContentType);
}
})
.Build();