Skip to content
imarch.dev
Назад в блог
· 7 мин чтения

Observability 3.0. Логи и трейсы - Loki, Tempo и OpenTelemetry

DevOps мониторинг Loki OpenTelemetry инструменты

Это третья статья из серии Observability 3.0 простыми словами. В предыдущей разобрали метрики и дашборды. Теперь логи и трейсы.

Метрики говорят что случилось. CPU вырос, ошибок стало больше, латентность подскочила. Но они не говорят почему. Для этого нужны логи и трейсы.

Зачем логи, если есть метрики

Простой пример. Grafana показывает - количество ошибок 500 выросло в 10 раз за последние 5 минут. Это метрика. Полезно, но что дальше?

Без логов вы знаете что сломалось (HTTP 500), но не знаете почему. С логами вы видите конкретное сообщение

2026-03-07 14:32:05 ERROR [order-service] Failed to connect to payment gateway: connection timeout after 30s

Теперь понятно. Сервис заказов не может достучаться до платёжного шлюза. Это уже конкретика, с которой можно работать.

Loki. Хранилище логов

Loki - система для хранения и поиска логов. Разработана Grafana Labs, поэтому идеально встраивается в стек.

Чем отличается от ELK

Если вы слышали про ELK (Elasticsearch + Logstash + Kibana), то Loki - его лёгкая альтернатива. Главное отличие в том, что ELK индексирует весь текст каждого лога. Loki индексирует только метки (labels) - название приложения, среда, сервер.

Что это значит на практике

  • Loki потребляет в разы меньше памяти и диска
  • Loki проще в настройке и обслуживании
  • Поиск по меткам быстрый, полнотекстовый поиск медленнее чем в ELK
  • Для 90% задач L2-поддержки скорости Loki хватает с запасом

Как устроен

Логи попадают в Loki через сборщик - Promtail или Grafana Alloy (новая замена Promtail).

Сборщик работает так

  1. Читает файлы логов на сервере, например /var/log/nginx/access.log
  2. Добавляет метки вроде {app="nginx", env="production", host="web-01"}
  3. Отправляет в Loki

Loki сжимает логи и хранит их. Когда вы ищете, Loki сначала фильтрует по меткам (это быстро), а потом ищет текст внутри подходящих потоков.

LogQL. Язык запросов для логов

У Loki свой язык запросов, похожий на PromQL.

{app="api-gateway"} |= "error"

Все логи приложения api-gateway, содержащие слово error.

{app="api-gateway"} |= "error" | json | response_time > 5000

Те же логи, но только где время ответа больше 5 секунд. Оператор | json автоматически разбирает JSON-формат логов.

{env="production"} |~ "timeout|refused|unreachable"

Все логи из рабочей среды, где встречается timeout, refused или unreachable. Оператор |~ это поиск по регулярному выражению.

sum(rate({app="api-gateway"} |= "error" [5m])) by (host)

Количество ошибок в секунду по каждому хосту за последние 5 минут. Полезно, чтобы понять - проблема на одном сервере или на всех.

Promtail и Grafana Alloy

Promtail - оригинальный сборщик логов для Loki. Простой, надёжный, делает свою работу.

Но есть нюанс. Grafana Labs прекратила активную разработку Promtail с февраля 2025 года. Замена - Grafana Alloy.

Alloy умеет всё то же, что Promtail, плюс

  • Собирает не только логи, но и метрики и трейсы (как OpenTelemetry)
  • Единый конфигурационный формат
  • Активно развивается

Если ставите с нуля - берите Alloy. Если Promtail уже работает, спешить с миграцией не надо. Он продолжает работать в режиме долгосрочной поддержки (LTS).

Зачем трейсы, если есть логи

Логи отвечают на вопрос - что произошло внутри одного сервиса. Но в микросервисной архитектуре один запрос пользователя проходит через 5-10 сервисов. Лог каждого сервиса - кусок пазла. Трейс собирает пазл целиком.

Пример. Пользователь жалуется - оформление заказа заняло 8 секунд.

Без трейсов. Вы смотрите логи каждого сервиса по отдельности. API Gateway - 200мс. Сервис заказов - 300мс. Сервис оплаты - непонятно. Где потерялись остальные 7.5 секунд? Неизвестно.

С трейсами. Открываете трейс конкретного запроса и видите водопад

API Gateway         |████|                                    200ms
  → Order Service   |    |██████|                             300ms
    → Inventory     |          |██|                           100ms
    → Payment       |            |████████████████████████|   7200ms  ← вот оно
      → Bank API    |            |████████████████████████|   7100ms

Сразу видно. Платёжный шлюз ответил за 7.1 секунды. Проблема не в ваших сервисах, а во внешнем API банка.

Tempo. Хранилище трейсов

Tempo - хранилище трейсов от Grafana Labs. Работает по тому же принципу, что Loki. Минимум индексации, максимум эффективности.

Как устроен

  1. Приложения инструментируются через OpenTelemetry (добавляется библиотека в код)
  2. При каждом запросе создаётся уникальный trace ID - идентификатор, который проходит через все сервисы
  3. Каждый сервис отправляет свой span (отрезок) в Tempo
  4. Tempo собирает все спаны с одним trace ID и складывает в единый трейс

