Skip to content

    Back to articles

    5 min read

    Validate .NET 11 tracing before changing instrumentation

    Treat the preview as an evaluation prompt

    .NET 11 Preview 2 announced native OpenTelemetry tracing for ASP.NET Core on March 10, 2026 (the .NET 11 Preview 2 announcement). I would treat that as a reason to compare traces in a test environment, not as a reason to remove the instrumentation a service already relies on.

    The ASP.NET Core .NET 11 release notes describe subscribing to the Microsoft.AspNetCore activity source to collect built-in request traces. They also say the framework populates HTTP semantic-convention attributes on the request activity and that the OpenTelemetry.Instrumentation.AspNetCore package is not needed for that collection path. That is a useful change in where request instrumentation comes from. It does not answer whether the resulting trace has the shape my application needs, or whether other parts of my telemetry setup are still required.

    I would compare the old and new paths against the same request scenarios and inspect the exported traces side by side. The useful questions are practical: Is there one server span or more than one? Does it have the fields my dashboards and queries use? Do custom operations appear beneath the request in the right order? What important business context is still absent? Until I can answer those, I would not change production instrumentation.

    Make the comparison controlled

    Use a small test app or an isolated environment with the same relevant middleware, route handlers, custom spans, and telemetry exporter as the service under consideration. Send a repeatable request through each configuration. Keep the request and application behavior the same; change only which ASP.NET Core instrumentation path is enabled. Otherwise, differences in the trace could come from the test rather than the instrumentation.

    For example, a service using the OpenTelemetry instrumentation package might configure tracing along these lines:

    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddAspNetCoreInstrumentation()
            .AddSource("Orders")
            .AddOtlpExporter());

    The candidate built-in path subscribes to ASP.NET Core’s activity source instead. Keep the application source and exporter in place so the comparison isolates the server instrumentation:

    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing => tracing
            .AddSource("Microsoft.AspNetCore")
            .AddSource("Orders")
            .AddOtlpExporter());

    These snippets show the distinction documented in the ASP.NET Core tracing guidance: the built-in path subscribes to Microsoft.AspNetCore; the existing package registers its instrumentation for you. They are alternatives for collecting the server request activity, not a reason to remove unrelated sources or exporters. I would test them separately first. If both paths are active during a test, inspect whether they produce duplicate request spans rather than assuming either that duplication must happen or that it cannot.

    Pick representative requests, not just the easiest successful route. Include a request that returns an error, one that makes an outbound call, and one that exercises application-specific work if the service has those paths. The point is not to benchmark the preview. It is to learn whether the trace still explains the work the team needs to understand.

    Compare trace shape, then attributes

    Start with the tree. For each request, check the root span, its parent-child relationships, and the number of server request spans. A repeated span representing the same inbound request can make a trace confusing and distort counts in tooling. A missing request span, or one that is detached from the expected trace, is a different failure. I would record what I actually observe in the exported data rather than infer it from the configuration.

    Then compare the request attributes that matter to the team. The release notes list examples such as http.request.method, url.path, http.response.status_code, and server.address. Check the fields your dashboards, alerts, and search queries depend on; do not assume that a familiar key or value survives a change in instrumentation. Also check whether sensitive values or high-cardinality details are being captured. The instrumentation source is not a substitute for deciding what telemetry is appropriate to export.

    Finally, compare the custom spans. A server span can describe the HTTP request without explaining the application operation that matters to someone debugging it. If a trace currently has a span for a meaningful operation, verify that it remains present and correctly parented. If it does, keep it. If an old custom span merely duplicates generic request bookkeeping that the framework now provides, that may be a candidate to simplify—but only after confirming that no team or telemetry query relies on it.

    The OpenTelemetry distributed tracing walkthrough for .NET shows the established pattern of collecting ASP.NET Core activities alongside other sources. That separation is useful: framework instrumentation can describe the request boundary, while application-owned instrumentation can explain domain work. I would preserve that distinction in the review instead of treating “native tracing” as an instruction to delete all manual spans.

    Decide from evidence, not from the word native

    I would capture a short comparison record before changing the service: which configuration produced each trace, whether the request span count changed, which attributes changed, whether parentage stayed intact, and which custom spans remain necessary. A trace viewer screenshot or exported trace is more useful here than a checklist marked “enabled.” Keep the record specific to the test; don’t turn one route’s result into a claim about every service.

    If the built-in path produces the expected request activity and attributes without duplicate spans, then removing the ASP.NET Core instrumentation package may be a reasonable follow-up. Confirm what else that package configuration or adjacent setup was responsible for before removing it. If the trace is missing something the team needs, retain the current path while investigating the gap, or adapt the configuration after verifying the supported options. If both paths produce overlapping request spans, keep only the path that gives the desired trace and test again.

    I would make the production change separately from the evaluation. Update the target framework and instrumentation in a controlled change, then verify exported traces after deployment. A preview announcement is a useful signal to investigate, not production evidence. The decision should be based on whether the resulting trace remains readable and useful—not on whether the instrumentation now ships with ASP.NET Core.

    Drafted with AI assistance, reviewed and edited by Ali.