Skip to main content

Telemetry Configuration

Configure OpenTelemetry integration for metrics collection and distributed tracing across the Excalibur framework.

Before You Start

  • .NET 8.0+ (or .NET 9/10 for latest features)
  • Install the required packages:
    dotnet add package Excalibur.Dispatch.Observability
    dotnet add package OpenTelemetry.Extensions.Hosting
  • Familiarity with OpenTelemetry .NET concepts
  • An OpenTelemetry-compatible backend (Prometheus, OTLP, Azure Monitor, etc.)

Quick Start

Register All Metrics and Tracing

The simplest approach registers all framework meters and activity sources at once:

builder.Services.AddOpenTelemetry()
.AddAllDispatchMetrics()
.AddAllDispatchTracing();

These extension methods are defined in OpenTelemetryExtensions from the Excalibur.Dispatch.Observability package.

With Exporters

Combine with your preferred exporter:

builder.Services.AddOpenTelemetry()
.AddAllDispatchMetrics()
.AddAllDispatchTracing()
.WithMetrics(metrics => metrics
.AddPrometheusExporter())
.WithTracing(tracing => tracing
.AddOtlpExporter());

Metrics Registration

AddAllDispatchMetrics() registers all known meters across the entire Excalibur framework:

// On IOpenTelemetryBuilder (simplest)
builder.Services.AddOpenTelemetry()
.AddAllDispatchMetrics();

// On MeterProviderBuilder (within WithMetrics)
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddAllDispatchMetrics()
.AddPrometheusExporter());

This registers the following meters:

Meter NamePackageDescription
Excalibur.Dispatch.CoreDispatchCore message processing counters and histograms
Excalibur.Dispatch.PipelineDispatchPipeline execution metrics
Excalibur.Dispatch.TimePolicyDispatchTime policy metrics
Excalibur.Dispatch.BatchProcessorDispatchBatch processing metrics
Excalibur.Dispatch.TransportTransport.AbstractionsTransport send/receive/error counters
Excalibur.Dispatch.DeadLetterQueueObservabilityDead letter queue metrics
Excalibur.Dispatch.CircuitBreakerObservabilityCircuit breaker state and operation metrics
Excalibur.Dispatch.StreamingDispatchStreaming document handler metrics
Excalibur.Dispatch.ComplianceComplianceCompliance monitoring metrics
Excalibur.Dispatch.Compliance.ErasureComplianceGDPR erasure request metrics
Excalibur.Dispatch.EncryptionComplianceEncryption operation metrics
Excalibur.Dispatch.BackgroundServicesOutboxBackground service processing metrics
Excalibur.Dispatch.SagasSagaSaga orchestration metrics
Excalibur.Dispatch.WriteStoresData.AbstractionsWrite store operation metrics
Excalibur.Dispatch.Observability.ContextObservabilityContext flow tracking metrics
Excalibur.EventSourcing.MaterializedViewsEventSourcingMaterialized view rebuild metrics
Excalibur.Data.CdcData.SqlServerChange data capture metrics
Excalibur.Data.AuditData.ElasticSearchSecurity audit metrics
Excalibur.LeaderElectionLeaderElectionLeader election acquisition metrics

Per-Package Opt-In Registration

For fine-grained control, register individual meters:

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
// Core dispatch only
metrics.AddDispatchMetrics();

// Transport only
metrics.AddTransportMetrics();

// Or use AddMeter with specific names
metrics.AddMeter("Excalibur.Dispatch.CircuitBreaker");
metrics.AddMeter("Excalibur.LeaderElection");
});

The AddDispatchMetrics() and AddTransportMetrics() convenience methods are available on both IOpenTelemetryBuilder and MeterProviderBuilder.

DI-Based Metrics Registration

Register metrics instrumentation services for dependency injection:

// All observability metrics (core + circuit breaker + DLQ)
builder.Services.AddAllDispatchMetrics();

// Or individually
builder.Services.AddDispatchMetricsInstrumentation();
builder.Services.AddCircuitBreakerMetrics();
builder.Services.AddDeadLetterQueueMetrics();

// With configuration
builder.Services.AddDispatchMetricsInstrumentation(options =>
{
options.MeterName = "MyApp.Dispatch";
});

These extension methods from ObservabilityMetricsServiceCollectionExtensions register the singleton metric classes (DispatchMetrics, CircuitBreakerMetrics, DeadLetterQueueMetrics) into the DI container.

Transport-Specific Meters

Each transport registers its own meter automatically during AddXxxTransport():

TransportMeter Name
RabbitMQExcalibur.Dispatch.Transport.RabbitMQ
KafkaExcalibur.Dispatch.Transport.Kafka
Azure Service BusExcalibur.Dispatch.Transport.AzureServiceBus
Google Pub/SubExcalibur.Dispatch.Transport.GooglePubSub
AWS SQSExcalibur.Dispatch.Transport.AwsSqs

These follow the pattern Excalibur.Dispatch.Transport.{TransportName} defined in TransportTelemetryConstants.MeterName().

To subscribe to transport-specific meters:

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
// All transports at once (wildcard)
metrics.AddMeter("Excalibur.Dispatch.Transport.*");

// Or specific transports
metrics.AddMeter("Excalibur.Dispatch.Transport.Kafka");
metrics.AddMeter("Excalibur.Dispatch.Transport.RabbitMQ");
});

