Поради

Ознайомтесь з найкращими практиками використання OpenTelemetry .NET для логів

Дотримуйтесь цих рекомендацій, щоб отримати максимальну віддачу від OpenTelemetry .NET для журналів.

Logging API

ILogger

.NET підтримує високопродуктивне структуроване логування за допомогою інтерфейсу Microsoft.Extensions. Logging.ILogger (включаючи ILogger<TCategoryName>), що допомагає контролювати поведінку застосунків і діагностувати проблеми.

Версія пакунка

Використовуйте інтерфейс ILogger (включаючи ILogger<TCategoryName>) з останньою стабільною версією пакунка Microsoft.Extensions.Logging, незалежно від версії середовища виконання .NET, що використовується:

  • Якщо ви використовуєте останню стабільну версію OpenTelemetry .NET SDK, вам не потрібно турбуватися про версію пакунка Microsoft.Extensions.Logging, оскільки це вже враховано за вас через залежність пакунка.
  • Починаючи з версії 3.1.0, команда .NET runtime дотримується високих стандартів зворотної сумісності для Microsoft.Extensions.Logging, навіть під час великих оновлень версії, тому сумісність тут не є проблемою.

Отримання Logger

Щоб використовувати інтерфейс ILogger, спочатку потрібно отримати логер. Як отримати логер, залежить від двох речей:

  • Типу програми, яку ви створюєте.
  • Місця, де ви хочете вести журнал.

Як правило:

Використовуйте розділені крапками UpperCamelCase як назву категорії журналу, що робить зручним фільтрацію журналів. Загальною практикою є використання повної кваліфікованої назви класу, а якщо потрібна подальша категоризація, додайте назву підкатегорії. Ознайомтеся з офіційним документом .NET для отримання додаткової інформації. Наприклад:

loggerFactory.CreateLogger<MyClass>(); // це еквівалентно CreateLogger("MyProduct.MyLibrary.MyClass")
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass"); // використовуйте повну кваліфіковану назву класу
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass.DatabaseOperations"); // додайте назву підкатегорії
loggerFactory.CreateLogger("MyProduct.MyLibrary.MyClass.FileOperations"); // додайте ще одну назву підкатегорії

Уникайте надто частого створення логерів. Хоча логери не є надто дорогими, вони все ж вимагають витрат процесорного часу та памʼяті і призначені для повторного використання в усьому застосунку.

Запис повідомлень в журнал

Використовуйте структуроване ведення журналу.

  • Структуроване ведення журналу є більш ефективним, ніж неструктуроване.
    • Фільтрація та редагування можуть відбуватися в окремих парах ключ-значення, а не у всьому повідомленні журналу.
    • Зберігання та індексація є більш ефективними.
  • Структуроване ведення журналу спрощує управління та використання журналів.

Наприклад:

var food = "tomato";
var price = 2.99;

logger.LogInformation("Hello from {food} {price}.", food, price);

Уникайте інтерполяції рядків. Наприклад:

var food = "tomato";
var price = 2.99;

logger.LogInformation($"Hello from {food} {price}.");

Використовуйте шаблон генерації джерела журналу під час компіляції для досягнення найкращої продуктивності. Наприклад:

var food = "tomato";
var price = 2.99;

logger.SayHello(food, price);

internal static partial class LoggerExtensions
{
    [LoggerMessage(Level = LogLevel.Information, Message = "Hello from {food} {price}.")]
    public static partial void SayHello(this ILogger logger, string food, double price);
}

Використовуйте LogPropertiesAttribute з Microsoft.Extensions.Telemetry.Abstractions, якщо вам потрібно записати складні обʼєкти. Ознайомтеся з розділом Логування з використанням складних обʼєктів для отримання додаткової інформації.

Уникайте методів розширення з LoggerExtensions, ці методи не оптимізовані для продуктивності. Наприклад:

var food = "tomato";
var price = 2.99;

logger.LogInformation("Hello from {food} {price}.", food, price);

Дотримуйтесь високих стандартів, використовуючи ILogger.IsEnabled.

API журналювання є високо оптимізованим для сценарію, в якому більшість логерів відключені для певних рівнів журналювання. Додатковий виклик IsEnabled перед журналюванням не дасть вам жодного приросту продуктивності. Наприклад:

var food = "tomato";
var price = 2.99;

if (logger.IsEnabled(LogLevel.Information)) // не робіть цього, це не дасть жодного приросту продуктивності
{
    logger.SayHello(food, price);
}

internal static partial class LoggerExtensions
{
    [LoggerMessage(Level = LogLevel.Information, Message = "Hello from {food} {price}.")]
    public static partial void SayHello(this ILogger logger, string food, double price);
}

IsEnabled може дати переваги в продуктивності, коли оцінка аргументів є дорогою. Наприклад, у наступному коді виклик Database.GetFoodPrice буде пропущений, якщо логер не ввімкнено:

