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


Руководство по ведению журнала для авторов библиотек .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 classLogMessagesобъект, static который позволяет определить методы расширения для ILogger типа.
  • Декорирует LogProductSaleDetails метод расширения атрибутом и Message шаблономLoggerMessage.
  • LogProductSaleDetailsОбъявляет , который расширяет ILogger и принимает и quantitydescription.

Совет

Во время отладки можно перейти в исходный код, так как он является частью той же сборки, что и код, вызывающий его.

Использование 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 ведения журнала, которые ожидают ILoggerILoggerFactoryили не хотите предоставлять средство ведения журнала. В этих случаях пакет NuGet Microsoft.Extensions.Logging.Abstractions предоставляет значения по умолчанию для ведения журнала без опов.

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