Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Last active January 21, 2026 15:20
Show Gist options
  • Select an option

  • Save ramonsmits/f5cfb5a95aa12ad899ecfab81e2e93c0 to your computer and use it in GitHub Desktop.

Select an option

Save ramonsmits/f5cfb5a95aa12ad899ecfab81e2e93c0 to your computer and use it in GitHub Desktop.
NServiceBus behavior that uses Microsoft.Extensions.Logging to create a logger scope containing incoming message identifier and processing identifier
//
// 1. Register the type in the host builder:
//
// builder.Services.AddSingleton<LoggerScopeBehavior>();
//
// 2. Register the behavior in NServiceBus:
//
// endpointConfiguration.Pipeline.Register<LoggerScopeBehavior>(b => b.GetRequiredService<LoggerScopeBehavior>(), nameof(LoggerScopeBehavior));
//
// DOES NOT USE THE NSERVICEBUS LOGGING ABSTRACTION AS THIS DOES NOT SUPPORT `.BeginScope`
//
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NServiceBus.Pipeline;
sealed class LoggerScopeBehavior(ILogger<LoggerScopeBehavior> logger) :
Behavior<IIncomingPhysicalMessageContext>
{
// Behaviors are created once, per instance. UUID v7 are time sortable.
#if NET9_0_OR_GREATER
readonly Guid instanceId = Guid.CreateVersion7();
#else
readonly Guid instanceId = Guid.NewGuid();
#endif
// Instance global count of total incoming message count
long incomingCount;
public override async Task Invoke(IIncomingPhysicalMessageContext context, Func<Task> next)
{
var incomingNr = Interlocked.Increment(ref incomingCount);
var logger = context.Builder.GetRequiredService<ILogger<LoggerScopeBehavior>>();
var nativeId = context.Message.NativeMessageId;
context.MessageHeaders.TryGetValue(Headers.EnclosedMessageTypes, out var enclosedMessageTypes);
// Use message ID from headers and fallback to native message id if header does not exist
if (!context.MessageHeaders.TryGetValue(Headers.MessageId, out var messageId))
{
messageId = context.MessageId;
}
// Below FIRST creates a single processing reference that is passed to the log template
var processingReference = $"{instanceId}/{incomingNr}";
// DO NOT merge above in log template as the reference an composite key
// Alternatively can be swapped with a UUID, ideally a v7 for sorting/filtering on time
using var x = logger.BeginScope("RX {ProcessingRef} {MessageId}", processingReference, messageId);
try
{
logger.LogDebug("Processing {NativeMessageId}, {MessageId}, {EnclosedMessageTypes}", nativeId, messageId, enclosedMessageTypes);
await next();
logger.LogDebug("Completed {NativeMessageId}, {MessageId}, {EnclosedMessageTypes}", nativeId, messageId, enclosedMessageTypes);
}
catch
{
logger.LogError("Failed {NativeMessageId}, {MessageId}, {EnclosedMessageTypes}", nativeId, messageId, enclosedMessageTypes);
throw;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment