我具有动态更改日志文件路径的功能。但是,当我更改 Consul 中可配置的路径时,它会在两个位置(即旧路径和新路径)写入部分日志。更改日志文件路径应该可以在不重新启动任何服务的情况下进行。我们如何存档?
我们在日志文件中写入如下:
.WriteTo.File(logFolderFullPath + "\\" + applicationName + "_.txt",
LogEventLevel.Error, shared: true,
fileSizeLimitBytes: fileSizeLimitBytes, rollOnFileSizeLimit: true, rollingInterval: RollingInterval.Day,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] [{MachineName}] [{SourceContext}] {RequestId} {CorrelationId} {Message}{NewLine}{Exception}{properties}")
Run Code Online (Sandbox Code Playgroud)
logFolderFullPath是可配置的路径appsetting.json。当我们更改路径时,它会在新路径中创建日志文件,但同时也会继续写入旧路径文件。
所以我们希望它应该停止写入旧路径。
Serilog FileSink 一旦设置路径就不允许修改。我仍然更喜欢使用 appsettings.json 来存储 serilog 配置,但我在使用它之前修改了配置。
我的 appsettings.json 如下所示:
...
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "../logs/log-.txt",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
"rollingInterval": "Day",
"buffered": true
}
}
]
...
Run Code Online (Sandbox Code Playgroud)
在配置 Serilog 之前,我创建了一个扩展方法来覆盖 appsettings.json 中的配置。
public static class IHostBuilderExtensions
{
public static IHostBuilder ConfigureSerilog(this IHostBuilder hostBuilder, string appName)
{
return hostBuilder.ConfigureAppConfiguration((hostCtx, configBuilder) =>
{
var config = configBuilder.Build();
var pid = Process.GetCurrentProcess().Id;
var logFilePath = $@"{MyLogFolder}\\{appName}_pid_{pid}_.txt";
var logFileNameWithPidPattern = $"{appName}_pid_{pid}_.txt";
const string serilogWriteConfigPattern = "Serilog:WriteTo:";
const string serilogFilePathConfigPattern = ":Args:path";
var serilogFoundKvpFilePathFromConfig = config
.AsEnumerable()
.FirstOrDefault(kvp =>
kvp.Key.Contains(serilogWriteConfigPattern, StringComparison.InvariantCultureIgnoreCase)
&& kvp.Key.Contains(serilogFilePathConfigPattern, StringComparison.InvariantCultureIgnoreCase))
;
var keyToReplace = serilogFoundKvpFilePathFromConfig.Key;
var overridenValue = serilogFoundKvpFilePathFromConfig.Value
.Replace("log-.txt", logFileNameWithPidPattern);
var serilogWriteToFilePathOverride = KeyValuePair.Create(keyToReplace, overridenValue);
configBuilder.AddInMemoryCollection(new[] { serilogWriteToFilePathOverride });
})
.UseSerilog((ctx, lc) =>
{
lc
// At this point, the config has been updated
// and the file name contains the Process Id:
// eg.: MyName_pid_15940_20220826.txt
.ReadFrom.Configuration(ctx.Configuration)
.WriteTo
.Console();
});
}
}
Run Code Online (Sandbox Code Playgroud)
我在 Program.cs 中使用它,如下所示:
...
hostBuilder
.ConfigureAppConfiguration((hostCtx, configBuilder) => { /* other config */ })
.ConfigureSerilog(appName)
...
Run Code Online (Sandbox Code Playgroud)
您可以尝试使用Serilog.Settings.Reloader,它可以在配置更改时在运行时交换记录器实例。
在运行时更改记录器属性的另一种常见方法是使用Serilog.Sinks.Map,这是一个根据日志事件的属性调度事件的接收器。
下面的示例使用调用的日志事件属性FileName来决定它将写入的日志文件的名称,因此每当此属性更改时,日志文件都会相应更改:
Log.Logger = new LoggerConfiguration()
.WriteTo.Map("FileName", "IDontKnow", (fileName, wt) => wt.File($"{fileName}.txt"))
.CreateLogger();
Log.ForContext("FileName", "Alice").Information("Hey!"); // writes to Alice.txt
Log.ForContext("FileName", "Bob").Information("Hello!"); // writes to Bob.txt
Log.Information("Hi Again!"); // writes to IDontKnow.txt (default if property is missing)
Log.CloseAndFlush();
Run Code Online (Sandbox Code Playgroud)
在您的情况下,您希望根据配置的更改动态更改此属性名称。一种简单的方法是创建一个自定义丰富器,它可以根据您的配置设置更改上述属性的值。
您的自定义丰富器看起来像这样:
internal class LogFilePathEnricher : ILogEventEnricher
{
private string _cachedLogFilePath;
private LogEventProperty _cachedLogFilePathProperty;
public const string LogFilePathPropertyName = "LogFilePath";
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var logFilePath = // Read path from your appsettings.json
// Check for null, etc...
LogEventProperty logFilePathProperty;
if (logFilePath.Equals(_cachedLogFilePath))
{
// Path hasn't changed, so let's use the cached property
logFilePathProperty = _cachedLogFilePathProperty;
}
else
{
// We've got a new path for the log. Let's create a new property
// and cache it for future log events to use
_cachedLogFilePath = logFilePath;
_cachedLogFilePathProperty = logFilePathProperty =
propertyFactory.CreateProperty(LogFilePathPropertyName, logFilePath);
}
logEvent.AddPropertyIfAbsent(logFilePathProperty);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:如果您使用选项模式,而不是每次写入日志消息时检查配置,上面的示例丰富器可能会更有效。
使用可以根据配置动态设置属性的丰富器LogFilePath,您只需将日志记录管道配置为基于该属性进行映射即可。
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.With<LogFileNameEnricher>()
.WriteTo.Map(LogFileNameEnricher.LogFilePathPropertyName,
(logFilePath, wt) => wt.File($"{logFilePath}"), sinkMapCountLimit: 1)
.CreateLogger();
// ...
Log.CloseAndFlush();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9565 次 |
| 最近记录: |