Observability: метрики, логи, трейсы
Что и где собирать, как соединять три пилара, какой бюджет на cardinality и хранение.
Спроектируй observability для {{stack}}. Масштаб: {{scale}}.
Главное различие — monitoring vs observability. Monitoring отвечает на известные вопросы ("CPU > 80%?"). Observability — на новые ("почему вчера в 15:42 latency для tenant X выросла в 3 раза только на /search?"). Цель — система, в которой можно расследовать то, чего ты не предусмотрел.
Три пилара и их назначение
| Пилар | Отвечает на | Сила | Слабость |
|---|---|---|---|
| Metrics | Что? Сколько? Куда тренд? | Дёшево хранить, быстрые запросы, alerts | Низкая cardinality, без контекста |
| Logs | Что именно случилось с этим запросом? | Богатый контекст, поиск по тексту | Объём, дорого, сложно агрегировать |
| Traces | Где время / ошибка в цепочке сервисов? | End-to-end видимость, причинно-следственная | Sampling нужен, инструментирование на каждом hop |
Используй все три, но осознанно: каждое событие — это не каждый сразу в три места.
1. Метрики: что собирать
Golden signals (USE / RED)
RED для сервисов:
- Rate — requests/sec по endpoint
- Errors — error rate (5xx, 4xx отдельно)
- Duration — гистограмма latency (p50, p95, p99)
USE для ресурсов:
- Utilization — CPU, memory, disk %
- Saturation — очереди (run queue, queue depth)
- Errors — counter ошибок
Business metrics
Технические метрики не достаточны. Добавь:
- Конверсия ключевых flow (signup, checkout)
- Заказов / минуту
- Активных юзеров
- Cost / request (если важно)
Падение бизнес-метрики при зелёных техническом — самый частый incident.
Cardinality — главный враг
Метрика http_requests_total{endpoint, status} — ок (десятки endpoint × ~10 status кодов).
Метрика http_requests_total{endpoint, status, user_id} — катастрофа: 1M юзеров × 100 endpoint = 100M time series. Бюджет Prometheus / Datadog взорвётся.
Правила:
- High-cardinality (user_id, request_id, session_id) — никогда в метриках. Они идут в logs/traces
- Bounded sets (env, region, plan, tier) — ок
- Перед добавлением label — оцени кардинальность
2. Логи: что и как
Структурированные
JSON, не printf. Без структуры — невозможно фильтровать.
{
"ts": "2026-05-17T10:11:12.345Z",
"level": "error",
"service": "billing",
"trace_id": "abc123...",
"span_id": "def456...",
"user_id": "u_42",
"tenant_id": "t_7",
"request_id": "req_99",
"msg": "charge failed",
"error": { "code": "card_declined", "provider_ref": "..." },
"amount_cents": 1999,
"duration_ms": 432
}
Уровни — дисциплинированно
- DEBUG — детальная отладка, выключено в prod (или sample)
- INFO — заметные бизнес-события (заказ создан, юзер зашёл)
- WARN — нештатно, но не баг (retry сработал, fallback использован)
- ERROR — что-то не сработало, нужно посмотреть
- FATAL — процесс умирает
Если в prod логи на INFO — > 90% объёма, у тебя проблема: пишешь шум.
Что НЕ логировать
- PII (email, телефон) без хеширования / маскирования
- Полные payload (могут содержать секреты)
- Каждый успешный SQL-запрос (это для tracing)
- Тонна одинаковых строк в цикле (sample)
Контекст
Каждая лог-запись должна иметь:
- trace_id и span_id (для связи с трейсом)
- request_id (если нет трейсинга)
- user / tenant / env
Это делается через middleware один раз, не вручную в каждом лог-вызове.
3. Трейсы: что и как
Что инструментировать
- HTTP входы/выходы
- DB queries
- External API calls
- Queue publish / consume
- Дорогие внутренние операции (рендеринг, вычисление)
OpenTelemetry — стандарт. Большинство фреймворков имеет auto-instrumentation.
Span атрибуты
- HTTP: method, url, status, duration
- DB: db.system, db.statement (без значений!), table
- Custom: business-relevant (order_id, items_count)
Sampling
100% трейсов — слишком дорого. Стратегии:
- Head sampling — решение на старте (1%, 10%) — простое, дешёвое, но теряет редкие ошибки
- Tail sampling — собрать весь trace, решить сохранять ли (всегда сохраняй errors / slow) — лучше, но требует сборщика
- Always sample errors + slow + sample regular at 1-10% — практичный компромисс
Связь с логами и метриками
Trace_id в каждой лог-записи и в exemplar'ах метрик (Prometheus + OTel поддерживает) — переход метрика → trace → лог в один клик.
4. Связывание трёх пиларов
Алерт: "p99 latency на /checkout > 2s"
↓ метрика → exemplar trace_id
Trace: видим что 80% времени в DB query
↓ trace span links
Logs: фильтруем по trace_id → видим конкретный запрос с user_id
↓ user_id
Logs: ищем все запросы user'а → видим pattern (retry storm)
Без trace_id в логах и exemplars в метриках — каждый incident это ручное соединение точек.
5. Алертинг
Симптом, не причина. Алёрти на то, что реально влияет на пользователя:
- SLO violation (error budget burn rate)
- High p99 latency
- Бизнес-метрика упала
- DLQ inflow
Не алёрти на:
- "CPU > 80%" сам по себе (это симптом, не проблема — может быть нормально)
- Каждый 5xx (шум) — алёрти на rate
- Disk space на 1% за день вперёд (есть время, не P1)
Каждый алерт имеет:
- Runbook (что делать)
- Severity (P0/P1/P2 с разными каналами)
- Owner
6. SLI / SLO
Service Level Indicator — измеримая метрика качества (% успешных запросов под 200ms). Service Level Objective — цель (99.9% за 30 дней). Error budget — что осталось до нарушения (10 min downtime в месяц при 99.97%).
Burn rate alert: тревожить не "у нас 0.5% ошибок", а "при текущем темпе бюджет кончится через 4 часа".
7. Бюджет и retention
Observability стоит реальных денег. Распределяй:
| Тип | Volume | Retention | Стоимость |
|---|---|---|---|
| High-resolution metrics (1s) | малый | 1-7 дней | средне |
| Standard metrics (1min) | средний | 90-400 дней | дёшево |
| Logs (info+) | большой | 7-30 дней горячее | дорого |
| Logs (debug) | очень большой | sample 1-10% или off в prod | – |
| Traces | средний (sampled) | 7-14 дней | средне |
| Audit logs | малый | годы | дёшево, но обязательно |
Tip: дешёвый "холодный" storage (S3 + Athena) для долгой ретенции; горячий — только для оперативного дебага.
8. Стек
- Metrics: Prometheus + Grafana (open), Datadog / New Relic / Honeycomb (SaaS), CloudWatch (AWS)
- Logs: Loki, ELK, Datadog, CloudWatch Logs, OpenSearch
- Traces: Jaeger, Tempo, Datadog APM, Honeycomb
- Standard: OpenTelemetry на стороне приложения, backends свободно меняешь
Не вендор-локись через проприетарные SDK; пиши через OTel.
9. Health checks vs metrics
- Liveness probe — "процесс жив?". Простой. Failure → restart
- Readiness probe — "готов принимать трафик?" (deps ok, warm cache). Failure → удаляем из LB
- Startup probe — для медленного старта (миграции). Не путать с liveness
Health check — это не observability, это управление трафиком. Метрики и логи нужны отдельно.
10. Чек-лист зрелости
- Каждый сервис экспортирует RED-метрики
- Логи структурированные с trace_id
- OTel инструментация на HTTP / DB / queue
- SLO определены для top-3 user-facing flow
- Алерты — по SLO burn rate, не по симптомам
- У каждого алерта есть runbook и owner
- Дашборд "один сервис" с RED + ресурсами в шаблоне
- Sampling настроен (errors всегда, остальное %)
- Cardinality budget описан
- Cost monitoring самой observability
Анти-паттерны
- Логировать "на всякий случай" каждое поле в DEBUG в prod — счёт за хранение взорвётся
- Метрика с user_id в label — cardinality bomb
- Trace без context propagation — каждый сервис делает свой root span
- Алерт на каждую ошибку — alert fatigue, важное теряется
- "У нас же есть Datadog" — но никто не смотрит до incident
- 99.999% SLO без обоснования — недостижимо и дорого, перепланируешь архитектуру под нереальное требование
- Логи без trace_id, метрики без exemplars — три пилара становятся тремя островами
В конце
- Карта: какие сервисы и что инструментировано
- RED для каждого критичного flow
- SLO и burn rate alerts для top-N flow
- Логирование conventions (формат, уровни, что нельзя)
- Sampling-стратегия для трейсов
- Cardinality budget по сервисам
- Бюджет $$ на observability и retention policy
- Runbook каждого алерта
Мониторинг и алёрты
Что мерить, какие алёрты ставить, как не превратить on-call в ад.
Измерение воронок: настройка
Какие воронки строить, как считать, на каких сегментах смотреть.
North-Star метрика и input-метрики
Одна главная метрика которая отражает успех продукта + 3-5 драйверов под неё.