Closed tjrobinson closed 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 :)
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);
What's the best way of initialising the Serilog Logger using this sink for a WebJob?
In the README you suggest the following:
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
butTraceWriter
isn't available at this point.Any ideas? Perhaps I am just missing something.