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