我有一个 Serilog Enricher,有时会崩溃,因为它正在尝试执行一些可能无法将项目添加到日志条目中的操作。
当发生这种情况时,我当然可以忽略异常。但是,我想在日志中通知发生了这种情况,并从当前配置中删除 Enricher。
最初,我只是想在捕获异常时记录一些内容 - 但随后所有丰富器都对其进行了丰富,包括刚刚尝试记录某些内容的内容。因此,我们得到了一个永远不会解决的无限循环。
为了解决这个问题,我想从 LogContext 中删除 Enricher,以便使用工作丰富器来记录项目,然后不再担心它。
这是我的例子Enricher:
public class PotentiallyCrashingEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
try
{
var myString = "Something i want to log"
property = propertyFactory.CreateProperty("MyProperty", myString);
logEvent.AddPropertyIfAbsent(property);
throw new Exception("Oh no...");
}
catch (Exception e)
{
var oldContext = LogContext.Clone();
LogContext.Reset();
Log.ForContext(typeof(PotentiallyCrashingEnricher)).Information($"{e.Message}");
LogContext.Push(oldContext);
}
}
}
在 catch 块中,您可以看到我尝试的内容 - 但我认为这不应该按照我理解的方式工作。 有没有办法可以做到这一点?
LoggerConfiguration
是一个构建者,一旦我们调用了.CreateLogger()
,我们就无法真正做太多改变它。但不用担心,出行并非不可能。
选项 1:重建配置
这可能有点矫枉过正,而且非常不灵活,但它确实满足了您的评论:“我想从 LogContext 中删除 Enricher,以便使用工作丰富器来记录项目,然后不再担心它”
由于我们无法删除 Enricher,因此我们将在没有它的情况下重建整个配置。
void BuildLogger(bool enrichWithCrashingThing = true) {
var cfg = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
//...etc
;
if (enrichWithCrashingThing)
cfg.Enrich.With<PotentiallyCrashingEnricher>();
// (Re)create the logger
Log.Logger = cfg.CreateLogger();
}
// .. On app start
BuildLogger();
在您的 Enricher 中,捕获异常后,只需触发重建即可。
catch (Exception e) {
BuildLogger(false);
Log.ForContext(this.GetType()).Error(e, "Enricher has crashed");
}
这将从全局记录器中永远删除 Enricher。
选项 2:条件丰富
这可能是更好的选择。您所需要的只是一个全局标志,并告诉 Serilog 在丰富日志事件时检查它。
var cfg = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.When( // Conditional enrichment only when flagged as ok
evt => PotentiallyCrashingEnricher.IsOk,
enrich => enrich.With<PotentiallyCrashingEnricher>()
)
.WriteTo.Console()
//...etc
;
Log.Logger = cfg.CreateLogger();
还有带有静态标志的丰富器:
public class PotentiallyCrashingEnricher : ILogEventEnricher
{
public static bool IsOk { get; private set; } = true; // Start as OK
public void Enrich(LogEvent evt, ILogEventPropertyFactory factory) {
try {
//...
}
catch (Exception e) {
IsOk = false; // Not OK!
Log.ForContext(this.GetType()).Error(e, "Enricher has crashed");
// We can re-enable the enricher again, if desired:
// IsOk = true;
}
}
}
这样,Serilog 将在每次记录事件时检查该标志。
是的,它的效率比“重建后忘记”选项 1 非常轻微低(每个事件的
if
声明的成本极小)。但如果您从未将 IsOk 设置回 true,JIT 最终可能会对其进行优化。
您可以选择重新打开它。