Skip to content

Metrics

blehnen edited this page Nov 10, 2026 · 14 revisions

Metrics

Metrics use System.Diagnostics.Metrics from the core library. You do not need an extra package.

Note for advanced users: The library's metric abstractions map to System.Diagnostics.Metrics (SDM) types as follows: Counter = UpDownCounter<long>, Meter = Counter<long> (monotonic), Histogram = Histogram<long>, Timer = Histogram<double> (unit: ms), Gauge = ObservableGauge<double>. Some metric backends display the SDM type name rather than the library's abstraction name.

Metrics are on by default. The library records these types:

  • Counter: state tracking (up/down counts)
  • Meter: rate measurement (events per second)
  • Histogram: distribution of values
  • Timer: duration tracking
  • Gauge: observable values through lambda functions

All metrics are published under the DotNetWorkQueue meter name (PascalCase as registered, but some backends normalize it to lowercase). Each metric is prefixed with dotnetworkqueue.{queueName} (e.g. dotnetworkqueue.myqueue.Serializer.SendMessagesMeter). Some backends convert dots to underscores (e.g. dotnetworkqueue_myqueue_Serializer_SendMessagesMeter).

Available metrics
Metric Type What it tracks
SendTimer Timer Time to send a single message
SendAsyncTimer Timer Time to send a single message (async)
SendBatchTimer Timer Time to send a batch
SendMessagesMeter Meter Messages sent (rate)
SendMessagesErrorMeter Meter Send failures (rate)
HandleTimer Timer Time in user message handler
HandleCompiledMethodTimer Timer Time in compiled LINQ handler
HandleDynamicMethodTimer Timer Time in dynamic LINQ handler
CommitCounter Counter Messages committed after processing
RollbackCounter Counter Messages rolled back
MessageFailedProcessingErrorMeter Meter Messages that failed and moved to error
MessageFailedProcessingRetryMeter Meter Messages that failed and will retry
PoisonHandleMeter Meter Poison messages handled
ResetTimer (heartbeat context) Timer Time to reset stale heartbeats
ResetCounter (heartbeat context) Counter Heartbeat resets performed
ClearMessages.ResetTimer Timer Time to clear expired messages
ClearMessages.ResetCounter Counter Expired messages cleared
SendTimer (heartbeat context) Timer Time to send a heartbeat update
ConvertMessageToBytesTimer Timer Serialization time
ConvertBytesToMessageTimer Timer Deserialization time
ConvertMessageToBytesHistogram Histogram Serialized message size (bytes)
CompileActionTimer Timer LINQ expression compile time
LinqActionCacheHitCounter Counter LINQ cache hits
LinqActionCacheMissCounter Counter LINQ cache misses
BytesToMessageTimer Timer Interceptor decode time
MessageToBytesTimer Timer Interceptor encode time
WaitingOnTaskCounter Counter Tasks waiting for a free thread (scheduler)
Collecting and exporting metrics

DotNetWorkQueue does not ship an exporter. Use OpenTelemetry.Metrics with whatever backend you prefer.

Prometheus (example)

One option is to export metrics to Prometheus through the ASP.NET Core exporter and show them in Grafana.

1. Add NuGet packages:

dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore

2. Configure in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics.AddMeter("DotNetWorkQueue");   // capture all queue metrics
        metrics.AddPrometheusExporter();
    });

var app = builder.Build();
app.MapPrometheusScrapingEndpoint();           // exposes /metrics
app.Run();

3. Scrape from Prometheus. Add this to prometheus.yml:

scrape_configs:
  - job_name: 'dotnetworkqueue'
    scrape_interval: 15s
    static_configs:
      - targets: ['localhost:5000']    # your app's host:port

4. Query in Prometheus. Go to http://localhost:9090 and type dotnetworkqueue_. Autocomplete then shows all queue metrics.

Note: OTLP export converts dots to underscores in some backends. Metric names may appear as dotnetworkqueue_myqueue_Serializer_ConvertMessageToBytesHistogram_Bytes_bucket. All DotNetWorkQueue metrics share the dotnetworkqueue prefix.

5. Grafana (optional). The samples repo includes a pre-built Grafana dashboard. It works with any Prometheus-compatible data source.

To import it:

  1. Add your metrics backend as a data source in Grafana
  2. Go to Dashboards → New → Import → Upload JSON file
  3. Select grafana-dashboard.json from the samples repo root
  4. Pick your data source when prompted
  5. The queue name dropdown auto-populates from available metrics

The dashboard includes panels for message throughput (sent, received, committed, rollbacks, errors, retries), send/receive rates, error and retry rates, message handler duration, commits and rollbacks over time, heartbeat and maintenance counters, serialization time and message sizes, and interceptor encode/decode time.

DotNetWorkQueue Grafana Dashboard

This screenshot is hosted in the samples repo. If the image fails to load, visit the samples repo directly.

OTLP (OpenTelemetry Protocol)

For backends that accept OTLP (Prometheus, Grafana Cloud, Azure Monitor, Datadog, etc.):

dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics.AddMeter("DotNetWorkQueue");
        metrics.AddOtlpExporter(options =>
        {
            // Prometheus OTLP endpoint (requires --web.enable-otlp-receiver flag)
            options.Endpoint = new Uri("http://localhost:9090/api/v1/otlp/v1/metrics");
            options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf;
        });
    });

For gRPC-based OTLP collectors (Jaeger, OpenTelemetry Collector), use http://localhost:4317 with the default gRPC protocol instead.

Console (debugging)

Use this exporter to check that DotNetWorkQueue emits metrics:

dotnet add package OpenTelemetry.Exporter.Console
builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics.AddMeter("DotNetWorkQueue");
        metrics.AddConsoleExporter();
    });
Non-ASP.NET Core hosts (console apps, Windows services)

If you do not use WebApplication, create the meter provider directly:

using OpenTelemetry;
using OpenTelemetry.Metrics;

var meterProvider = Sdk.CreateMeterProviderBuilder()
    .AddMeter("DotNetWorkQueue")
    .AddPrometheusHttpListener(options =>
    {
        options.UriPrefixes = new[] { "http://localhost:9464/" };
    })
    .Build();

// ... run your consumer/producer ...

// Dispose when shutting down
meterProvider.Dispose();

This starts an HTTP listener on port 9464 that Prometheus can scrape.

Reading metrics in code

You can also read metric snapshots directly without an exporter:

// From a consumer queue
var metrics = container.GetInstance<IMetrics>();
var snapshot = metrics.GetCollectedMetrics();

foreach (var counter in snapshot.Counters)
    Console.WriteLine($"{counter.Key}: {counter.Value}");

foreach (var meter in snapshot.Meters)
    Console.WriteLine($"{meter.Key}: {meter.Value}");

Note: GetCollectedMetrics() returns cumulative counter and meter totals. For time-series data and rate calculations, use an OpenTelemetry exporter with a proper metrics backend.

Samples

The samples repo includes working examples of OpenTelemetry configuration for both tracing and metrics export. See the SampleShared/Injectors.cs file for how to wire up OpenTelemetry providers.

Disabling metrics

If you do not configure an OpenTelemetry exporter, the library still records metrics internally. The overhead of the System.Diagnostics.Metrics instrumentation alone is negligible.

Clone this wiki locally