'Reload Serilog JSON Configuration on changes in .NET Core 2.1
I'm currently working on a ASP.NET Core 2.1 application and I use Serilog for logging. I want to reload the application settings file for my Serilog implementation during runtime.
My goal is to change the log level at runtime e.g. I write into minimumLevel Debug instead of Information and I save the file. I want to trigger a live reload of the settings. My appsettings.json
looks like this:
{
"serilog": {
"using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console" ],
"minimumLevel": "Information",
"writeTo": [
{
"name": "File",
"args": {
"fileSizeLimitBytes": 256000000,
"retainedFileCountLimit": 62,
"rollingInterval": "Day",
"rollOnFileSizeLimit": true,
},
{
"name": "Console",
}
]
}
}
In my Program.cs I load the settings with the flag reloadOnChange: true
.
public class Program
{
public static readonly ServiceSettings Settings = new ServiceSettings();
public static void Main(string[] args)
{
//...
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.GetFullPath(CoreServiceBase.Instance.ConfigFilePath), optional: false, reloadOnChange: true)
.AddCommandLine(args)
.Build();
config.Bind(Settings);
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
.UseConfiguration(config);
}
}
My Startup looks like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Custom application logging
ApplicationLogging.LoggerFactory = loggerFactory;
// ...
}
}
Do you know how to reload the Serilog configuration during runtime, if I somehow the appsettings.json gets changed. => Live reload for the appsettings.json.
Thank you!!
Solution 1:[1]
You can change the loglevel using LoggingLevelSwitch
. You can read about it here
You can use the IOptionsSnapshot<>
interface to reload the configuration. You can read more about that here
Solution 2:[2]
You can use Serilog.Settings.Reloader
I don't have ASP example, but on console program you can do this:
// Service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddLogging(loggingBuilder =>
loggingBuilder
.AddSerilog(SwitchableLogger.Instance, true)
.AddSerilogConfigurationLoader(configuration, SwitchableLogger.Instance)
);
// Services
using (var services = serviceCollection.BuildServiceProvider())
{
// Create logger
Microsoft.Extensions.Logging.ILogger logger = services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
// Write
logger.LogInformation("Hello World");
// Modify config
config.Set("Serilog:WriteTo:0:Args:OutputTemplate", "[{SourceContext}] {Message:lj}{NewLine}{Exception}");
configuration.Reload();
// Write with the previous logger instance, but with different settings
logger.LogInformation("Hello world again");
}
For clarification, the singleton "SwitchableLogger.Instance" is only for demonstration, a new instance "new SwitchableLogger()" can also be created.
Solution 3:[3]
The current Serilog implementation (2.9.0) is such that it is unable to fully reload settings. To work around this issue without introducing additional dependencies, avoid creating static loggers and follow the example provided here: https://github.com/akovac35/Logging/blob/v1.0.4/src/com.github.akovac35.Logging.Serilog/SerilogHelper.cs
public static void CreateLogger()
{
CreateLogger(configure =>
{
configure.AddJsonFile("serilog.json", optional: false, reloadOnChange: true);
});
}
public static void CreateLogger(Action<IConfigurationBuilder> configure)
{
if (configure == null) throw new ArgumentNullException(nameof(configure));
UpdateLogger(configure);
}
public static void UpdateLogger(Action<IConfigurationBuilder> configure)
{
if (configure == null) throw new ArgumentNullException(nameof(configure));
// The following pattern fires the reload token only once per settings change
var configurationBuilder = new ConfigurationBuilder();
try
{
configure(configurationBuilder);
IConfiguration configuration = configurationBuilder.Build();
// Release previous callback - will be released only if this line is reached, allowing for another reload
_changeCallback?.Dispose();
_changeCallback = null;
// .NET will not trigger a reload for invalid config file format, so reaching this line signals Json is OK
_changeCallback = configuration.GetReloadToken().RegisterChangeCallback(state =>
{
UpdateLogger(configure);
}, null);
// Reading configuration will fail for invalid properties, that is why reload registration must happen
// before this line or subsequent file updates may not be detected
global::Serilog.ILogger newLogger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.CreateLogger();
Log.Logger = newLogger;
GetLogger().Here(l => l.LogInformation("Updated logger: {@configuration}", configuration));
}
catch (Exception ex)
{
GetLogger().Here(l => l.LogError(ex, ex.Message));
}
}
Or you can simply use the Logging.Serilog library which provides utility functions for just that.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 |