Основы логирования в ASP.NET Core. Создание собственного провайдера логирования

Несмотря на то, что ASP.NET Core предоставляет самые широкие возможности логирования событий в приложении, все же может потребоваться каким-либо доработать предложенную систему и разработать собственный провайдер логирования ASP.NET Core.

Для создания конфигурации нам потребуется разработать три компонента:

  • Класс, реализующий интерфейс ILogger
  • Класс, реализующий интерфейс ILoggerProvider — собственно, сам провайдер
  • Класс, который добавляет метод расширения к объекту ILoggingBuilder

Допусти, нам необходимо разработать провайдер, который будет сохранять лог json-файл.

Класс, реализующий интерфейс ILogger

Класс логгера должен реализовать интерфейс ILogger:

public class JsonFileLogger : ILogger, IDisposable
 {
     object _lock = new object();

     string _filePath;

     public JsonFileLogger(string filePath) 
     {
         _filePath = filePath;
     } 
     public IDisposable? BeginScope<TState>(TState state) where TState : notnull
     {
         return this;
     }

     public void Dispose() { }

     public bool IsEnabled(LogLevel logLevel)
     {
         return true;
     }

     public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
     {
         var jsonLine = JsonSerializer.Serialize(new
         {
             logLevel,
             eventId,
             parameters = (state as IEnumerable<KeyValuePair<string, object>>)?.ToDictionary(i => i.Key, i => i.Value),
             message = formatter(state, exception),
             exception = exception?.GetType().Name
         });

         lock (_lock)
         {
             File.AppendAllText(_filePath, jsonLine + Environment.NewLine);
         }
     }
 }

У ILogger определены следующие методы:

Log()

void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);

метод предназначен для выполнения логгирования. Он принимает пять параметров:

  • LogLevelуровень детализации текущего сообщения
  • EventId — идентификатор события
  • TState — объект, который хранит сообщение
  • Exception — информация об исключении
  • formatter — функция форматирования, которая с помощью TState и Exception позволяет получить текстовое сообщение для логирования

В этом методе мы формируем строку в формате Json, используя стандартный сериализатор .NET в который передаем анонимный тип, содержащий все необходимые сведения:

var jsonLine = JsonSerializer.Serialize(new
{
    logLevel,
    eventId,
    parameters = (state as IEnumerable<KeyValuePair<string, object>>)?.ToDictionary(i => i.Key, i => i.Value),
    message = formatter(state, exception),
    exception = exception?.GetType().Name
});

И далее, полученную строку записываем в файл

lock (_lock)
{
    File.AppendAllText(_filePath, jsonLine + Environment.NewLine);
}

В результате, на каждое сообщение в логе будет формироваться отдельный JSON-объект.

BeginScope()

IDisposable? BeginScope<TState>(TState state) where TState : notnull;

этот метод возвращает объект, реализующий IDisposable завершающий некоторую область логической операции при удалении. В данном случае нам этот метод не важен, поэтому возвращаем значение this — ссылку на текущий объект.

IsEnabled()

bool IsEnabled(LogLevel logLevel);

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

Класс, реализующий интерфейс ILoggerProvider

Теперь напишем, собственно, провайдер

public class JsonFileLoggerProvider : ILoggerProvider
 {
     string path;
     public JsonFileLoggerProvider(string path)
     {
         this.path = path;
     }
     public ILogger CreateLogger(string categoryName)
     {
         return new JsonFileLogger(path);
     }
     public void Dispose() { }
 }

В конструкторе мы принимаем путь к файлу лога, который затем передаем в конструктор логгера. У ILoggerProvider имеется всего два метода:

public ILogger CreateLogger(string categoryName)

создает и возвращает объект логгера

public void Dispose()

Управляет освобождением ресурсов. В нашем случае этого не требуется, поэтому тело метода пустое.

Метод расширения для ILoggingBuilder

Для создания метода расширения добавим в приложение следующий класс:

public static class JsonFileLoggerExtensions
{
    public static ILoggingBuilder AddJsonFile(this ILoggingBuilder builder, string filePath)
    {
        builder.AddProvider(new JsonFileLoggerProvider(filePath));
        return builder;
    }
}

метод расширения AddJsonFile() принимает путь к файлу лога. Полученный путь, в дальнейшем, передается в конструктор провайдера. В самом методе, используя метод AddProvider() мы добавляем новый провайдер логирования в приложение.

Тестирование собственного провайдера логирования

Напишем следующий код приложения ASP.NET Core:

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Logging.AddJsonFile("log.json"); //добавляем новый логгер

        var app = builder.Build();

        app.MapGet("/", (ILogger<Program> logger) => 
        {
            logger.LogInformation(MyEvents.TestEvent, "Тестирование идентификатора событий");
            logger.LogInformation(MyEvents.LoadMainPage, "Загрузили главную страницу");
            return "Hello World!";
        });

        app.Run();
    }
}

Теперь запустим приложение и после его закрытия увидим результат работы нового логгера в обозревателе решений Visual Studio:

Если открыть этот файл, то можно увидеть следующий вывод:

{"logLevel":2,"eventId":{"Id":14,"Name":"ListeningOnAddress"},"parameters":{"address":"https://localhost:7201","{OriginalFormat}":"Now listening on: {address}"},"message":"Now listening on: https://localhost:7201","exception":null}
{"logLevel":2,"eventId":{"Id":14,"Name":"ListeningOnAddress"},"parameters":{"address":"http://localhost:5209","{OriginalFormat}":"Now listening on: {address}"},"message":"Now listening on: http://localhost:5209","exception":null}
{"logLevel":2,"eventId":{"Id":0,"Name":null},"parameters":{"{OriginalFormat}":"Application started. Press Ctrl\u002BC to shut down."},"message":"Application started. Press Ctrl\u002BC to shut down.","exception":null}
{"logLevel":2,"eventId":{"Id":0,"Name":null},"parameters":{"EnvName":"Development","{OriginalFormat}":"Hosting environment: {EnvName}"},"message":"Hosting environment: Development","exception":null}
{"logLevel":2,"eventId":{"Id":0,"Name":null},"parameters":{"ContentRoot":"D:\\YandexDisk\\CSharp Sources\\ASP.NET Core Book\\MtSite\\AspLogTest","{OriginalFormat}":"Content root path: {ContentRoot}"},"message":"Content root path: D:\\YandexDisk\\CSharp Sources\\ASP.NET Core Book\\MtSite\\AspLogTest","exception":null}
{"logLevel":2,"eventId":{"Id":10002,"Name":null},"parameters":{"{OriginalFormat}":"\u0422\u0435\u0441\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u0439"},"message":"\u0422\u0435\u0441\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u0430 \u0441\u043E\u0431\u044B\u0442\u0438\u0439","exception":null}
{"logLevel":2,"eventId":{"Id":10001,"Name":null},"parameters":{"{OriginalFormat}":"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u043B\u0438 \u0433\u043B\u0430\u0432\u043D\u0443\u044E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443"},"message":"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u043B\u0438 \u0433\u043B\u0430\u0432\u043D\u0443\u044E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443","exception":null}

Итого

Создание своего провайдера логирования позволяет сохранять логи приложения в самых различных форматах, добавлять свою логику разрешения/запрета логирования и т.д.. Для разработки своей системы логирования в ASP.NET Core нам необходимо разработать три компонента: класс, реализующий интерфейс ILogger; класс, реализующий интерфейс ILoggerProvider и класс, который добавляет метод расширения к объекту ILoggingBuilder.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии