C# Serilog:如何使用字符串插值进行记录并在消息模板中保留参数名称?

C# Serilog: how to log with String interpolation and keep argument names in message templates?

如何替换此代码:

string name = "John";
logger.Information("length of name '{name}' is {nameLength}", name, name.Length);

像这样或类似的 C# 字符串插值

string name = "John";
// :-( lost benefit of structured logging: property names not passed to logger
logger.Information($"length of name '{name}' is {name.Length}");

但保留 属性 名称以使结构化日志记录正常工作?

好处是:

  1. 提高可读性
  2. 您永远不会忘记参数列表中的参数或消息模板中的 属性 名称,尤其是当您更改日志代码时
  3. 您总是知道这个 属性 名称将打印到您的日志中的内容

Add this file to your project. It has ILogger extension methods VerboseInterpolated(), DebugInterpolated() and so on. There are also unit tests here

格式字符串的用法

string name = "John";
// add 'Interpolated' to method name: InformationInterpolated() instead of Information()
// add name of the property after the expression. Name is passed to the logger
logger.InformationInterpolated($"length of name '{name:name}' is {name.Length:Length}");

但要小心:很容易用错方法。如果您不小心使用了 Serilog 的方法,例如 logger.Debug($"length = {length:propertyNameForLogger}"),它将记录 length = propertyNameForLogger,因此 没有参数值 将被记录。这是因为 propertyNameForLoggerformat 的值。

匿名类型的用法

string name = "John";
// add 'Interpolated' to method name: InformationInterpolated() instead of Information()
// create an anonymous object with 'new { propertyName }'. 
// It's much slower because of using Reflection, but allows to write the variable name only once. 
logger.InformationInterpolated($"length of name '{new { name }}' is {new { name.Length }}");
// you can also specify other property names
logger.InformationInterpolated($"length of name '{new { userName = name }}' is {new { lengthOfName = name.Length }}");

谢谢你的好主意。我看到了一个缺点,那就是在与 DI 一起使用时不支持通用记录器。我已经扩展了@Artemious 解决方案。如果这不是正确的方法,请告诉我。

public static void LogInformationInterpolated<T>(this ILogger<T> logger, FormattableString? message) =>
            WriteInterpolated<T>(logger, null, message, Information);

public static void LogInformationInterpolated<T>(this ILogger<T> logger, Exception? ex, FormattableString? message) =>
            WriteInterpolated<T>(logger, ex, message, Information);

public static void WriteInterpolated<T>(this ILogger<T> logger, Exception? ex, FormattableString? message, Serilog.Events.LogEventLevel logEventLevel)
{
    var contextedLogger = Log.ForContext<T>();
    WriteInterpolated(contextedLogger, ex, message, logEventLevel);
}

ILogger 来自Microsoft.Extensions.Logging 命名空间,Serilog 好像没有。但我不是这方面的专家,因此愿意接受建议。