Перейти к основному содержимому

ADR-0047: Structured Logging И Observability Standards

Статус

Принято.

Контекст

Pin — это распределённая система из 10+ микросервисов, работающих в k8s. Без единого observability стандарта:

  • логи несравнимы между сервисами (разные field names, разные уровни);
  • метрики несогласованы (нет единого naming convention);
  • traces не связываются между сервисами (отсутствие propagation);
  • при инциденте невозможно восстановить хронологию операции.

Для gambling регулятора необходимо доказать «что произошло» по конкретной транзакции — это невозможно без полноценного observability.

Решение

Structured Logging — Serilog

Каждый log entry содержит стандартный набор полей:

ПолеТипИсточник
correlationIdstringHTTP header X-Correlation-Id или генерируется
requestIdstringHttpContext.TraceIdentifier
tenantIdintIActorContext.TenantId
brandIdintIActorContext.BrandId
actorIdGuidIActorContext.ActorId
actorTypestringIActorContext.ActorType
servicestringenv var SERVICE_NAME
environmentstringenv var ASPNETCORE_ENVIRONMENT

Поля добавляются через LogContext.PushProperty() в middleware, один раз — не в каждом log call.

Log Level Семантика

LevelКогда
InformationНормальный flow: операция начата, завершена, ключевые шаги
WarningRecoverable: idempotent duplicate, rate limit, validation fail, stale replica
ErrorUnexpected failures: DB error, provider error, unhandled exception
CriticalСистема нефункциональна: DB down, security breach, OOM

Debug и Trace — только в local/staging, не попадают в production.

Metric Naming Convention

Формат: <service>_<entity>_<operation>_<unit>

Примеры:

  • accounting_withdrawal_create_duration_ms — гистограмма
  • accounting_withdrawal_approved_total — counter
  • accounting_deposit_pending_count — gauge
  • kyc_verification_queue_size — gauge
  • masstransit_outbox_pending_count — gauge

Все метрики экспортируются через Microsoft.Extensions.Diagnostics.Metrics (OTEL).

Обязательные метрики для каждого handler:

  • _duration_ms (histogram)
  • _errors_total (counter by error type)
  • _requests_total (counter)

OTEL Tracing

Span создаётся для:

  • каждого MediatR handler (command/query);
  • каждого EF transaction;
  • каждого HTTP/gRPC client вызова;
  • каждого Kafka publish/consume;
  • каждого Redis get/set;
  • каждого Elasticsearch query;
  • каждого provider adapter call.

Span attributes минимум: service.name, correlation.id, tenant.id, actor.id, db.statement (без значений параметров).

Correlation Propagation

correlationId распространяется через:

  • HTTP header X-Correlation-Id (inbound и outbound)
  • Kafka message header correlation-id
  • MassTransit envelope field CorrelationId
  • Log scope (LogContext.PushProperty("correlationId", ...))

При отсутствии X-Correlation-Id — генерируется Guid.NewGuid().ToString() в middleware.

Sentry

Unexpected exceptions отправляются в Sentry. Перед отправкой:

  • SentryBeforeSendCallback удаляет поля из [Sensitive] атрибута;
  • Нет raw PII, payment credentials, provider tokens в Sentry context.

Последствия

  • По любому correlationId можно восстановить полную трассировку операции через все сервисы.
  • Метрики по единому naming convention собираются в единый Grafana dashboard.
  • Log scanning в CI может обнаружить sensitive поля в логах по regex.
  • Регулятор получает доказательную базу по любой транзакции.

Ссылки