Skip to content

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

SpanWhen
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

InstrumentKind
mediant.send.countCounter
mediant.send.durationHistogram
mediant.publish.countCounter
mediant.publish.durationHistogram

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>>;