StarRezDevTeam / Serilog.Sinks.AzureWebJobsTraceWriter

Serilog Azure WebJobs/Functions TraceWriter Sink
MIT License
19 stars 4 forks source link

Initialisation for use in WebJobs #1

Closed tjrobinson closed 7 years ago

tjrobinson commented 7 years ago

What's the best way of initialising the Serilog Logger using this sink for a WebJob?

In the README you suggest the following:

ILogger log = new LoggerConfiguration()
    .WriteTo.TraceWriter(traceWriter)
    .CreateLogger();

But the only place I can put this code (to have TraceWriter available) is in the function itself, which gets called every time it's triggered.

I'd like to initialise Serilog once, on startup and put the initialisation code in Program.cs but TraceWriter isn't available at this point.

Any ideas? Perhaps I am just missing something.

ScottHolden commented 7 years ago

Hey,

This is a tricky one. As you have mentioned, the TraceWriter is only available upon invocation of the of the function.

One pattern we have been using internally is to build up a "baseline" logger within startup, then use a factory (if using Dependency Injection/IoC) or static helper to add context when available.

For example, using the 'static helper' route:

Within Program.cs, setup Log.Logger to be your baseline config, eg, shipping warnings to another sink:

// Program.cs
...
Log.Logger = new LoggerConfiguration().WriteTo.Trace(LogEventLevel.Warning).CreateLogger();
...

The you can now add the new sink when available:

// LogHelper.cs
public ILogger LoggerWithTraceWriter(TraceWriter traceWriter)
{
    return new LoggerConfiguration()
        .WriteTo.Logger(Log.Logger)
        .WriteTo.TraceWriter(traceWriter)
        .CreateLogger();
}

Then you can simply pass it along to the next service:

public static void ServiceBusJob([QueueTrigger("input")] string input, TraceWriter log)
{
    ILogger myLogger = LoggerWithTraceWriter(log);
    SomeOtherService service = new SomeOtherService(myLogger);

    service.DoWork();
}

TLDR; As TraceWriter is only available upon invocation of the function, you can't hook it up before hand. You can setup a global logger, for example Trace or Console, but these won't be scoped. Personally my recommendation is to use a mix of a global logger, and a function invocation scoped logger.

Let me know if this has answered your question :)

tjrobinson commented 7 years ago

Thanks @ScottHolden that does help a bit - mostly just reassurance that I'm not missing something obvious. We've gone with an approach similar to the one you suggested. There's a static helper, LoggingSetup (a factory basically) which sets up the logger and has a different overload depending on whether TraceWriter is available or not.

    public static class LoggingSetup
    {
        public static void Setup()
        {
            var loggerConfiguration = CreateLoggerConfiguration();

            var logger = loggerConfiguration.CreateLogger();

            Log.Logger = logger;
        }

        public static void Setup(TraceWriter traceWriter)
        {
            var loggerConfiguration = CreateLoggerConfiguration()
                .WriteTo.TraceWriter(traceWriter);

            var logger = loggerConfiguration.CreateLogger();

            Log.Logger = logger;
        }

        private static LoggerConfiguration CreateLoggerConfiguration()
        {
            var environmentName = CloudConfigurationManager.GetSetting("Environment") ?? "Local";
            var seqPath = CloudConfigurationManager.GetSetting("SeqPath") ?? "http://localhost:5341/";
            var seqApiKey = CloudConfigurationManager.GetSetting("SeqApiKeySmsWebjob") ?? "";
            var levelSwitch = new LoggingLevelSwitch();

            var loggerConfiguration = new LoggerConfiguration()
                .WriteTo.Seq(seqPath, apiKey: seqApiKey)
                .WriteTo.LiterateConsole()
                .MinimumLevel.ControlledBy(levelSwitch)
                .Enrich.WithProperty("Environment", environmentName)
                .Enrich.WithProperty("Application", "Empactis.NotificationService.Sms");

            return loggerConfiguration;
        }
    }
    class Program
    {
        private static void Main()
        {
            LoggingSetup.Setup();
        public async Task ProcessQueueMessage([QueueTrigger("smsqueue")] string message, TraceWriter log)
        {
            LoggingSetup.Setup(log);