Reputation: 1509
I'm using the new HostApplicationBuilder
in .NET. I want to use IOptionsMonitor
to monitor a section of appsettings.json
for option changes, and apply those changes to Serilog LoggingLevelSwitch.MinimumLevel
for such an object that's created during host build (using builder.Services.AddSerilog
).
Something like this:
builder.Services.Configure<LoggingOptions>(
builder.Configuration.GetSection(key: "Logging"));
LoggingLevelSwitch theSwitch = new LoggingLevelSwitch(); // Later added to a Serilog sink.
builder.Services.AddSingleton<Something>((serviceProvider) =>
{
var monitor = serviceProvider.GetRequiredService<IOptionsMonitor<LoggingOptions>>();
monitor.OnChange((options) => {
theSwitch.MinimumLevel = options.MinimumLevel;
});
}
But what do I return from the AddSingleton
call? I don't really want to add a service, but just add the OnChange
handler to the singleton for IOptionsMonitor<LoggingOptions>
.
Without a call such as builder.Services.AddXXX
, how can I get an IServiceProvider
instance that I can use to obtain a reference to the IOptionsMonitor<LoggingOptions>
singleton?
This is supposed to be a helper in a utility package that we can use in multiple different apps to simplify and unify app code and reduce boilerplate code in apps, to allow apps based on .NET hosting to have logging set up and allow the logging level to be modified at runtime.
It would be sufficient to have the IOptionsMonitor
to be hooked up to the LoggingLevelSwitch
when the app worker starts and removed when the app worker terminates. But I would like to avoid a requirement on the app author to remember to make extra calls in the worker's start/stop code. So, preferably be able to set this up in the utility package's code that sets up the logging in general.
Upvotes: 0
Views: 367
Reputation: 1509
OK, so the solution was kind-of staring me in the face right from the start.
// Apparently this is the new way of binding options, but same semantics as in my question.
builder.Services.AddOptions<LoggingOptions>().BindConfiguration("Logging");
builder.Services.AddSerilog((services, loggerConfiguration) =>
{
// Create the switch, which will later be applied to a logging sink.
var levelSwitch = new LoggingLevelSwitch();
// Here inside AddSerilog we do have an IServiceProvider,
// so we can request the IOptionsMonitor<LoggingOptions> singleton.
// I assume that this is the point where it gets instantiated by the DI container.
var monitor = services.GetRequiredService<IOptionsMonitor<LoggingOptions>>();
// Here we hook the monitor up to the LoggingLevelSwitch.
monitor.OnChange(options => levelSwitch.MinimumLevel = options.MinimumLevel);
// Finally, we can add the sink using the switch,
// which will be modified by the monitor at runtime.
loggerConfiguration
.WriteTo.ApplicationInsights(
services.GetRequiredService<TelemetryConfiguration>(),
TelemetryConverter.Traces,
levelSwitch: levelSwitch);
});
I've tried it in a small console app and it works fine. When I change the logging level in appsettings.json
, the sink starts receiving log events according to the new setting, without any code within the actual worker service.
Upvotes: 3
Reputation: 1454
I correct myself. I was wrong, you can achieve this with singleton.
I was able to make it work. I just returned the LoggingLevelSwitch as singleton I was able to se changes in MinimumLevel when appsetting value is changed.
Upvotes: 0