Что вы увидите

В Grafana трейсы отображаются как диаграмма-водопад (waterfall). Каждая строка - один сервис. Ширина полоски - время выполнения. Красным подсвечиваются ошибки.

Вы кликаете на конкретный спан и видите

  • Длительность
  • HTTP-статус
  • Теги - URL, метод, параметры
  • Логи, привязанные к этому спану

Связка с метриками и логами

Главная сила Tempo - интеграция с Grafana. Видите в метриках всплеск латентности? Кликаете View traces и видите конкретные медленные запросы. Кликаете на спан - видите привязанные логи из Loki. Всё связано.

Эта связка метрики -> трейсы -> логи называется корреляция. Именно она отличает observability от простого мониторинга.

OpenTelemetry. Универсальный стандарт

OpenTelemetry (OTel) - не инструмент, а стандарт. Он описывает, как приложения должны генерировать и отправлять метрики, логи и трейсы.

Зачем нужен стандарт

Без OTel каждый инструмент говорит на своём языке. Prometheus хочет метрики в одном формате, Jaeger хочет трейсы в другом, Fluentd хочет логи в третьем. Разработчику приходится ставить три разные библиотеки.

С OTel всё проще. Одна библиотека, один формат, один коллектор. Данные идут в OTel Collector, а он уже отправляет их куда нужно. Метрики в Prometheus, логи в Loki, трейсы в Tempo.

OTel Collector

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

Зачем нужен посредник

  • Приложения не зависят от конкретной системы хранения. Решили перейти с Jaeger на Tempo - меняете конфигурацию коллектора, а не код приложений
  • Коллектор может сжимать данные, отбрасывать ненужные, добавлять метки
  • Одна точка конфигурации вместо десяти

Проверка работы коллектора

Если OTel Collector установлен и вам надо проверить, работает ли он

curl http://localhost:13133/

Если ответ содержит "status": "Server available" - коллектор работает.

Если метрики пустые (эндпоинт /metrics отвечает, но данных нет) - приложение не отправляет данные в коллектор. Проверяйте настройки приложения.

Как всё работает вместе

Полный поток данных

  1. Приложение инструментировано через OpenTelemetry. При каждом запросе создаётся трейс, пишутся логи, обновляются метрики
  2. OTel Collector принимает всё и раскидывает. Метрики в Prometheus, логи в Loki, трейсы в Tempo
  3. Grafana показывает всё в едином интерфейсе с корреляцией

Или без OTel (проще, но менее гибко)

  1. Node Exporter / cAdvisor отдают метрики напрямую в Prometheus
  2. Promtail / Alloy отправляет логи в Loki
  3. Grafana показывает

Первый вариант для приложений (нужна инструментация кода). Второй для инфраструктуры (работает без изменений в коде).

Docker Compose. Добавляем логи и трейсы

Если вы уже подняли базовый стек из статьи про метрики, добавьте в docker-compose.yml следующие сервисы

  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    volumes:
      - loki-data:/loki
    restart: unless-stopped

  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/log:/var/log:ro
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    restart: unless-stopped

  tempo:
    image: grafana/tempo:latest
    ports:
      - "3200:3200"
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
    volumes:
      - tempo-data:/var/tempo
    command: -config.file=/etc/tempo/tempo.yaml
    restart: unless-stopped

Добавьте volumes в секцию volumes

volumes:
  loki-data:
  tempo-data:

Минимальный promtail-config.yml

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/*.log

После запуска добавьте в Grafana два новых Data Source

  • Loki → URL http://loki:3100
  • Tempo → URL http://tempo:3200

Теперь в Grafana работает корреляция. На графике метрик можно кликнуть и перейти к логам за тот же период.

Практический сценарий для L2

Вот как выглядит разбор инцидента с полным стеком.

Ситуация. Алерт в Slack - латентность order-service выше 2 секунд

Шаг 1. Метрики (Grafana + Prometheus) Открываете дашборд order-service. Видите, что латентность p99 прыгнула с 200мс до 3 секунд в 14:25.

Шаг 2. Трейсы (Tempo) Кликаете View traces за период 14:25-14:30. Видите трейсы, где спан database-query занимает 2.8 секунды вместо обычных 50мс.

Шаг 3. Логи (Loki) Кликаете на спан, смотрите привязанные логи. Видите WARN: connection pool exhausted, waiting for free connection. База не виновата - закончились соединения в пуле.

Шаг 4. Корреляция Смотрите метрики PostgreSQL Exporter за тот же период. Активных соединений 100 из 100 (максимум). Обычно 20-30. Кто-то открывает соединения и не закрывает.

Эскалация. Сервис order-service - латентность выросла из-за исчерпания пула соединений к PostgreSQL. Активных соединений 100 из 100 с 14:25. В логах connection pool exhausted. Вероятная причина - утечка соединений. Нужна проверка кода.

Весь разбор - 5 минут.


Часть серии Observability 3.0 простыми словами. На основе материалов Cumhur M. Akkaya.

Поделиться:

Похожие статьи