The Serilog documentation and online examples all use log properties inside a using statement like this:
using (LogContext.PushProperty("Username", _username))
{
_logger.LogInformation("Hello!");
}
In my specific case I have a service class LoggedInUserService which will live for longer than "saying hello" (has other methods inside) and needs to prefix all log messages with the username. I'm running my setup in an embedded environment, and the username has nothing to do with HttpContext etc, as it is not doing any http requests or so... Up until now I just added this functionality by manually adding the username, but this looks a bit cumbersome after a while:
_logger.LogInformation("{username}: Hello!", _username);
I've created an example project (you can copy and run it locally) to demonstrate my project setup. The current log output is this:
[INF] : The program started.
[INF] Joe: Hello!
[INF] Joe: Hello!
[INF] Joe: The program ended.
The desired log output should be:
[INF] The program started.
[INF] Frank: Hello!
[INF] Joe: Hello!
[INF] The program ended.
And here is the demo project:
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate:
"[{Level:u3}] {Username}: {Message}{NewLine:1}{Exception:1}")
.CreateLogger();
var host = Host.CreateDefaultBuilder()
.UseSerilog()
.ConfigureServices((_, services) =>
{
services.AddSingleton<LoggedInUserServiceFactory>();
})
.Build();
var factory = host.Services.GetRequiredService<LoggedInUserServiceFactory>();
Log.Logger.Information("The program started.");
var loggedInUser1 = factory.Create("Frank");
var loggedInUser2 = factory.Create("Joe");
loggedInUser1.SayHello();
loggedInUser2.SayHello();
Log.Logger.Information("The program ended.");
}
}
internal class LoggedInUserServiceFactory
{
private readonly IServiceProvider _serviceProvider;
public LoggedInUserServiceFactory(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public LoggedInUserService Create(string username)
{
return new LoggedInUserService(
_serviceProvider.GetRequiredService<ILogger<LoggedInUserService>>(),
username);
}
}
internal class LoggedInUserService
{
private readonly ILogger<LoggedInUserService> _logger;
private readonly string _username;
public LoggedInUserService(
ILogger<LoggedInUserService> logger,
string username)
{
_logger = logger;
_username = username;
// How to push log properties on class level??
LogContext.PushProperty("Username", _username);
}
public void SayHello()
{
_logger.LogInformation("Hello!");
}
}