Подключаем Kibana для хранения логов ASP.NET Core
Логи — полезные инструмент для отладки и мониторинга приложений. Только просмотр текстовых логов — дело скучное и неинтересное. Поэтому изучают логи в исключительных ситуациях. Здесь на помощь приходят структурированные логи — когда записи хранятся не просто в виде текста, а в виде структурированных данных с разделением информации на отдельные поля. По таким логам можно настроить фильтры, поиск, при соблюдении определенных условий — отправлять уведомления и т.д.
Инфраструктура логгирования ASP.NET Core поддерживает структурированные логи. Посмотрим как это можно использовать на примере ELK.
Elasticsearch и Kibana
Один из вариантов хранения структурированных логов — хранение их в Elasticsearch. Преимущество этого подхода заключается в том, что для этого всё уже готово — вам не нужно допиливать код для хранения данных.
ELK — это стек из трех продуктов: Elasticsearch, Logstash и Kibana:
- Elasticsearch — хранилище данных логов, позволяет искать по большим объемам данных с приемлемой скоростью.
- Kibana — удобный веб-интерфейс к хранилищу данных.
- Logstash — агрегатор, который забирает логи с жесткого диска и отправляет их в Logstash.
Чтобы попробовать ELK можно использовать Docker-образ sebp/elk:
docker pull sebp/elk
docker run -p 5601:5601 -p 9200:9200 -p 5044:5044 -it --name elk sebp/elk
Подробнее о том, как запустить ELK внутри Docker можно почитать в документации.
После запуска Kibana будет доступна через порт 5601, а Elasticsearch API — через 9200.
Для реального проекта Elasticsearch весьма требователен к оборудванию (в частности - к RAM). Поэтому хорошим вариантом может быть использование ELK как сервиса. К счастью, таких сервисов очень много, например — раз, два, три.
Отправка в данных в Elasticsearch
Если для отправки данных используется Logstash, то всё сводится к записи логов в файл и настройки Logstash для чтения этих файлов и отправки в Elasticsearch. Альтернативным способом может быть отправка данных напрямую в Elasticsearch, минуя Logstash. Рассмотрим этот способ.
Стоит отметить, что в данном случае нам потребуется какая-нибудь библиотека для работы с Elasticsearch. Желательно также, чтобы эта библиотека была интегрирована с инфраструктурой логгирования ASP.NET Core. С учетом этого, Serilog может быть хорошим выбором: она интегрирована с ASP.NET Core и имеет провайдер для отправки логов в Elastisearch.
Установим в проект пакет Serilog, а заодно Serilog.Extensions.Logging и Serilog.Sinks.Elasticsearch.
Теперь необходимо добавить Serilog-логгер в DI-контейнер:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Serilog.Core.Logger>(container => new LoggerConfiguration()
.ConfigureEnrichers(container)
.ConfigureElk("http://elk:9200", Configuration.GetSection("Logging:Elk"), container)
.ConfigureSelfLog(container)
.CreateLogger());
}
И в список логгинг-провайдеров:
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddSerilog(services.GetService<Serilog.Core.Logger>());
}
}
Обратите внимание, что при конфигурировании ELK указана конфигурационная секция Logging:Elk. В ней мы указываем параметры фильтров, о которых я писал ранее.
Для данного примера конфигурационный файл может выглядеть так:
{
"Logging": {
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"Elk": {
"IncludeScopes": false,
"LogLevel": {
"MyApp.Services": "Information",
"MyApp.Infrastructure": "Information"
}
}
}
}
Теперь можно воспользоваться стандартным API для логгирования и создавать записи в в ELK:
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogError("Some error occured");
return View();
}
}
Однако, гораздо интереснее в таком случае писать в лог структурированную информацию. Для этого немного изменим способ логгирования:
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
var request = ...;
var config = ...;
_logger.LogError("Some error occured.{User}{@Request}{@Config}",
User.Identity.Name, request, config);
return View();
}
}
Теперь данные отправляются в ELK в структурированном виде.
Если для каждой записи в лог требуется сохранять какие-то стандартные данные (например, данные HTTP-запроса, окружения и т.д.) можно воспользоваться специальными объектами Serilog — Enricher‘ами. О том, как их использовать можно почитать в документации.
Добавить комментарий