Observability
Mediant emits OpenTelemetry-compatible traces and metrics using the System.Diagnostics
primitives built into .NET. There is no dependency on the OpenTelemetry SDK — you wire it up only if you
want to collect them.
Wire it up
builder.Services.AddOpenTelemetry() .WithTracing(t => t.AddSource(MediatorDiagnostics.ActivitySourceName)) // "Mediant" .WithMetrics(m => m.AddMeter(MediatorDiagnostics.MeterName)); // "Mediant"Both constants resolve to the string "Mediant". Use the constants rather than the literal — they are
the contract.
Spans
| Span | When |
|---|---|
mediator.send <RequestType> | Every Send and CreateStream |
mediator.publish <NotificationType> | Every Publish |
Each span carries the request or notification type as a tag, and an Ok or Error status. Failures
record error.type.
Because behaviors run inside the span, a slow request shows you which behavior was slow — the retry backoff, the cache miss, the transaction commit.
Metrics
| Instrument | Kind |
|---|---|
mediant.send.count | Counter |
mediant.send.duration | Histogram |
mediant.publish.count | Counter |
mediant.publish.duration | Histogram |
Cost when nothing listens
This is the part that matters. With no listener registered — the default — the hot path performs a cheap
ActivitySource.HasListeners() / Instrument.Enabled check and skips all activity creation and
measurement. The benchmark numbers are measured with instrumentation compiled in and
nobody listening; that is the cost you pay for having it available.
Structured logging
Separately from OpenTelemetry, the logging behavior (order 200, always on when you use
Mediant.Behaviors) writes a structured log line per request through ILogger. It masks properties
marked [SensitiveData], plus anything you name explicitly, and it is safe against circular references
in the request graph.
builder.Services.AddMediantAllBehaviors(opts =>{ opts.ConfigureLogging = log => { log.MaskProperties.Add("CardNumber"); log.MaxSerializedLength = 4096; // truncate large payloads };});For a durable, queryable trail rather than log lines, use [Auditable] with an IAuditStore — see
Pipeline behaviors and EF Core.
Performance thresholds
[PerformanceThreshold] logs a warning past WarningMs and an error past CriticalMs, with the elapsed
time. It’s the cheapest way to notice a handler that has quietly gotten slower, without standing up a
tracing backend first.
[PerformanceThreshold(WarningMs = 500, CriticalMs = 5000)]public record GenerateMonthlyReport(int Year, int Month) : ICommand<Result<Uri>>;