Skip to main content

Distributed Tracing

Vali-Mediator creates an Activity (OpenTelemetry span) for each request, notification, and fire-and-forget command.

ActivitySource

// Access the source directly
var source = ValiMediatorDiagnostics.ActivitySource;
// Name: "Vali-Mediator"
// Version: "2.0.0"

OpenTelemetry Setup

builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("MyApp", serviceVersion: "1.0.0"))
// Listen to Vali-Mediator spans
.AddSource("Vali-Mediator")
// Also listen to your own spans
.AddSource("MyApp.*")
// Export to Jaeger
.AddJaegerExporter(opts =>
{
opts.AgentHost = "localhost";
opts.AgentPort = 6831;
})
// Or to Zipkin
.AddZipkinExporter(opts =>
{
opts.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
})
// Or to OTLP (Grafana, Tempo, etc.)
.AddOtlpExporter();
});

Activity Tags

Each request activity includes these tags:

TagValue
mediator.request.typeFull type name of the request
mediator.handler.typeFull type name of the handler
mediator.request.successtrue / false
mediator.request.error_typeErrorType value (if failed)
mediator.request.duration_msDuration in milliseconds

Creating Child Activities

From within a handler, create child spans that nest under the Vali-Mediator span:

public class GetProductHandler : IRequestHandler<GetProductQuery, Result<ProductDto>>
{
private static readonly ActivitySource Source = new("MyApp.Handlers");

public async Task<Result<ProductDto>> Handle(GetProductQuery req, CancellationToken ct)
{
using var activity = Source.StartActivity("GetProduct.DatabaseQuery");
activity?.SetTag("product.id", req.ProductId.ToString());

var product = await _repository.GetByIdAsync(req.ProductId, ct);

activity?.SetTag("product.found", product is not null);

if (product is null)
return Result<ProductDto>.Fail("Not found.", ErrorType.NotFound);

return Result<ProductDto>.Ok(product.ToDto());
}
}

Aspire Dashboard

For local development, use the .NET Aspire Dashboard for a zero-config trace viewer:

// OTLP exporter to Aspire Dashboard
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Vali-Mediator")
.AddOtlpExporter(opts =>
{
opts.Endpoint = new Uri("http://localhost:4317");
}));