Вообще, эта статья — по сути краткий обзор современных инструментов логирования, которые часто используются в.NET. Но из‑за того, что настройка ELK — относительно трудоемкий процесс, весь фокус по итогу сместился в сторону использования сего стека. Я просто смирился с этим. Надеюсь, мой опыт сэкономит время и силы читателю, желающему ознакомиться с ELK.

Что такое и зачем нужно?
ELK — это довольно популярный стек для сбора логов, основанный на базе данных Elasticsearch. Приблизительная схема работы ELK такова: есть три сервиса (сам Elasticsearch, Logstash и Kibana) и есть ваше приложение.
-
Ваше приложение отправляет свои логи в Logstash
-
Logstash их сохраняет в Elasticsearch
-
Через Kibana пользователь может посмотреть сохраненные логи
Почему так сложно, и не проще ли grep’ать данные из файликов? Да, проще. Но не когда у тебя куча сервисов, ещё и от разных команд. Тут понадобится мощный инструмент для хранения и поиска (он же — Elasticsearch) и желательно удобный интерфейс (привет, Kibana).
Мониторинг в ELK
На самом деле Elastic предоставляет множество различных решений для сбора данных, которые так или иначе могут помочь в troubleshooting’е. Одно из них — сервис Application Performance Monitoring (APM). Он осуществляет сбор телеметрии — метрик, логов и трассировок. Это позволяет разработчикам не только отловить нежелательное поведение, но и полностью проследить «путь» поломавшегося запроса, даже если он проходит через брокер сообщений типа RabbitMQ. В общем, APM — инструмент впечатляющий и безумно полезный в SOA‑подходах.
APM использует протокол OpenTelemetry для получения данных из приложения. Это значит, что ваш код, написанный для отправки телеметрии в APM, также сгодится и для Prometheus или.NET Aspire. По крайней мере в теории, ибо на практике приходется скачивать дополнительные Nuget‑пакеты для совместимости.
Немного об аналогах
Loki-стек
Раз речь зашла не только о ELK, хотелось бы кратенько затронуть тему его конкурентов. Наверное, один из самых известных аналогов — Loki‑стек. Он представляет собой связку Promtail + Loki + Grafana. К сожалению, у меня нет опыта его использования. Но мне доводилось пользоваться связкой Grafana + Prometheus. Это стек для сбора телеметрии. Из плюсов могу отметить, что он потребляет значительно меньше ресурсов, чем решение от Elastic.
.NET Aspire
Говоря об инструментах логирования и мониторинга в.NET, нельзя не упомянуть.NET Aspire (а вернее отдельный его компонент — Dashboard). Он является dev‑time решением, которое осуществляет сбор логов и телеметрии. Его очень легко развернуть (достаточно запустить докер‑образ) и он не поедает много ресурсов. В общем, очень рекомендую к ознакомлению.
Написание кода
Подключить отправку логов в ELK в C# очень легко. Достаточно добавить nuget-пакет Elastic.Extensions.Logging, после чего будет доступен специальный метод расширения для отправки данных в Logstash:
// По-умолчанию присоединится к http://localhost:5044 builder.Logging.AddElasticsearch();
Для мониторинга нужно установить пакеты Elastic.OpenTelemetry и OpenTelemetry.Instrumentation.AspNetCore
builder.Services .AddOpenTelemetry() .WithTracing(opt => opt .AddOtlpExporter(x => x.Endpoint = new Uri("http://localhost:8200")) .WithElasticDefaults() .AddSource("MassTransit") // если используете MassTransit .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .ConfigureResource(resource => resource.AddService("MicroFun.A")) ).WithLogging(opt => opt .AddOtlpExporter(x => x.Endpoint = new Uri("http://localhost:8200")) .ConfigureResource(resource => resource.AddService("MicroFun.A")) .WithElasticDefaults() ).WithMetrics(opt => opt .AddOtlpExporter(x => x.Endpoint = new Uri("http://localhost:8200")) .ConfigureResource(resource => resource.AddService("MicroFun.A")) .WithElasticDefaults() );
Этого в принципе достаточно, чтобы собирать телеметрию. Если нужно собрать метрики по специфичному участку кода, то можно добавить в трейсинг новый источник и обернуть этот участок в Activity
builder.Services.AddOpenTelemetry().WithTracing(opt => opt.AddSource("MicroFun.B") // ... ); // ... в некотором участке кода: using var source = new System.Diagnostics.ActivitySource("MicroFun.B"); using var activity = source.StartActivity("CreatingProduct"); // ...
Установка ELK
Установка ELK может занять некоторые время, т.к. потребуется сконфигурировать каждый сервис. В примере я буду использовать docker-compose
Elasticsearch
В первую очередь запускаем сам Elasticsearch
services: my-elasticsearch: image: elasticsearch:9.0.2 environment: ES_JAVA_OPTS: -Xms2g -Xmx2g # Elasticsearch очень любит поедать оперативку cluster.name: "docker-cluster" network.host: 0.0.0.0 discovery.type: single-node node.name: my-elasticsearch xpack.security.enabled: false xpack.security.audit.enabled: false ports: - 9200:9200
и генерим токен для Кибаны
docker exec -it dd6add1b6b82 sh ./bin/elasticsearch-service-tokens create elastic/kibana my-token # SERVICE_TOKEN elastic/kibana/my-token = AAEAAWVsYXN0aWMva2liE... <--- это наш токен
Такая сложность только с Elasticsearch. Остальные сервисы можно будет спокойно запустить с уже готовым docker-compose
Logstash
Теперь можно сконфигурировать остальные сервисы. Для Logtash пишем простой конфиг, который собирает логи по TCP и передает их в Elasticsearch
input { tcp { port => 5044 type => "logs" } } output { elasticsearch { hosts => ["my-elasticsearch:9200"] index => "%{[@metadata][beat]}-%{+YYYY.MM.dd}" } }
services: my-elasticsearch: // ... my-logstash: image: logstash:9.0.2 volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf ports: - 5044:5044 depends_on: - my-elasticsearch
Kibana
Для Kibana не забываем добавить сгенерированный токен в конфиг (ей богу, не знаю, как это автоматизировать)
server.host: "0.0.0.0" server.shutdownTimeout: "5s" elasticsearch.hosts: [ "http://my-elasticsearch:9200" ] elasticsearch.serviceAccountToken: AAEAAWVsYXN0aWMva2liYW5h...
services: my-elasticsearch: // ... my-logstash: // ... my-kibana: image: kibana:9.0.2 volumes: - ./kibana.yml:/usr/share/kibana/config/kibana.yml ports: - 5601:5601 depends_on: - my-elasticsearch
APM
Для APM никаких хитростей благо нет. Так же пишем конфиг и добавляем в docker-compose секцию с сервисом
apm-server: host: "0.0.0.0:8200" shutdown_timeout: 15s output.elasticsearch: hosts: ['my-elasticsearch:9200'] setup.dashboards.enabled: true setup.kibana: host: "my-kibana:5601"
services: my-logstash: // ... my-elasticsearch: // ... my-kibana: // ... my-apm: image: elastic/apm-server:9.0.2 volumes: - ./apm-server.yml:/usr/share/apm-server/apm-server.yml ports: - "8200:8200" depends_on: - my-elasticsearch
Теперь можем запускать наше приложение и смотреть в Kibana, как Elasticsearch наполняется драгоценными логами. Для этого переходим в Observability > Logs > Discover и наблюдаем записи
Пара слов о best-practises
Во‑первых, хотел бы упомянуть, что вообще в ELK‑стек чуть ли не по‑умолчанию входит Filebeat. Это сервис, который собирает логи из файлов и отправляет их в Logstash. Мне он показался не настолько интересным, чтобы перегружать им статью и без того полную всяких конфигов. Тем не менее это не отменяет его полезности и даже необходимости, поскольку как‑минимум third‑party приложения скорее всего не смогут самостоятельно слать логи в Logstash.
Во‑вторых, хорошей практикой в C# является использование Serilog. Если кто‑то вдруг с ним не знаком, то в двух словах Serilog — это такой провайдер логов «на стероидах» (хотя в основном все его используют просто для сохранения логов в файлы, т.к. в.NET нет такого провайдера по‑умолчанию). И скорее всего в реальном приложении вам потребуется пакет Elastic.Serilog.Sinks https://www.elastic.co/docs/reference/ecs/logging/dotnet/serilog‑data‑shipper
Вместо заключения
Несмотря на то, что современные инструменты логирования обладают высоким порогом вхождения (и я имею в виду в первую очередь ресурсоемкость сбора и обработки логов), они созданы для того, чтобы наоборот упростить нам жизнь. И в целом с этой задачей справляются они неплохо, хоть и сисадмины скорее всего облегчения особо не чувствуют.
Для интеграции ELK в.NET приложение разработчик Elastic предоставляет очень удобный Nuget‑пакет, с помощью которого сия задача решается в одну строчку кода. Примерно так же обстоят дела с мониторингом, что не может не радовать.
Из минусов можно отметить то, что ELK довольно громоздкий. По этой причине в будущем хочу копнуть в сторону Loki‑стека. Впрочем, для облачных приложений это не проблема. Популярные облачные платформы поддерживают интеграцию с ELK, а в development среде всегда можно развернуть Prometheus + Grafana или воспользоваться.NET Aspire, который существенно упрощает жизнь.
ссылка на оригинал статьи https://habr.com/ru/articles/922942/
Добавить комментарий