Наблюдаемость

NOTE

Статус: Mixed — структурное логирование (platform/observability.StartPhase / LogEvent) и Prometheus stack (ADR-0048) уже live. Дашборды, алерты и SLO остаются target. Правила маркировки — в 50-processes/documentation-standard.md.

Политика мониторинга, логирования и трейсинга.

Трейсинг

  • OpenTelemetry SDK во всех сервисах.
  • Exporter: OTLP gRPC → collector → Tempo/Jaeger.
  • Каждый внешний запрос создаёт span.
  • correlation_id пропагируется во всех событиях и HTTP-вызовах.
  • Минимум tracing: вход HTTP → use case → external call → DB.

Метрики

  • Prometheus-compatible endpoint /metrics.
  • Префикс метрик по сервису (ingestion_, matcher_, …).
  • Обязательные метрики для каждого сервиса:
    • RED (requests, errors, duration).
    • Кастомные (см. README сервиса).

Логирование

  • Structured JSON, stdout.
  • Обязательные поля: ts, level, service, trace_id, msg.
  • Для фазовых операций используется единый контракт platform/observability.StartPhase. Обязательные поля события: event, event_type, state, outcome, duration_ms, operation, component, layer, job_id. Для supplier ingestion дополнительно: supplier, phase, credential_ref, счётчики фазы (item_count, price_count, stock_count, warehouse_count) и безопасные параметры стратегии (batch_size, max_items). Credentials и raw URL с query/token в логи не пишутся.
  • Для одиночных событий вне границ фазы используется LogEvent(ctx, log, "event.name", attrs...) — тот же набор correlation-полей (request_id/trace_id/span_id/operation/component/ layer/job_id/message_id/parent_operation) подтягивается автоматически из platform/errors.Snapshot(ctx). Имена событий — стабильные dot-separated строки (ingestion.bulk.checkpoint), не free-form, чтобы Loki/Vector могли фильтровать без зависимости от текста.
  • Для долгих внутренних циклов пишутся progress-события event="ingestion.bulk.progress" с тем же trace_id: phase, warehouse_ref, page, page_count, rows_seen. Это позволяет отличить зависание от штатной долгой пагинации.
  • Для внешних HTTP-вызовов поставщиков пишутся event="ingestion.http.request.start" / event="ingestion.http.request.end". В лог попадают supplier, phase, безопасный path, page, batch_size, status, duration_ms, но не query string, session id, accessCode, login, password или token.
  • Чувствительные данные (credentials, customer PII) — редактируются.
  • Уровень логирования — через env var, по умолчанию info.

Быстрая трассировка ingestion

  1. Найти тик поставщика:
docker logs tracium-supplier-sync-1 2>&1 \
  | jq -c 'select(.event=="ingestion.tick" and .supplier=="etm")'
  1. Взять trace_id из ingestion.tick.end.

  2. Посмотреть все фазы того же тика:

docker logs tracium-supplier-sync-1 2>&1 \
  | jq -c 'select(.trace_id=="<trace_id>")'
  1. Найти ошибочные фазы по всем поставщикам:
docker logs tracium-supplier-sync-1 2>&1 \
  | jq -c 'select(.event=="ingestion.bulk.phase" and .state=="completed" and .outcome!="success")'
  1. Посмотреть прогресс длинной фазы:
