serilog / serilog-extensions-logging

Serilog provider for Microsoft.Extensions.Logging

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Logger should have its own scope rather than a global wide scope on provider

quabug opened this issue · comments

consider something like this:

var foo = loggerFactory.CreateLogger("foo");
var bar = loggerFactory.CreateLogger("bar");
var fooScope = foo.BeginScope("A");
var barScope = bar.BeginScope("B");
foo.Log(...);
bar.Log(...);
barScope.Dispose();
fooScope.Dispose();

IMO, the scope of foo and bar should not effect each other.

The [serilog] scope is stored in the .NET ExecutionContext, which is Attached to the logical thread (and flows to child Tasks etc).

At the serilog level, you can make a contextual logger with properties attached using the .ForContext<>() overloads - this layers a set of properties into the logger by holding them in a wrapper/decorator object that you can then pass inwards.

I'd suggest asking this as a question with some more details on what you have tried and are seeking exactly (and/or whether its critical for you to stay using the Microsoft.Extensions.Logging APIs) on stackoverflow using a serilog tag as you'll get way more eyes on it there than here.

The [serilog] scope is stored in the .NET ExecutionContext, which is Attached to the logical thread (and flows to child Tasks etc).

At the serilog level, you can make a contextual logger with properties attached using the .ForContext<>() overloads - this layers a set of properties into the logger by holding them in a wrapper/decorator object that you can then pass inwards.

I'd suggest asking this as a question with some more details on what you have tried and are seeking exactly (and/or whether its critical for you to stay using the Microsoft.Extensions.Logging APIs) on stackoverflow using a serilog tag as you'll get way more eyes on it there than here.

Thanks for your suggestion.
Make BeginScope local to a logger or make it global is a design decision from my perspective.
And it is not about serilog itself, it is about how to design this BeginScope interface.
Yes, I can achieve this by ForContext method while creating a logger, but there's no way to call a custom ForContext from Microsoft.Extensions.Logging APIs, and it is practically a bad idea to create a new logger every time I need some "local" property on a logger.

I suspect it's not an either/or situation. MEL dictates a lot of how stuff works. I don't know all the details, but I can guarantee someone one SO will have a viable answer pretty quick.

and it is practically a bad idea to create a new logger every time I need some "local" property on a logger.

It's a pretty well-established pattern that's designed into Serilog. Yes, it allocates, but no, its not the same cost as making a 'new logger'.

So, in summary:

  • there are two orthogonal mechanisms in Serilog, that work as designed; I'd be surprised if your situation is so unique that you have discovered a major new logging need not yet known to developers on .NET
  • I am not certain how best to approach it given you are constrained by wanting to use MEL (or, I am guessing you are)
    So... I'm suggesting the best place to work all this out is on SO - literally 3 people have read this now, whereas the reach on SO would be much broader. I am not trying to tell you to go away or that you are wrong to ask this - I am attempting to help you to help yourself.

Actually I have a fork of this project which make a local scope as describe here.
So this post is more like a suggestion/discussion of design decision than a question or a help needed...

https://github.com/quabug/Serilog.Sinks.Unity3D/blob/cd8eff849cdbafd26a3d2fb82de7895ea6dec4a0/Serilog.Sinks.Unity3D/Assets/Serilog.Extensions.Logging/Extensions/Logging/SerilogLogger.cs#L53

Maybe the example is not clear, take this as another example:

class SomeClass
{
    private ILogger _logger;
    public SomeClass(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger("SomeClass"); // create a new logger with category name
        _logger.BeginScope(this); // seems like push a scope into "logger", but actually a global-wide provider instead.
    }
}

var foo = new SomeClass(loggerFactory);
var bar = new SomeClass(loggerFactory);
// now the scope for both foo and bar is:
//     parent: foo -> child: bar

I found it is very confusion and hard to maintain a global wide scope which affect each other when I have an independent logger instance.


update: Begin/End style example

class SomeClass
{
    private ILogger _logger;
    private IDisposable _scope;
    public SomeClass(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger("SomeClass");
    }

    public void BeginSometing()
    {
        _logger.BeginScope(this);
    }

    public void EndSomething()
    {
        _scope?.Dispose();
    }
}

var foo = new SomeClass(loggerFactory);
var bar = new SomeClass(loggerFactory);
foo.BeginSomething();
bar.BeginSomething();
// now the scope for both foo and bar is:
//     parent: foo -> child: bar