In a .NET web application, I'm using Autofac + Serilog + Microsoft.Extensions.Logging. I would like to use a custom enricher to log the current user ID from a proprietary connection object, like this:
public class ConnectionUserIdEnricher : ILogEventEnricher
{
private readonly FooConnection _fooConnection;
public InnovatorUserIdEnricher(FooConnection _fooConnection)
{
_fooConnection = fooConnection;
}
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
LogEventProperty userIdProperty = propertyFactory.CreateProperty("FooConnectionUserId", _fooConnection.UserId);
logEvent.AddPropertyIfAbsent(userIdProperty);
}
}
The connection would be correctly injected using Autofac. I have found this question and this article, which both explain exactly what I'd like to do. However, my container configuration looks different, namely like this:
public class FooContainer
{
public static Lazy<IContainer> Container = new Lazy<IContainer>(BuildContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static LoggerFactory CreateLoggerFactory(IOptions<SeqLogOptions> seqLogOptions)
{
LoggerConfiguration loggerConfigurator = new LoggerConfiguration().MinimumLevel.Debug()
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithThreadId()
.Enrich.WithProcessId()
.WriteTo.Seq(seqLogOptions.Value.SeqUrl, apiKey: seqLogOptions.Value.SeqApiKey);
Log.Logger = loggerConfigurator.CreateLogger();
var loggerFactory = new LoggerFactory(new[] {new SerilogLoggerProvider(Log.Logger)});
return loggerFactory;
}
public static IContainer BuildContainer(ILoggerFactory loggerFactory)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterAssemblyTypes(typeof(FooRepository<>).Assembly);
// [...]
containerBuilder.RegisterInstance(loggerFactory).As<ILoggerFactory>();
containerBuilder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>));
IContainer container = containerBuilder.Build();
return container;
}
}
So since I don't use the UseSerilog
method, I don't have the services parameter available.
Is there a way to make this work anyway, without changing the whole container setup?
Ideally, this would also work if the FooConnection
service/component can't be resolved (either not enriching the user ID or using a NULL value).
Edit: The answer to this question about using a middleware seems very useful, but we are not using ASP.NET Core. We use the container directly from a classical .NET code (could be either a .NET web application or a console application) like this:
FooConnection fooConnection = ThirdPartyLibrary.GetCurrentConnection();
using (ILifetimeScope lifetimeScope = FooContainer.Container.Value.BeginLifetimeScope(x => x.RegisterInstance(fooConnection)))
{
var component = lifetimeScope.Resolve<ISomeService>>();
// ...
}
Maybe it would make more sense to hook into the registration of the FooConnection, update the LogContext whenever a new FooConnection component is registered? I would like to keep the container, uhm, contained, so that code that uses the container does not have to worry about setting up the log context.