Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Eventuous/eventuous/llms.txt

Use this file to discover all available pages before exploring further.

Eventuous ships dedicated OpenTelemetry instrumentation packages that expose distributed tracing, metrics, and a logging bridge with zero boilerplate. All internal spans and metrics are produced via the standard .NET ActivitySource and Meter APIs, so they compose seamlessly with the rest of your OpenTelemetry pipeline. Two NuGet packages are involved:
Eventuous.Diagnostics.OpenTelemetry   # tracing + metrics
Eventuous.Diagnostics.Logging         # ILoggerFactory bridge

Tracing

AddEventuousTracing() is an extension method on TracerProviderBuilder. It does three things at once:
  1. Registers the Eventuous ActivitySource — identified by EventuousDiagnostics.InstrumentationName — so all command handling and event store spans are captured.
  2. Removes the DummyListener — Eventuous installs a no-op listener on startup so that W3C trace-context headers are propagated even before a real collector is attached. Calling AddEventuousTracing() removes that listener because OpenTelemetry takes over.
  3. Adds a PollingSampler — suppresses low-level internal client spans (kind Client, name "eventuous") that have no parent trace, preventing subscription polling noise from flooding your backend.
services.AddOpenTelemetry()
    .WithTracing(builder => {
        builder
            .AddAspNetCoreInstrumentation()
            .AddGrpcClientInstrumentation()
            .AddEventuousTracing()               // ← Eventuous spans
            .SetResourceBuilder(
                ResourceBuilder.CreateDefault().AddService("bookings")
            )
            .SetSampler(new AlwaysOnSampler());

        if (otelEnabled)
            builder.AddOtlpExporter();
        else
            builder.AddZipkinExporter();
    });

What is traced automatically

OperationSpan description
Command handlingOne span per ICommandService<TState>.Handle() call, tagged with command type
Event store readsOne span per ReadStream / ReadEvents call
Event store writesOne span per AppendToStream call
Subscription event processingOne span per event batch, tagged with subscription ID and stream

Metrics

Two extension methods on MeterProviderBuilder cover different layers of the system.

AddEventuous()

Registers meters for core components:
  • CommandServiceMetrics — counts commands handled, errors, and records latency histograms per command type.
  • PersistenceMetrics — counts event store reads and writes.
services.AddOpenTelemetry()
    .WithMetrics(builder => {
        builder
            .SetResourceBuilder(
                ResourceBuilder.CreateDefault().AddService("bookings")
            )
            .AddAspNetCoreInstrumentation()
            .AddEventuous()                      // ← command service + store metrics
            .AddPrometheusExporter();
    });

AddEventuousSubscriptions()

Registers SubscriptionMetrics, which exposes:
  • Events processed count (tagged by subscription ID and event type).
  • Subscription lag (distance behind the end of stream).
  • Processing duration histogram.
builder
    .AddEventuous()
    .AddEventuousSubscriptions()                 // ← subscription metrics
    .AddPrometheusExporter();
Both methods accept an optional TagList? customTags parameter to attach static key-value tags (e.g., environment name, deployment region) to every metric data point:
var tags = new TagList { { "env", "production" } };
builder.AddEventuous(tags).AddEventuousSubscriptions(tags);

Logging bridge

LoggingEventListener subscribes to Eventuous’ internal EventSource events and forwards them to ILoggerFactory, making internal diagnostics visible in your structured logs alongside application logs. Instantiate it after building the WebApplication (so the ILoggerFactory is available) and dispose it during teardown:
var factory  = app.Services.GetRequiredService<ILoggerFactory>();
var listener = new LoggingEventListener(factory);

try {
    app.Run();
} finally {
    listener.Dispose();
}
Pass "OpenTelemetry" as the prefix argument to additionally capture diagnostic events emitted by the OpenTelemetry SDK, whose EventSource names start with that prefix:
var listener = new LoggingEventListener(factory, "OpenTelemetry");
The constructor signature in full:
public LoggingEventListener(
    ILoggerFactory loggerFactory,
    string?        prefix   = null,        // default: "eventuous"
    EventLevel     level    = EventLevel.Verbose,
    EventKeywords  keywords = EventKeywords.All
)

Complete telemetry setup

The following AddTelemetry extension method is taken directly from the Bookings sample and combines all three elements — tracing, metrics, and the Prometheus exporter — in a single place:
public static class Registrations {
    extension(IServiceCollection services) {
        public void AddTelemetry() {
            var otelEnabled =
                Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") != null;

            // ── Metrics ─────────────────────────────────────────────────────────
            services.AddOpenTelemetry()
                .WithMetrics(builder => {
                    builder
                        .SetResourceBuilder(
                            ResourceBuilder.CreateDefault().AddService("bookings")
                        )
                        .AddAspNetCoreInstrumentation()
                        .AddEventuous()
                        .AddEventuousSubscriptions()
                        .AddPrometheusExporter();

                    if (otelEnabled) builder.AddOtlpExporter();
                });

            // ── Traces ──────────────────────────────────────────────────────────
            services.AddOpenTelemetry()
                .WithTracing(builder => {
                    builder
                        .AddAspNetCoreInstrumentation()
                        .AddGrpcClientInstrumentation()
                        .AddEventuousTracing()
                        .SetResourceBuilder(
                            ResourceBuilder.CreateDefault().AddService("bookings")
                        )
                        .SetSampler(new AlwaysOnSampler());

                    if (otelEnabled)
                        builder.AddOtlpExporter();
                    else
                        builder.AddZipkinExporter();
                });
        }
    }
}
And in Program.cs, the Prometheus scraping endpoint is exposed and the logging bridge is activated:
app.UseOpenTelemetryPrometheusScrapingEndpoint();

var factory  = app.Services.GetRequiredService<ILoggerFactory>();
var listener = new LoggingEventListener(factory, "OpenTelemetry");

try {
    app.Run("http://*:5051");
    return 0;
} catch (Exception e) {
    Log.Fatal(e, "Host terminated unexpectedly");
    return 1;
} finally {
    Log.CloseAndFlush();
    listener.Dispose();
}

Build docs developers (and LLMs) love