Руководство по ведению журнала для авторов библиотек .NET
Как автор библиотеки, предоставление ведения журнала — отличный способ предоставить потребителям представление о внутренней работе библиотеки. Это руководство помогает предоставлять ведение журнала таким образом, чтобы он соответствовал другим библиотекам и платформам .NET. Это также помогает избежать распространенных узких мест производительности, которые могут не быть очевидными в противном случае.
Когда следует использовать ILoggerFactory
интерфейс
При написании библиотеки, которая выдает журналы, требуется ILogger объект для записи журналов. Чтобы получить этот объект, API может принять ILogger<TCategoryName> параметр или принять ILoggerFactory вызов.ILoggerFactory.CreateLogger Какой подход следует предпочтительнее?
Если вам нужен объект ведения журнала, который можно передать вместе с несколькими классами, чтобы все из них могли выдавать журналы, используйте
ILoggerFactory
. Рекомендуется, чтобы каждый класс создает журналы с отдельной категорией, которая называется той же, что и класс. Для этого необходимо, чтобы фабрика создавала уникальныеILogger<TCategoryName>
объекты для каждого класса, который выдает журналы. Распространенные примеры включают API общедоступной точки входа для библиотеки или общедоступных конструкторов типов, которые могут создавать вспомогательные классы внутри системы.Если вам нужен объект ведения журнала, который используется только в одном классе и никогда не предоставляет общий доступ, используйте
ILogger<TCategoryName>
TCategoryName
тип, который создает журналы. Типичным примером этого является конструктор для класса, созданного путем внедрения зависимостей.
Если вы разрабатываете общедоступный API, который должен оставаться стабильным с течением времени, помните, что в будущем может потребоваться рефакторинг внутренней реализации. Даже если класс изначально не создает внутренние вспомогательные типы, которые могут измениться по мере развития кода. Использование позволяет создавать новые объекты для любых новых ILogger<TCategoryName>
классов, не изменяя ILoggerFactory
общедоступный API.
Дополнительные сведения см. в статье о применении правил фильтрации.
Предпочитать ведение журнала с исходным кодом
API ILogger
поддерживает два подхода к использованию API. Можно вызывать такие методы, как LoggerExtensions.LogError и LoggerExtensions.LogInformation, или использовать генератор источников ведения журнала для определения строго типизированных методов ведения журнала. В большинстве случаев генератор источника рекомендуется, так как он обеспечивает более высокую производительность и более высокую производительность. Он также изолирует такие проблемы, как шаблоны сообщений, идентификаторы и уровни журналов от вызывающего кода. Подход, отличный от источника, в первую очередь полезен для сценариев, когда вы готовы отказаться от этих преимуществ, чтобы сделать код более кратким.
using Microsoft.Extensions.Logging;
namespace Logging.LibraryAuthors;
internal static partial class LogMessages
{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}
Предыдущий код:
- Определяет именованный
partial class
LogMessages
объект,static
который позволяет определить методы расширения дляILogger
типа. - Декорирует
LogProductSaleDetails
метод расширения атрибутом иMessage
шаблономLoggerMessage
. LogProductSaleDetails
Объявляет , который расширяетILogger
и принимает иquantity
description
.
Совет
Во время отладки можно перейти в исходный код, так как он является частью той же сборки, что и код, вызывающий его.
Использование IsEnabled
, чтобы избежать дорогостоящих вычислений параметров
Могут возникнуть ситуации, когда оценка параметров является дорогой. Расширяя предыдущий пример, представьте description
, что параметр является string
дорогостоящим для вычислений. Возможно, продукт, который продается, получает понятное описание продукта и зависит от запроса базы данных или чтения из файла. В таких ситуациях можно указать генератору источника пропустить IsEnabled
охрану и вручную добавить IsEnabled
охранник на сайте вызова. Это позволяет пользователю определить, где вызывается охранник, и гарантирует, что параметры, которые могут быть дорогостоящими для вычислений, оцениваются только при необходимости. Рассмотрим следующий код:
using Microsoft.Extensions.Logging;
namespace Logging.LibraryAuthors;
internal static partial class LogMessages
{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information,
SkipEnabledCheck = true)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}
LogProductSaleDetails
При вызове метода расширения охранник вызывается вручную, IsEnabled
а оценка дорогостоящих параметров ограничена, если это необходимо. Рассмотрим следующий код:
if (_logger.IsEnabled(LogLevel.Information))
{
// Expensive parameter evaluation
var description = product.GetFriendlyProductDescription();
_logger.LogProductSaleDetails(
quantity,
description);
}
Дополнительные сведения см. в разделе "Создание источника журнала во время компиляции" и ведение журнала с высокой производительностью в .NET.
Избегайте интерполяции строк в журнале
Распространенная ошибка заключается в использовании интерполяции строк для создания сообщений журнала. Интерполяция строк в журнале проблематична для производительности, так как строка вычисляется, даже если соответствующая LogLevel
строка не включена. Вместо интерполяции строк используйте шаблон сообщения журнала, форматирование и список аргументов. Дополнительные сведения см. в разделе "Ведение журнала в .NET: шаблон сообщения журнала".
Использование ведения журнала без опов по умолчанию
Иногда при использовании библиотеки, которая предоставляет API ведения журнала, которые ожидают ILogger
ILoggerFactory
или не хотите предоставлять средство ведения журнала. В этих случаях пакет NuGet Microsoft.Extensions.Logging.Abstractions предоставляет значения по умолчанию для ведения журнала без опов.
Потребители библиотеки могут по умолчанию регистрировать значение NULL , если не ILoggerFactory
указано. Использование ведения журнала NULL отличается от определения типов как допускающих значение NULL (ILoggerFactory?
), так как типы не являются null. Эти удобные типы не регистрируют ничего и, по сути, нет опов. Рекомендуется использовать любой из доступных типов абстракции, в которых применимо: