1

I have the need to add information to the log scopes in .NET and they need to survive longer than a single method call...

Usualy the samples always tell us to use log scopes like this

  public void DoSomething()
  {
    using(Logger.BeginScope("Instance id {Guid}", strGuid) 
    {
      Logger.LogInformation("did something");
    }
  }

This would need to wrap every public accessible with the begin scope call....

public class SampleServivce : IDisposable
{
  public readonly ILogger<SampleService> Logger;
  private IDisposable _logScope;

  public SampleService(ILogger<SampleService> logger) 
  {
     Logger = logger;
     _logScope = Logger.BeginScope("Instance id {Guid}", Guid.NewGuid().ToString();
  }

  public void DoSomething()
  {
    Logger.LogInformation("did something");
  }

  public void Dispose() 
  {
    logScope?.Dispose();
  }

}

The second idea would be to create the scope in the constructor and dispose it in the dispose method. And this is where the

This post gives greater explanation why this would not work and yes, I can confirm this won't work because I am currently troubleshooting "wrong log scope info".

What our setup extends is the layer we have multiple instances of background services waiting for and doing tasks up on demand. Unfortunatly some scoped services required us to create dedicated service scopes per instance of background workers and requesting the services on demand...

So we do ...

public class BackgroundWorker1 : BackgroundService 
{

    private string _workerInstance = Guid.NewGuid().ToString();
    
    private IDisposable _logScope;
    private IServiceScope _serviceScope;
    
    public readonly IServiceScopeFactory ServiceScopeFactory;
    public readonly ILogger<BackgroundWorker1> Logger;
    
    private IServiceProvider _serviceProvider;
    
    public BackgroundWorker1(IServiceScopeFactory serviceScopeFactory) 
    {
       ServiceScopeFactory = serviceScopeFactory;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) 
    {
        _serviceScope = ServiceScopeFactory.CreateScope();
        _serviceProvider = _serviceScope.ServiceProvider;

        Logger = _serviceProvider.GetRequiredService<ILogger<BackgroundWorker1>>();
        _logScope = Logger.BeginScope("worker instance id {WorkerId}", _workerInstance);
    }
    
    public void OneOfManyExternallyCalledMethods() 
    {
        var service = _serviceProvider.GetRequiredService<Some3rdPartyService>();
        service.DoSomething();
        // all logs of all (incl. 3rd party/Microsoft) libs should contain the guid of the worker instance
    }
    
    public void Dispose() 
    {
      _serviceScope?.Dispose();
      _logScope?.Dispose();
    }
}

The ultimate goal is to have each worker instance id in each log .... and in some services the services (we can rewrite) the serivce instance id from the second sample ....

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
monty
  • 7,888
  • 16
  • 63
  • 100
  • _"This post gives greater explanation why this would not work and yes, I can confirm this won't work because I am currently troubleshooting "wrong log scope info"."_ - it depends on how do you use the class, for me exactly the same approach work brilliantly due to classes being restricted to scoped instances. – Guru Stron Nov 03 '22 at 10:09
  • Why your background worker has methods which are called externally? Have you considered using queue approach? Also from my personal experience I would recommend to create a service scope per "iteration" of worker instead of single service scope per the whole worker lifetime. – Guru Stron Nov 03 '22 at 10:12
  • In our setup we have services will be used only in some instances. Investigating the log files revealed that those services "sometimes" show wrong worker instances. So, yeah, it seemed to work but with issues I need to figure out the origin. – monty Nov 03 '22 at 10:16
  • I think I understand the problem but I would say that a [mre] is required for a definite answer. – Guru Stron Nov 03 '22 at 10:19
  • "Why your background worker has methods which are called externally?" It was a simplified example. The real example spins up an instance of a background worker for each dynamically loaded module. (Our internal/propiatory plug in mechanism). These plugins define their own scope but have the ability to hook up into the surrounding server. Depending on the implementation may or may not hook up into the middleware pipeline, register, provide and consume services, provide API controllers, signalR hubs or long running singleton services with a lot of timers. Ugly but necesary due to requirements – monty Nov 03 '22 at 10:20
  • _"This would need to wrap every public accessible with the begin scope call...."_ - based on the description you gave that would be the easiest approach. – Guru Stron Nov 03 '22 at 10:29

0 Answers0