Tracing Registration

AddAllDispatchTracing() registers all known activity sources:

// On IOpenTelemetryBuilder
builder.Services.AddOpenTelemetry()
.AddAllDispatchTracing();

// On TracerProviderBuilder
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAllDispatchTracing()
.AddOtlpExporter());

This registers the following activity sources:

Activity Source NamePackageDescription
Excalibur.Dispatch.CoreDispatchCore message processing spans
Excalibur.Dispatch.PipelineDispatchPipeline execution spans
Excalibur.Dispatch.TimePolicyDispatchTime policy operation spans
Excalibur.Dispatch.StreamingDispatchStreaming handler spans
Excalibur.Dispatch.Compliance.ErasureComplianceGDPR erasure operation spans
Excalibur.Data.CdcData.SqlServerCDC processing spans
Excalibur.Data.AuditData.ElasticSearchAudit recording spans
Excalibur.LeaderElectionLeaderElectionLeader election acquisition spans

Per-Source Opt-In

builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
// Only core dispatch tracing
tracing.AddSource("Excalibur.Dispatch.Core");

// Or use wildcards
tracing.AddSource("Excalibur.Dispatch.*");
});

Pipeline-Level Telemetry

Enable OpenTelemetry middleware in the Dispatch pipeline:

builder.Services.AddDispatch(dispatch =>
{
dispatch.UseOpenTelemetry(); // Enables tracing + metrics (recommended)
});

// Or enable individually
builder.Services.AddDispatch(dispatch =>
{
dispatch.UseTracing(); // Distributed tracing only
dispatch.UseMetrics(); // Metrics only
});

This registers:

  • TracingMiddleware -- Creates OpenTelemetry spans for each dispatched message
  • MetricsMiddleware -- Records processing duration, success/failure counts

Custom Metric Filtering

Filter by Meter Name

Use OpenTelemetry's view API to filter or customize metrics:

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAllDispatchMetrics();

// Drop high-cardinality tags from transport metrics
metrics.AddView(
instrumentName: "dispatch.transport.send.duration",
new MetricStreamConfiguration
{
TagKeys = ["dispatch.transport.name"]
});

// Set histogram boundaries for processing duration
metrics.AddView(
instrumentName: "dispatch.messages.duration",
new ExplicitBucketHistogramConfiguration
{
Boundaries = [1, 5, 10, 25, 50, 100, 250, 500, 1000]
});
});

Suppress Specific Metrics

builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAllDispatchMetrics();

// Drop a specific metric instrument entirely
metrics.AddView(
instrumentName: "dispatch.circuitbreaker.state",
MetricStreamConfiguration.Drop);
});

Telemetry Constants Reference

Each package exposes its telemetry names through constants classes. Use these when building custom instrumentation or filtering rules.

Constants ClassNamespaceContains
DispatchTelemetryConstantsExcalibur.Dispatch.DiagnosticsCore meter/activity/tag names
StreamingHandlerTelemetryConstantsExcalibur.Dispatch.DiagnosticsStreaming meter/activity names
TransportTelemetryConstantsExcalibur.Dispatch.Transport.DiagnosticsTransport metric/tag names
GooglePubSubTelemetryConstantsExcalibur.Dispatch.Transport.GooglePubSubGoogle Pub/Sub consolidated names
ContextObservabilityTelemetryConstantsExcalibur.Dispatch.Observability.DiagnosticsContext flow meter/activity names
ErasureTelemetryConstantsExcalibur.Dispatch.Compliance.DiagnosticsGDPR erasure meter/activity/metric names
CdcTelemetryConstantsExcalibur.Data.SqlServer.DiagnosticsCDC meter/activity/metric names
AuditTelemetryConstantsExcalibur.Data.ElasticSearch.DiagnosticsAudit meter/activity/metric names
LeaderElectionTelemetryConstantsExcalibur.LeaderElection.DiagnosticsLeader election meter/activity/metric names
EventSourcingMetersExcalibur.EventSourcing.ObservabilityEvent/snapshot store meter names
EventSourcingActivitySourcesExcalibur.EventSourcing.ObservabilityEvent/snapshot store activity source names

Complete Example

var builder = WebApplication.CreateBuilder(args);

// 1. Register Dispatch with pipeline telemetry
builder.Services.AddDispatch(dispatch =>
{
dispatch.UseOpenTelemetry();
});

// 2. Register DI-based metrics instrumentation
builder.Services.AddAllDispatchMetrics();

// 3. Configure OpenTelemetry with all meters and activity sources
builder.Services.AddOpenTelemetry()
.AddAllDispatchMetrics()
.AddAllDispatchTracing()
.WithMetrics(metrics =>
{
// Transport-specific meters (registered automatically by AddXxxTransport)
metrics.AddMeter("Excalibur.Dispatch.Transport.*");

// Event sourcing meters (if using ES)
metrics.AddMeter("Excalibur.EventSourcing.*");

// Export to Prometheus
metrics.AddPrometheusExporter();
})
.WithTracing(tracing =>
{
// Event sourcing activity sources (if using ES)
tracing.AddSource("Excalibur.EventSourcing.*");

// Export to OTLP
tracing.AddOtlpExporter();
});

var app = builder.Build();

// Expose Prometheus scrape endpoint
app.MapPrometheusScrapingEndpoint();

app.Run();

See Also