.NET by Patrik

Auto-Generate Logging Scopes in .NET Background Services

When building background services in .NET, it’s helpful to include structured logging for better traceability and diagnostics. One common pattern is using logging scopes to include context like the service or task name in each log entry.

Instead of manually providing this context everywhere, you can simplify the process by automatically extracting it based on the class and namespace — making the code cleaner and easier to maintain.


✅ Goal

Replace this verbose pattern:

_logger.BeginScope(LoggingScopeHelper.CreateScope("FeatureName", "WorkerName"));

With a simple, reusable version:

_logger.BeginWorkerScope();

Implementation

🔧 1. Logger Extension

public static class LoggerExtensions
{
    public static IDisposable BeginWorkerScope(this ILogger logger)
    {
        var scopeData = LoggingScopeHelper.CreateScope();
        return logger.BeginScope(scopeData);
    }
}

🧠 2. Logging Scope Helper

public static class LoggingScopeHelper
{
    public static Dictionary<string, object> CreateScope()
    {
        var stackTrace = new System.Diagnostics.StackTrace();
        var frames = stackTrace.GetFrames();

        string featureName = "Unknown";
        string workerName = "Unknown";

        foreach (var frame in frames ?? Array.Empty<StackFrame>())
        {
            var method = frame.GetMethod();
            var type = method?.DeclaringType;

            if (type == null || type.Name.StartsWith("<") || type.Namespace?.StartsWith("System") == true)
                continue;

            workerName = type.Name;

            var ns = type.Namespace;
            var segments = ns?.Split('.');
            if (segments?.Length >= 3)
            {
                featureName = segments[2]; // Assuming format: Company.App.Feature.Workers
            }

            break;
        }

        return new Dictionary<string, object>
        {
            ["Feature"] = featureName,
            ["Worker"] = workerName
        };
    }
}

Example Usage in a Worker

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    using (_logger.BeginWorkerScope())
    {
        // Your background task logic
    }
}

This ensures that every log written within the scope will automatically include "Feature" and "Worker" values, without manually specifying them.

logging
dotnet
diagnostics
background
clean-code

Comments