Поделиться через


Руководство по логированию для авторов библиотек .NET

Как автор библиотеки, обеспечение логирования — отличный способ предоставить потребителям представление о внутренних процессах вашей библиотеки. Это руководство помогает осуществлять логирование таким образом, чтобы оно соответствовало другим библиотекам и платформам .NET. Это также помогает избежать распространенных узких мест производительности.

Когда следует использовать ILoggerFactory интерфейс

При написании библиотеки, которая выдает журналы, требуется ILogger объект для записи журналов. Чтобы получить этот объект, ваше API может принять параметр ILogger<TCategoryName>, или оно может принять ILoggerFactory, после чего следует вызвать ILoggerFactory.CreateLogger. Какой подход следует предпочесть?

  • Если вам нужен объект ведения журнала, который можно передать вместе с несколькими классами, чтобы все из них могли выдавать журналы, используйте ILoggerFactory. Рекомендуется, чтобы каждый класс создает журналы с отдельной категорией, которая называется той же, что и класс. Для этого необходимо, чтобы фабрика создавала уникальные ILogger<TCategoryName> объекты для каждого класса, который генерирует логи. Распространенные примеры включают API общедоступной точки входа для библиотеки или общедоступных конструкторов типов, которые могут создавать вспомогательные классы внутри системы.
  • Если вам нужен объект ведения журнала, который используется только в одном классе и не делится, используйте ILogger<TCategoryName>, где TCategoryName — это тип, создающий логи. Типичным примером этого является конструктор для класса, созданного путем внедрения зависимостей.

Если вы разрабатываете общедоступный API, который должен оставаться стабильным с течением времени, помните, что в будущем может потребоваться рефакторинг внутренней реализации. Даже если класс изначально не создает внутренние вспомогательные типы, которые могут измениться по мере развития кода. Использование ILoggerFactory позволяет создавать новые объекты ILogger<TCategoryName> для любых новых классов, не изменяя публичный 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 атрибутом LoggerMessage и шаблоном Message.
  • Объявляет 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: шаблон сообщения журнала".

Использование ведения журнала no-op по умолчанию

Если вы используете библиотеку, которая предоставляет API логирования, ожидающие ILogger или ILoggerFactory, могут быть ситуации, когда вы не хотите предоставить средство логирования. В этих случаях пакет NuGet Microsoft.Extensions.Logging.Abstractions предоставляет "пустые" настройки ведения журнала по умолчанию.

Потребители библиотеки могут по умолчанию использовать пустой логгинг, если ILoggerFactory не указано. Использование нулевого лога отличается от определения типов как допускающих значение null (ILoggerFactory?), так как эти типы не допускают значение null. Эти удобные типы не регистрируют ничего и, по сути, нет опов. Рекомендуется использовать любой из доступных типов абстракции, в которых применимо: