13

Serilog allows creating a context-aware logger:

Log.ForContext<T>()

I would like to register Serilog with SimpleInjector in such a way that T is the type of the consumer, i.e. it is which class it is injected in to.

e.g.

public class Car
{
    public Car(ILogger logger) <= would be injected using Log.ForContext<Car>()
    {             
    }
}

I can see this has been done with AutoFac.

And looking through the SimpleInjector documentation, there is a very promising overload of RegisterConditional() (with the Func<TypeFactoryContext, Type> parameter).

c.RegisterConditional(typeof (ILogger),
    x => Log.ForContext(x.Consumer.ImplementationType), <= won't compile as expecting a Type
    Lifestyle.Scoped,
    x => true);

however, I don't want to tell SimpleInjector which Type to build, but how to build one.

janhartmann
  • 14,713
  • 15
  • 82
  • 138
jameskind
  • 1,097
  • 2
  • 13
  • 28
  • 3
    We explicitly left out the possibility to register a delegate with the `RegisterConditional` method, because that would allow making runtime decisions during object graph creation, and it disallows Simple Injector from doing deep analysis on your object graphs. So that trick here is to wrap the `Log.ForContext` call inside a self-defined generic class, which is exactly what @janhartmann advices. Whether or not you hide Serilog behind your own abstraction is up to you, but I do absolutely advice doing this. So for me, Jan's answer is *the* correct answer. – Steven Jun 04 '16 at 08:59
  • 2
    Also make sure that you're not logging too much: https://stackoverflow.com/a/9915056/264697 – Steven Jun 04 '16 at 12:12

1 Answers1

11

I have integrated Serilog with Simple Injector with the following code based on @Steven genius answer on StackOverflow: logger wrapper best practice

public interface ILogger
{
    void Log(LogEntry entry);
}

public class SerilogLogger<T> : ILogger
{
    private readonly Serilog.ILogger _logger;

    public SerilogLogger()
    {
        _logger = new LoggerConfiguration()
            .WriteTo
            .Trace(LogEventLevel.Information)
            .CreateLogger()
            .ForContext(typeof (T));
    }

    public void Log(LogEntry entry)
    {
        /* Logging abstraction handling */
    }
}

public static class ContainerExtensions {

    public static void RegisterLogging(this Container container)
    {
        container.RegisterConditional(
            typeof(ILogger),
            c => typeof(SerilogLogger<>).MakeGenericType(c.Consumer.ImplementationType),
            Lifestyle.Singleton,
            c => true);
    }

}

In your Composition Root:

var container = new Container();
container.RegisterLogging();
Community
  • 1
  • 1
janhartmann
  • 14,713
  • 15
  • 82
  • 138
  • 3
    Aren't `Serilog.ILogger` implementations thread-safe? I would suggest making the conditional registration `Singleton` if possible. – Steven Jun 04 '16 at 09:02
  • 2
    You are absolutely right, @Steven - obvisously a mistake. Editing answer now. – janhartmann Jun 04 '16 at 11:55
  • @janhartmann I have discovered there are times when c.Consumer could be null (I believe through Service Locator anti pattern) and have amended my line like so: `c => typeof(SerilogLogger <>).MakeGenericType(c.Consumer?.ImplementationType ?? typeof(object))`. What do you think? – jameskind Oct 16 '16 at 21:18
  • I think it would be prettier to add another registeration below the one as a fallback, something like: `container.RegisterConditional(typeof(ILogger<>), typeof(NullLogger<>), context => !context.Handled);` See: http://simpleinjector.readthedocs.io/en/latest/advanced.html#registration-of-open-generic-types //cc @Steven if you have another approach? – janhartmann Oct 17 '16 at 05:45
  • 5
    @jameskind eemove the null check. If it makes no sense that anyone requests a logger directly from the container (which unlikely does) just let it break. Don't cover up bad practices; expose them! – Steven Oct 17 '16 at 06:21
  • @Steven What if you register an inferface collection that has implementations that need the logger? – Mike Flynn May 04 '18 at 13:19
  • The Serilog.ILogger interface has about 100 members declared, so isn't the SerlogLogger implementation missing an enormous amount of required implementation? – Neutrino Sep 11 '18 at 15:25
  • I have a solution with different projects. I'm defining the ILogger abstraction and its Serilog implementation in my "Common" project which all other projects references. I'm using SimpleInjector in the projects and I'd like to have different SerilogConfigs for the different projects. With this code, configuration is tied to the class and not to the app using it. Ideally, this should be defined in Startup class of each project. How can I define each SerilogConfig in this file for each project? – empz Nov 22 '18 at 20:02