docker logs tracium-supplier-sync-1 2>&1 \
  | jq -c 'select(.event=="ingestion.bulk.progress" and .trace_id=="<trace_id>")'
  1. Для one-shot логов из .cache/live-runs/*.jsonl получить компактную таблицу фаз:
jq -rc '
  select(.event=="ingestion.bulk_snapshot.tick.summary"
    or (.event=="ingestion.bulk.phase" and .state=="completed"))
  | [.time,.supplier,(.phase//"summary"),.outcome,.duration_ms,
     (.item_count//""),(.price_count//""),(.stock_count//""),(.stock_complete//"")]
  | @tsv
' .cache/live-runs/<run>.jsonl
  1. Найти медленные или ошибочные supplier HTTP-вызовы:
docker logs tracium-supplier-sync-1 2>&1 \
  | jq -c 'select(.event=="ingestion.http.request.end" and (.outcome!="success" or .duration_ms>30000))'

Алертинг

  • Grafana / Alertmanager.
  • Базовые алерты:
    • error_rate > 1% за 5 min на любом сервисе.
    • p95 latency > threshold на HTTP endpoint.
    • Kafka consumer lag > threshold.
    • ES cluster status != green.
    • PG connection pool exhaustion.
  • Бизнес-алерты:
    • rate_limit_hits на поставщике > threshold.
    • session_refresh_failed.
    • async_job_timeout.
    • moderation_queue_size > threshold.

Наблюдаемость по пайплайнам

Базовые метрики RED обязательны для всех сервисов. Ниже — обязательный минимум дополнительных метрик и алертов по критическим путям.

Ingestion pipeline

Метрики:

  • ingestion_payload_fetched_total{supplier_id, credential_id, endpoint} — успешные/ошибочные fetch.
  • ingestion_payload_size_bytes{supplier_id} — гистограмма.
  • ingestion_normalization_duration_seconds{supplier_id, stage} — стадии fetch → parse → normalize → emit.
  • ingestion_backlog_size{supplier_id, stage} — кол-во SKU в очереди на каждой стадии.
  • ingestion_observations_emitted_total{supplier_id, scope} — observations записанные в offer.observation.v1.
  • ingestion_lag_seconds{supplier_id}now() - last_observed_at для топ-SKU данного поставщика.
  • etm_credential_usage_ratio{credential_id} — % использованного rate budget (см. etm/rate-budget.md).

Алерты:

  • ingestion_lag_seconds{supplier_id} > 12 * 3600 (12h) для топ-SKU.
  • ingestion_backlog_size > 10 000 для одного поставщика > 1 час.
  • etm_credential_usage_ratio > 0.85 за час.
  • kafka_consumer_lag{group="normalizer"} > 10 000 5 мин.
  • session_refresh_failed_total > 3 за 10 мин на одной credential.

Matching pipeline

Метрики:

  • matching_decisions_total{confidence} — exact / strong / probable / weak / unmatched.
  • matching_decision_duration_seconds{strategy} — deterministic / heuristic / ml.
  • matching_backlog_size — offers в очереди на матчинг.
  • matching_orphan_pool_size — текущий размер orphan pool.
  • matching_rematch_runs_total{trigger} — periodic / profile_changed / new_canonical.

Алерты:

  • matching_backlog_size > 5 000 > 30 мин.
  • matching_orphan_pool_size growth > 1000/day (рост быстрее ожидаемого).
  • Доля weak/unmatched > 30% от decided/час — деградация качества.

Enrichment pipeline

Метрики:

  • enrichment_jobs_queued_total{type} / enrichment_jobs_completed_total{type, status} / enrichment_jobs_failed_total{type, reason}.
  • enrichment_jobs_in_flight{type} — gauge.
  • enrichment_job_duration_seconds{type} — гистограмма по типу job (discovery/refresh_observation/refresh_characteristics/…).
  • enrichment_jobs_dedup_hits_total — сколько подключилось к pending job’у.
  • llm_calls_total{provider, model, intent} / llm_tokens_used_total{provider, direction}.

Алерты:

  • enrichment_jobs_in_flight > 1000 > 15 мин.
  • p95 длительности конкретного типа job выше 2× baseline (например, refresh_observation > 60s).
  • enrichment_jobs_failed_total{reason="timeout"} > 1% за час.

Visibility policy

Метрики (см. также 30-services/visibility/README.md):

  • visibility_evaluations_total{target_kind}.
  • visibility_evaluation_duration_seconds{target_kind}.
  • visibility_compile_duration_seconds.
  • visibility_cache_hits_total / visibility_cache_misses_total.
  • visibility_filtered_total{effect}.
  • visibility_policy_compile_errors_total.

Алерты:

  • visibility_cache_miss_rate > 0.5 за 5 мин — проблема инвалидации.
  • visibility_evaluation_duration_seconds p95 > 50ms — performance regression (см. бюджет в data-visibility-policy.md).
  • visibility_policy_compile_errors_total > 0 — некорректная policy в admin UI.

Event sourcing / outbox

Метрики:

  • event_store_writes_total{aggregate_type, event_type}.
  • outbox_unpublished_count — gauge.
  • outbox_lag_secondsnow() - min(enqueued_at) WHERE published_at IS NULL.
  • outbox_publish_attempts_total{outcome}.
  • event_store_archiver_runs_total{outcome}.

Алерты:

  • outbox_unpublished_count > 10 000 > 5 мин.
  • outbox_lag_seconds p95 > 5 > 5 мин.
  • outbox_publish_attempts_total{outcome="failed", attempts=">=5"} > 0.

ClickHouse projection (см. ADR-0019)

Метрики:

  • ch_ingestion_lag_seconds{topic} — kafka offset → видно в SELECT.
  • ch_ingestion_errors_total{topic} — попадания в _errors table.
  • ch_query_duration_seconds{query_class}.

Алерты:

  • ch_ingestion_lag_seconds p95 > 30 за 10 мин.
  • ch_ingestion_errors_total > 0 за час.

Дашборды

На момент Phase 0 — базовые:

  • Service RED по каждому сервису.
  • Kafka topic lag по группам + outbox lag.
  • Storage health: PG / ES / CH / Redis.
  • Ingestion overview: по поставщикам / credentials — fetch rate, lag, ошибки, использование rate budget.
  • Matching quality: распределение уровней confidence, backlog, размер orphan pool.
  • Visibility engine: cache hit rate, latency, количество отфильтрованных записей.
  • Enrichment jobs: in-flight, success rate, latency по типам.

SLO

ОбъектSLOОкноError budget
GET /v1/products/{id} p95 latency< 300 мс30 дней0.1%
POST /v1/pricing p95 latency< 300 мс30 дней0.1%
POST /v1/estimates p95 latency (≤ 200 строк)< 10 с30 дней0.5%
Доступность публичного API≥ 99.9%30 дней43 мин
Outbox publish lag p95< 1 с7 дней
Visibility evaluation overhead p95< 50 мс7 дней
Ingestion freshness (топ-SKU цена)≤ 6 ч7 дней1% SKU
Ingestion freshness (топ-SKU остаток)≤ 2 ч7 дней1% SKU

Политика error budget: при пробое — заморозка фич, фокус на восстановлении. Эскалация в oncall.