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 ответ
Добавьте этот файл в свой проект. Она имеет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.Logging
namespace, похоже, что у Serilog его нет. Но я не эксперт в этом и открыт для предложений.