if (logger.IsEnabled(LogLevel.Information))
{
    logger.SayHello(food, Database.GetFoodPrice(food));
}

Хоча IsEnabled може дати деякі переваги в продуктивності у вищезазначеному сценарії, для більшості користувачів це може спричинити більше проблем. Наприклад, продуктивність коду тепер залежить від того, який логер увімкнено, не кажучи вже про те, що оцінка аргументів може мати значні побічні ефекти, які тепер залежать від конфігурації логування.

Використовуйте спеціальний параметр для запису виключень при використанні генератора виходу на етапі компіляції. Наприклад:

var food = "tomato";
var price = 2.99;

try
{
    // Execute some logic

    logger.SayHello(food, price);
}
catch (Exception ex)
{
    logger.SayHelloFailure(ex, food, price);
}

internal static partial class LoggerExtensions
{
    [LoggerMessage(Level = LogLevel.Information, Message = "Hello from {food} {price}.")]
    public static partial void SayHello(this ILogger logger, string food, double price);

    [LoggerMessage(Level = LogLevel.Error, Message = "Could not say hello from {food} {price}.")]
    public static partial void SayHelloFailure(this ILogger logger, Exception exception, string food, double price);
}

Вам слід використовувати спеціальні перевантаження для запису виключень при використанні методів розширення журналювання.

var food = "tomato";
var price = 2.99;

try
{
    // Execute some logic

    logger.LogInformation("Hello from {food} {price}.", food, price);
}
catch (Exception ex)
{
    logger.LogError(ex, "Could not say hello from {food} {price}.", food, price);
}

Уникайте додавання деталей помилок у шаблон повідомлення. Наприклад:

Вам слід використовувати правильні API Exception, оскільки специфікація OpenTelemetry визначає спеціальні атрибути для деталей Exception. Наступні приклади показують, що НЕ слід робити. У цих випадках деталі не будуть втрачені, але спеціалізовані атрибути також не будуть додані.

var food = "tomato";
var price = 2.99;

try
{
    // Execute some logic

    logger.SayHello(food, price);
}
catch (Exception ex)
{
    logger.SayHelloFailure(food, price, ex.Message);
}

internal static partial class LoggerExtensions
{
    [LoggerMessage(Level = LogLevel.Information, Message = "Hello from {food} {price}.")]
    public static partial void SayHello(this ILogger logger, string food, double price);

    // ПОГАНО — Помилка не повинна бути частиною шаблону повідомлення. Використовуйте спеціальний параметр.
    [LoggerMessage(Level = LogLevel.Error, Message = "Could not say hello from {food} {price} {message}.")]
    public static partial void SayHelloFailure(this ILogger logger, string food, double price, string message);
}
var food = "tomato";
var price = 2.99;

try
{
    // Execute some logic

    logger.LogInformation("Hello from {food} {price}.", food, price);
}
catch (Exception ex)
{
    // ПОГАНО — Помилка не повинна бути частиною шаблону повідомлення. Використовуйте спеціальний параметр.
    logger.LogError("Could not say hello from {food} {price} {message}.", food, price, ex.Message);
}

LoggerFactory

У багатьох випадках ви можете використовувати ILogger без необхідності взаємодіяти безпосередньо з Microsoft.Extensions.Logging.LoggerFactory. Цей розділ призначений для користувачів, яким потрібно явно створювати та керувати LoggerFactory.

Уникайте надто частого створення екземплярів LoggerFactory, оскільки LoggerFactory є досить дорогим і призначений для повторного використання в усій програмі. Для більшості програм достатньо одного екземпляра LoggerFactory на процес.

Керуйте життєвим циклом екземплярів LoggerFactory, якщо ви їх створили.

  • Якщо ви забудете звільнити екземпляр LoggerFactory перед завершенням програми, журнали можуть бути втрачені через відсутність належного скидання.
  • Якщо ви звільните екземпляр LoggerFactory занадто рано, будь-який подальший виклик API журналювання, повʼязаний з фабрикою журналів, може стати бездіяльним (тобто жоден журнал не буде виведений).

Кореляція логів

В OpenTelemetry журнали автоматично корелюються з трейсами. Щоб дізнатися більше, перегляньте підручник Кореляція журналів.

Фільтрація логів

Для більш просунутого фільтрування та вибірки команда .NET має план охопити це в рамках .NET 9. Використовуйте це runtime issue для відстеження прогресу або надання відгуків і пропозицій.

Цензурування логів

Журнали можуть містити чутливу інформацію, таку як паролі та номери кредитних карток, тому необхідно провести належне цензурування, щоб запобігти інцидентам з конфіденційністю та безпекою. Перегляньте посібник з Цензурування логів, щоб дізнатися більше.


Востаннє змінено December 26, 2024: [uk] Ukrainian documentation for OpenTelemetry (2a3c5648)