Serilog + AutoFac

1,589 views
Skip to first unread message

Simon Fenton

unread,
Aug 31, 2014, 10:38:36 PM8/31/14
to ser...@googlegroups.com
This could be more of an AutoFac question than it is a Serilog question so please tell me if you think I should move this question elswhere.

I've managed to get my desired behaviour out of AutoFac and Serilog together but it feels a bit clunky to me and I'm wondering if anyone can suggest improvements/alternatives. The behaviour I want is the correct container scope for the underlying logger configuration (which I believe is supposed to be singleton), and then ILoggers injected with the SourceContext set to the name of the type that requested the ILogger. I've done this before with Ninject and Log4Net but I've been a bit surprised at how much code it took me to get Autofac doing this.

The relevant code snippets are as follows:

Autofac config:
            builder.Register(c => SerilogConfig.CreateLogger())
                   .As<ILogger>()
                   .SingleInstance();

            builder.RegisterModule<SerilogLoggingModule>();


SerilogLoggingModule:
    public class SerilogLoggingModule : Module
    {
        private static void AddILoggerToParameters(object sender, PreparingEventArgs e)
        {
            var t = e.Component.Activator.LimitType;
            e.Parameters = e.Parameters.Union(
                new[]
                {
                    new ResolvedParameter((p, i) => p.ParameterType == typeof(ILogger), (p, i) => i.Resolve<ILogger>().ForContext(t)),
                });
        }

        protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
        {
            registration.Preparing += AddILoggerToParameters;
        }
    }


SerilogConfig:
    public class SerilogConfig
    {
        public static ILogger CreateLogger()
        {
            var config = new LoggerConfiguration().
                MinimumLevel.Debug().
                WriteTo.ColoredConsole(LogEventLevel.Debug).
                WriteTo.Sink(new RollingFileSink("log-debug-{Date}.json", new JsonFormatter(renderMessage: true), null, null), LogEventLevel.Debug).
                WriteTo.RollingFile("log-debug-{Date}.log", LogEventLevel.Debug).
                WriteTo.Sink(new RollingFileSink("log-{Date}.json", new JsonFormatter(renderMessage: true), null, null), LogEventLevel.Warning);

            InitialiseGlobalContext(config);

            return config.CreateLogger();
        }

        public static LoggerConfiguration InitialiseGlobalContext(LoggerConfiguration configuration)
        {
            return configuration.Enrich.WithMachineName()
                                .Enrich.WithProperty("ApplicationName", typeof(SerilogConfig).Assembly.GetName().Name)
                                .Enrich.WithProperty("UserName", Environment.UserName)
                                .Enrich.WithProperty("AppDomain", AppDomain.CurrentDomain)
                                .Enrich.WithProperty("RuntimeVersion", Environment.Version)
                                // this ensures that calls to LogContext.PushProperty will cause the logger to be enriched
                                .Enrich.FromLogContext();
        }
    }


Nicholas Blumhardt

unread,
Sep 1, 2014, 2:08:31 AM9/1/14
to Simon Fenton, ser...@googlegroups.com
Hey Simon!

Yeah, this probably is a better one for Autofac, since the awkward code is really that second code snippet that integrates the two libraries, rather than the individual bits of Serilog or Autofac config.

For what it's worth, I've written code like that second snippet (the module) many, many times - having a one-liner baked into Autofac like:

builder.RegisterByTargetType((c, p, t) => t.Resolve<ILogger>().ForContext(t)).As<ILogger>();

would certainly save some repetition.

But, the API would still be awkward even with this example, so in the end most users will probably have to Google it, in which case copying in an example like yours above is probably a similar amount of effort.

Perhaps publishing either a Serilog.Extras.Autofac, or Autofac.Integration.Serilog package would be a better solution in the long run? Open to hosting it in the Serilog repo, but might equally well belong with Autofac if there are any similar integration packages already being published there (this example does log4net, including properties: https://code.google.com/p/autofac/wiki/Log4NetIntegration).

Cheers!
Nick


--
You received this message because you are subscribed to the Google Groups "Serilog" group.
To unsubscribe from this group and stop receiving emails from it, send an email to serilog+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages