C# Serilog: как вести журнал с интерполяцией строк и сохранять имена аргументов в шаблонах сообщений?

Как заменить этот код:

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. Вы всегда знаете, что это имя свойства напечатает в вашем журнале

1 ответ

Решение

Добавьте этот файл в свой проект. Она имеетILogger методы расширения VerboseInterpolated(), DebugInterpolated()и так далее. Здесь также есть модульные тесты.

Использование со строкой формата

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, поэтому значение аргумента не будет записано. Это связано сpropertyNameForLoggerэто формат для значения.

Использование с анонимными типами

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.Loggingnamespace, похоже, что у Serilog его нет. Но я не эксперт в этом и открыт для предложений.

Другие вопросы по тегам