# Наблюдаемость: метрики, логи, трейсы

> Три столпа, стек Prometheus + Loki + Grafana, что мерить в AI-продукте и как связать три источника

_Источник: https://sii.sergeivolchkov.ru/materials/observability/three-pillars_





Мониторинг отвечает на вопрос «работает ли?», наблюдаемость — на вопрос
&#x2A;*«почему сломалось?»**, причём для поломки, которую вы не предвидели.
Разница практическая: первое — это график с зелёной галочкой, второе —
возможность за десять минут дойти от «пользователь жалуется» до
конкретной строчки и конкретного запроса.

<img alt="Три столпа наблюдаемости" src="__img0" />

## Три столпа и вопросы, на которые они отвечают [#три-столпа-и-вопросы-на-которые-они-отвечают]

| Столп       | Что это                          | Вопрос                                      | Инструмент      |
| ----------- | -------------------------------- | ------------------------------------------- | --------------- |
| **Метрики** | Числа во времени, дёшево хранить | Что и когда пошло не так?                   | Prometheus      |
| **Логи**    | События с контекстом, дороже     | Что именно произошло в этот момент?         | Loki            |
| **Трейсы**  | Путь одного запроса по системе   | Где потерялось время и на каком шаге упало? | Tempo, Langfuse |

<DataShapes />

Порядок расследования всегда один: **метрика показывает аномалию, трейс
находит шаг, лог объясняет причину**. Стек нужен целиком именно потому,
что каждый следующий шаг без предыдущего означает «искать вручную».

* **Prometheus** ходит по `/metrics` ваших сервисов и складывает ряды.
  Формат простой текстовый, клиентские библиотеки есть везде.
* **Loki** хранит логи не как полнотекстовый индекс, а по меткам
  (`service`, `env`, `level`) — поэтому дёшево и хорошо ложится на
  один сервер.
* **Grafana** — одно окно: дашборды поверх обеих баз и алерты.

## Что мерить в AI-продукте [#что-мерить-в-ai-продукте]

Обычные четыре сигнала (нагрузка, задержка, ошибки, насыщение) остаются,
но добавляются свои — иначе продукт «работает», а денег нет:

* **Стоимость.** Токены и рубли на запрос, на пользователя, в сутки.
  Метрика, которая раньше всех показывает, что промпт распух.
* **Задержка отдельно для вызовов модели.** Общий p95 API маскирует то,
  что модель отвечает по восемь секунд.
* **Доля деградаций.** Как часто сработал фолбэк, ретрай, кэш вместо
  живого ответа — это [альтернативы из ваших
  UC](/materials/use-cases/writing), ставшие метрикой.
* **Качество.** Доля ответов, не прошедших проверку формата или
  валидацию; оценки из evals по свежему трафику.

Полезное правило: **если в UC написано «система деградирует на архив» —
это счётчик**. Не измеряете деградации — не узнаете, что продукт
наполовину живёт на фолбэках.

## Связка: один запрос — одна история [#связка-один-запрос--одна-история]

Три столпа сходятся только при общем идентификаторе. `trace_id`
рождается на входе в систему, кладётся в лог каждой записи, в спан
трейса и в атрибуты трейса LLM. Тогда из графика в Grafana можно
провалиться в конкретный запрос, а из него — в логи именно этого
запроса.

Без этого получается три отдельных инструмента и ручное сопоставление по
времени — то, ради чего наблюдаемость и заводили, не работает.

Минимум для проекта:

```yaml title="compose.yml — стек наблюдаемости"
services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
  loki:
    image: grafana/loki:latest
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3001:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
    volumes:
      - grafana:/var/lib/grafana

volumes:
  grafana:
```

Дашборды и источники данных тоже [as-code](/materials/architecture/c4-levels):
JSON дашборда и provisioning-файлы лежат в `monitoring/` в репозитории.
Дашборд, собранный кликами и живущий только в одном браузере, — это не
артефакт, его нельзя ни сдать, ни восстановить.

## Алерты [#алерты]

Алерт имеет смысл, только если на него **кто-то реагирует ночью или его
не должно быть**. Для проекта курса хватает трёх:

1. Сервис не отвечает (health-чек падает N минут).
2. Доля ошибок выше порога за окно.
3. Стоимость за сутки превысила бюджет.

Всё остальное смотрится глазами на дашборде: когда алертов много, команда
перестаёт на них реагировать.

Дальше — [Langfuse](/materials/observability/langfuse): третий столп
для той части системы, где обычные трейсы бесполезны.
