Skip to content
Brian Lehnen edited this page Apr 9, 2026 · 6 revisions

Tracing

DotNetWorkQueue uses System.Diagnostics.ActivitySource for tracing, which plugs into OpenTelemetry. All activities come from a single named source you subscribe to in your telemetry pipeline.

ActivitySource name: dotnetworkqueue.instrumentationlibrary

Packages

You will need the OpenTelemetry hosting extension plus an exporter of your choice:

OpenTelemetry.Extensions.Hosting
OpenTelemetry.Exporter.Jaeger   (or any other exporter)
ASP.NET Core Setup
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource("dotnetworkqueue.instrumentationlibrary")
        .AddJaegerExporter());
Non-ASP.NET Core Setup
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("dotnetworkqueue.instrumentationlibrary")
    .AddJaegerExporter()
    .Build();

Keep the tracerProvider instance alive for the lifetime of your application. Disposing it flushes and shuts down the exporter.

Trace Context Propagation

Trace context flows from producer to consumer automatically via message headers. When a producer enqueues a message, the current Activity context gets captured and stored in the message. The consumer restores that context before running your handler, so the full producer-to-consumer span chain shows up in your tracing backend without any extra configuration.

Activities Reference

The following activities are emitted by DotNetWorkQueue. All activity names are emitted under the dotnetworkqueue.instrumentationlibrary source.

Activity Description
ReceiveMessage Dequeue of a message from the transport
MessageHandler Synchronous user handler execution
MessageHandlerAsync Async user handler execution
LinqExecution LINQ handler execution
Commit Message commit after successful handler
RollBack Message rollback
Remove Message removal from the queue
Error Message moved to error state
PoisonMessage Poison message handling
ResetHeartBeat Stale heartbeat reset
SendHeartBeat Heartbeat update during processing
SendJobAsync Scheduler job enqueue
MessageSerializerMessageToBytes{Name} Serialization (name reflects the serializer)
MessageSerializerBytesToMessage{Name} Deserialization (name reflects the serializer)
MessageInterceptorMessageToBytes{Name} Interceptor encode (name reflects the interceptor)
MessageInterceptorBytesToMessage{Name} Interceptor decode (name reflects the interceptor)
SendMessage Message send (Memory transport)
SchedulerMessageHandler Scheduler handler execution

The {Name} suffix on serializer and interceptor activities is substituted at runtime with the display name of the specific serializer or interceptor implementation in use (e.g., MessageSerializerMessageToBytesJsonSerializer, MessageInterceptorMessageToBytesGZip).

Common Span Tags

Every activity is tagged with contextual metadata:

Tag Source Notes
Server IConnectionInformation.Server Transport server/host
Queue IConnectionInformation.QueueName Queue name
CorrelationId Message headers Cross-system correlation
Route Message route Only present when routing is enabled
MessageId Queue message ID Added by message-specific activities

You can also attach custom tags via IAdditionalMessageData.TraceTags when sending a message. These tags are propagated to all downstream activities for that message.

Creating Child Spans

Inside a consumer handler, IWorkerNotification exposes the queue's ActivitySource via the Tracer property. Use it to create child spans that are automatically parented to the queue's current activity:

consumer.Start<MyMessage>((message, notification) =>
{
    using var activity = notification.Tracer.StartActivity("ProcessOrder");
    activity?.SetTag("orderId", message.Body.OrderId);
    // your processing logic
}, notifications);

These child spans appear in your tracing backend as children of the MessageHandler or MessageHandlerAsync span.

Related Pages

Clone this wiki locally