Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Last active June 4, 2025 11:55
Show Gist options
  • Save ramonsmits/65eeced32548c58fe5d3a15f968a980c to your computer and use it in GitHub Desktop.
Save ramonsmits/65eeced32548c58fe5d3a15f968a980c to your computer and use it in GitHub Desktop.
NServiceBus - Create a logger scope for every incoming message so each log entry can be correlated to a incomming message processing attempt
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NServiceBus;
using NServiceBus.Pipeline;
[Description("Create a logger scope for every incoming message with incoming message processing attemps meta data so this is logged with every log statement")]
class LoggerScopeBehavior :
Behavior<IIncomingPhysicalMessageContext>
{
// Behaviors are created once, per instance. UUID v7 are time sortable.
readonly Guid instanceId = Guid.CreateVersion7();
// 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>>();
// 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);
await next();
}
}
var builder = Host.CreateApplicationBuilder(args);
// ...
// Ensure that the scopes are included in the logging
builder.Logging.AddSimpleConsole(o => { o.IncludeScopes = true; });
// For OpenTelemetry this might need to be set separately
builder.Logging.AddOpenTelemetry(logging =>
{
// ...
logging.IncludeScopes = true;
});
var endpointConfiguration = new EndpointConfiguration("EndpointX");
// ...
// Add the behavior to the pipeline configuration
endpointConfiguration.Pipeline.Register(
new LoggerScopeBehavior(),
typeof(LoggerScopeBehavior).GetCustomAttribute<DescriptionAttribute>()?.Description
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment