Содержание
Несмотря на то, что 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
.