OpenTelemetry Traceparent HTTP Header [.NET]

What is traceparent header?

The traceparent HTTP header contains information about the incoming request in a distributed tracing system, for example:

text
# {version}-{trace-id}-{parent-id}-{trace-flags}
traceparent: 00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01

You can find the traceparent header in HTTP responses using browser developer tools to extract trace IDs and locate specific traces in distributed tracing tools.

Using the header, you can extract a trace ID to find the trace in a one of distributed tracing tools. For example, from the header above, the trace ID is 80e1afed08e019fc1110464cfa66635c.

Traceparent header format

The traceparent header uses the version-trace_id-parent_id-trace_flags format where:

  • version is always 00
  • trace_id is a hex-encoded trace ID (16 bytes)
  • span_id is a hex-encoded span ID (8 bytes)
  • trace_flags is a hex-encoded 8-bit field containing tracing flags such as sampling

Injecting traceparent header

ASP.NET Core Middleware

You can inject the traceparent header into HTTP responses using ASP.NET Core middleware:

cs
using System.Diagnostics;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;

public class TraceparentMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TextMapPropagator _propagator;

    public TraceparentMiddleware(RequestDelegate next)
    {
        _next = next;
        _propagator = Propagators.DefaultTextMapPropagator;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Execute the rest of the pipeline
        await _next(context);

        // Inject traceparent header into response
        var activity = Activity.Current;
        if (activity != null)
        {
            _propagator.Inject(new PropagationContext(activity.Context, Baggage.Current),
                context.Response.Headers,
                (headers, key, value) => headers[key] = value);
        }
    }
}

// Register middleware
app.UseMiddleware<TraceparentMiddleware>();

Minimal API Example

cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseMiddleware<TraceparentMiddleware>();

app.MapGet("/api/users", () =>
{
    using var activity = MyActivitySource.StartActivity("GetUsers");
    // Your business logic here
    return Results.Ok(new { users = new[] { "Alice", "Bob" } });
});

app.Run();

Extracting

From incoming requests

ASP.NET Core automatically extracts traceparent headers from incoming requests when OpenTelemetry is configured. You can also manually extract trace context:

cs
public class TraceExtractionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly TextMapPropagator _propagator;

    public TraceExtractionMiddleware(RequestDelegate next)
    {
        _next = next;
        _propagator = Propagators.DefaultTextMapPropagator;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Extract trace context from incoming headers
        var propagationContext = _propagator.Extract(default, context.Request.Headers,
            (headers, key) => headers.TryGetValue(key, out var value) ? (IEnumerable<string>)value : null);

        if (propagationContext.ActivityContext.IsValid())
        {
            // Create activity with extracted parent context
            using var activity = MyActivitySource.StartActivity("ProcessRequest",
                ActivityKind.Server, propagationContext.ActivityContext);

            await _next(context);
        }
        else
        {
            await _next(context);
        }
    }
}

HttpClient integration

Outgoing requests

For outgoing HTTP requests, OpenTelemetry automatically injects traceparent headers when using instrumented HttpClient:

cs
using OpenTelemetry;
using OpenTelemetry.Trace;

// Configure OpenTelemetry with HttpClient instrumentation
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddHttpClientInstrumentation()
        .AddOtlpExporter()
    );

// HttpClient will automatically include traceparent headers
builder.Services.AddHttpClient<UserService>();

public class UserService
{
    private readonly HttpClient _httpClient;

    public UserService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<string> GetUserAsync(int id)
    {
        // This request will automatically include traceparent header
        var response = await _httpClient.GetAsync($"https://api.example.com/users/{id}");
        return await response.Content.ReadAsStringAsync();
    }
}

Manual header injection

If you need manual control over header injection:

cs
public async Task<string> CallExternalServiceAsync()
{
    using var httpClient = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data");

    // Manually inject traceparent header
    var propagator = Propagators.DefaultTextMapPropagator;
    propagator.Inject(new PropagationContext(Activity.Current?.Context ?? default, Baggage.Current),
        request.Headers,
        (headers, key, value) => headers.Add(key, value));

    var response = await httpClient.SendAsync(request);
    return await response.Content.ReadAsStringAsync();
}

ASP.NET Core setup

cs
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System.Diagnostics;

var builder = WebApplication.CreateBuilder(args);

// Create ActivitySource
var activitySource = new ActivitySource("MyApp");

// Configure OpenTelemetry
builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource
        .AddService("my-api", "1.0.0"))
    .WithTracing(tracing => tracing
        .AddSource("MyApp")
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter()
    );

var app = builder.Build();

// Add traceparent injection middleware
app.UseMiddleware<TraceparentMiddleware>();

app.MapGet("/api/trace-example", async () =>
{
    using var activity = activitySource.StartActivity("TraceExample");
    activity?.SetTag("example.type", "demo");

    // Simulate some work
    await Task.Delay(100);

    return Results.Ok(new {
        message = "Check response headers for traceparent",
        traceId = Activity.Current?.TraceId.ToString()
    });
});

app.Run();

Debugging

Logging trace information

cs
public class TraceLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<TraceLoggingMiddleware> _logger;

    public TraceLoggingMiddleware(RequestDelegate next, ILogger<TraceLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var activity = Activity.Current;
        if (activity != null)
        {
            _logger.LogInformation("Processing request with TraceId: {TraceId}, SpanId: {SpanId}",
                activity.TraceId, activity.SpanId);
        }

        await _next(context);
    }
}

Checking headers

You can verify traceparent headers are being added by checking the browser's Network tab in Developer Tools or using tools like Postman.

OpenTelemetry APM

Uptrace is a OpenTelemetry backend that supports distributed tracing, metrics, and logs. You can use it to monitor applications and troubleshoot issues.

Uptrace comes with an intuitive query builder, rich dashboards, alerting rules with notifications, and integrations for most languages and frameworks.

Uptrace can process billions of spans and metrics on a single server and allows you to monitor your applications at 10x lower cost.

In just a few minutes, you can try Uptrace by visiting the cloud demo (no login required) or running it locally with Docker. The source code is available on GitHub.

What's next?