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:
# {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 always00
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:
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
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:
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
:
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:
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
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
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.