If you have set up distributed tracing in an ASP.NET Core application any time in the last few years, you will have installed OpenTelemetry.Instrumentation.AspNetCore. It is one of those packages that every observability guide tells you to add on line three of the tutorial, right after the core SDK and your exporter of choice. It subscribes to diagnostic events from the hosting layer, enriches the HTTP server activity with semantic convention attributes, and generally does the plumbing that makes your traces useful in Jaeger, Zipkin, or whatever backend you are shipping to.
Starting with .NET 11 Preview 2, that package is no longer necessary for the most common tracing scenario. ASP.NET Core now natively populates OpenTelemetry semantic convention attributes on the HTTP server activity. The framework does the work that the instrumentation library used to do, and it does it without you having to install anything beyond the core OpenTelemetry SDK.
This is not a subtle internal change. It simplifies your dependency graph, removes a source of version conflicts, and brings tracing closer to being a zero-configuration default in ASP.NET Core.
The old approach
Here is what a typical OpenTelemetry setup looked like before .NET 11. You needed three NuGet packages at minimum for basic HTTP tracing:
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService("OrderService"))
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation() // requires OpenTelemetry.Instrumentation.AspNetCore
.AddOtlpExporter());
var app = builder.Build();
app.MapGet("/orders/{id}", (int id) => Results.Ok(new { Id = id }));
app.Run();
The AddAspNetCoreInstrumentation() call registered a listener that subscribed to the Microsoft.AspNetCore activity source, intercepted the HTTP server activity created by the hosting layer, and enriched it with attributes like http.request.method, url.path, http.response.status_code, and server.address. Without that call, your traces would contain activities but they would be missing the semantic metadata that makes them queryable in your tracing backend.
The instrumentation library also brought along its own configuration surface — AspNetCoreTraceInstrumentationOptions — for filtering requests, enriching spans with custom data, and controlling which attributes were recorded. It worked well, but it was a third-party dependency that had to track both the OpenTelemetry semantic conventions specification and the ASP.NET Core hosting internals. Version mismatches between the instrumentation package and the framework were a regular source of confusion.
What .NET 11 Preview 2 changes
ASP.NET Core now adds OpenTelemetry semantic convention attributes directly to the HTTP server activity, aligning with the OpenTelemetry HTTP server span specification. Every incoming request gets the required attributes populated by the framework itself, without any external instrumentation library.
The new setup is noticeably simpler:
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService("OrderService"))
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore") // subscribe to the built-in activity source
.AddOtlpExporter());
var app = builder.Build();
app.MapGet("/orders/{id}", (int id) => Results.Ok(new { Id = id }));
app.Run();
No AddAspNetCoreInstrumentation(). No OpenTelemetry.Instrumentation.AspNetCore package reference. You subscribe to the Microsoft.AspNetCore activity source and the framework handles the rest.
What attributes you get natively
The framework populates the following semantic convention attributes on the HTTP server activity out of the box:
| Attribute | Example value | Description |
|---|---|---|
http.request.method |
GET |
The HTTP method |
url.scheme |
https |
The URI scheme |
url.path |
/orders/42 |
The request path |
url.query |
?status=Redacted |
Query string (values redacted by default) |
server.address |
api.example.com |
The server hostname |
server.port |
443 |
The server port |
network.protocol.version |
1.1 |
The HTTP protocol version |
user_agent.original |
Mozilla/5.0... |
The User-Agent header value |
http.route |
/orders/{id} |
The matched route template |
http.response.status_code |
200 |
The response status code |
These align with the OpenTelemetry HTTP semantic conventions specification. If you have dashboards or alerts built around these attribute names, they will continue to work without modification.
Query parameter redaction
One detail worth highlighting: query string values are redacted by default. A request to /search?term=confidential&page=3 produces a url.query attribute of ?term=Redacted&page=Redacted. The parameter names are preserved but the values are replaced.
This is a sensible default. Query strings frequently carry sensitive data — API keys, search terms, user identifiers — and having those show up in your tracing backend is a compliance issue waiting to happen. The old instrumentation library recorded the full query string by default, which meant you either had to configure filtering or accept the risk.
// TIP
If you need the actual query values in your traces for debugging purposes, you can enrich the activity in middleware. But think carefully before doing so — tracing backends are often shared across teams and retention policies may not align with your data sensitivity requirements.
Migrating from the instrumentation package
If you are currently using OpenTelemetry.Instrumentation.AspNetCore, the migration path depends on how much you have customised the instrumentation.
Minimal configuration
If your setup is the standard AddAspNetCoreInstrumentation() call with no options, the migration is straightforward:
- Remove the
OpenTelemetry.Instrumentation.AspNetCorepackage reference - Replace
AddAspNetCoreInstrumentation()withAddSource("Microsoft.AspNetCore") - Verify your traces still contain the expected attributes
// Bad — old approach with external dependency
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddOtlpExporter());
// Good — new approach with native support
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore")
.AddOtlpExporter());
Custom enrichment or filtering
If you were using AspNetCoreTraceInstrumentationOptions to filter requests or enrich spans, you will need to move that logic elsewhere. The native implementation does not expose a configuration surface equivalent to the old options class.
For enrichment, use middleware or an activity listener:
public class TraceEnrichmentMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContext context)
{
var activity = Activity.Current;
if (activity is not null)
{
activity.SetTag("tenant.id", context.Request.Headers["X-Tenant-Id"].FirstOrDefault());
activity.SetTag("deployment.ring", Environment.GetEnvironmentVariable("DEPLOYMENT_RING"));
}
await next(context);
}
}
For filtering, you can use a custom processor that drops unwanted activities:
public class HealthCheckFilter : BaseProcessor<Activity>
{
public override void OnEnd(Activity data)
{
if (data.GetTagItem("http.route") is string route &&
route.StartsWith("/health", StringComparison.OrdinalIgnoreCase))
{
data.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
}
}
}
Register it in your tracing pipeline:
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Microsoft.AspNetCore")
.AddProcessor<HealthCheckFilter>()
.AddOtlpExporter());
Disabling native tracing
If you want to opt out of the native OpenTelemetry attributes entirely — perhaps because you are running a custom instrumentation setup and the additional attributes cause conflicts — you can suppress them with an AppContext switch:
AppContext.SetSwitch("Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData", true);
Alternatively, set it in your project file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<RuntimeHostConfigurationOption
Include="Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData"
Value="true" />
</ItemGroup>
</Project>
// WARNING
Suppressing the native attributes does not disable the Microsoft.AspNetCore activity source itself. Activities are still created — they just will not carry the semantic convention tags. If you need to disable activity creation entirely, that is a separate concern controlled by the System.Diagnostics configuration.
What this does not replace
The native support covers the HTTP server span — the top-level activity that represents an incoming HTTP request. It does not replace everything the instrumentation ecosystem provides:
- HttpClient tracing still requires
OpenTelemetry.Instrumentation.Http(orAddHttpClientInstrumentation()) for outgoing HTTP calls. - SQL and database tracing still requires the relevant instrumentation packages (
OpenTelemetry.Instrumentation.SqlClient,OpenTelemetry.Instrumentation.EntityFrameworkCore, etc.). - gRPC server tracing — while the instrumentation library previously handled gRPC hosted on ASP.NET Core, the scope of the native support in Preview 2 focuses on HTTP semantics. Check future previews for expanded coverage.
- Custom spans for your application logic still need manual
ActivitySourceandActivityusage, as they always have.
In other words, this change removes one package from a typical observability stack, not all of them. But it is the package that most often caused version conflicts and the one that every ASP.NET Core service needed.
The broader direction
This is part of a pattern. .NET 8 introduced built-in metrics following OpenTelemetry semantic conventions, removing the need for the metrics instrumentation package. .NET 11 Preview 2 does the same for tracing. The trajectory is clear: the framework is absorbing the most common instrumentation scenarios so that basic observability works without third-party dependencies.
The OpenTelemetry .NET project is aware of this shift. The OpenTelemetry.Instrumentation.AspNetCore package will continue to exist for scenarios that need the richer configuration surface — custom enrichment callbacks, request filtering predicates, and recording additional attributes beyond the specification. But for the standard case of "I want my traces to have the right attributes", the framework now handles it.
// NOTE
As of Preview 2, this is still a preview feature. The attribute set and behaviour may change before the .NET 11 GA release in November 2026. If you are adopting this in a production pipeline, pin to a specific preview version and verify attribute names against your dashboards after each update.
Common pitfalls
Running both native and library instrumentation simultaneously. If you upgrade to .NET 11 but keep AddAspNetCoreInstrumentation() in your pipeline, you may end up with duplicate attributes on the same activity. The library and the framework will both try to set the same tags. Remove the library call when targeting .NET 11 or later.
Assuming metrics are also native. Built-in metrics have been available since .NET 8, but they use a different mechanism (IMeterFactory and the Microsoft.AspNetCore.Hosting meter). Do not confuse the new native tracing with the existing native metrics — they are complementary but separate.
Forgetting to subscribe to the activity source. The framework creates and enriches the activities, but you still need to subscribe to Microsoft.AspNetCore in your tracing configuration for them to be exported. Without AddSource("Microsoft.AspNetCore"), the activities exist but no exporter will pick them up.
Expecting the old configuration options to work. AspNetCoreTraceInstrumentationOptions is part of the OpenTelemetry.Instrumentation.AspNetCore package. If you remove that package, you lose the options class. Move filtering and enrichment to middleware or custom processors as shown above.
Not testing after migration. Tracing changes are invisible until something breaks a dashboard. After migrating, verify that your traces in your backend still have the expected attributes, that your alerts still trigger correctly, and that any span-based SLOs are still being calculated from the right data.
Summary
- .NET 11 Preview 2 adds native OpenTelemetry semantic convention attributes to the ASP.NET Core HTTP server activity.
- The
OpenTelemetry.Instrumentation.AspNetCorepackage is no longer required for standard HTTP tracing. - Subscribe to the
Microsoft.AspNetCoreactivity source withAddSource("Microsoft.AspNetCore")instead of callingAddAspNetCoreInstrumentation(). - Query string values are redacted by default — a welcome change from the old library's behaviour.
- Custom enrichment and filtering need to move to middleware or custom processors.
- HTTP client, database, and gRPC instrumentation packages are still needed for those scenarios.
- Use the
SuppressActivityOpenTelemetryDataAppContext switch if you need to opt out.