# ADR-024 — Observabilidade nativa: JSON + RFC 7807 (+ OTel opcional)

| Campo | Valor |
|---|---|
| **Status** | Aceito (2026-10-16) |
| **Data** | 2026-10-14 |
| **Plano de origem** | `wiki/plan/01-analise-e-decisoes.md` |
| **Relacionadas** | ADR-001, ADR-020 |

## Contexto

O legado usava `var_dump`/texto livre e logs não correlacionáveis. O `cms/` provou o valor de
`X-Request-ID` + OTel. Porém, o OTel PHP é uma árvore de dependências pesada; o projeto exige
**mínimo de dependências** e simplicidade. O obrigatório precisa ser **nativo**.

## Decisão

- **Obrigatório e nativo:** log **JSON estruturado** (Monolog) com `ts`, `level`, `request_id`,
  `trace_id`, `module`, `action`, `user`, `duration_ms`, `message`, `context`; **`X-Request-ID`**
  (UUIDv7) sempre propagado/ecoado; erros em **RFC 7807**. Por padrão o canal `stack` envia para
  a observabilidade (`json`/stderr) **e** para arquivo local com **rotação diária e retenção de
  730 dias** (`daily`, `LOG_DAILY_DAYS`).
- **PII mascarada** antes de sair do processo (`pii_redact()`).
- **OTel é trilha opcional** (perfil `telemetry`), atrás de interface fina e **fail-open**
  (nunca derruba a request). O núcleo funciona sem ele.
- Sem fachada paralela ao `Log`: helper fino `log_event()`.

> **Atualização (integração).** A telemetria é **capacidade de primeira classe** do projeto —
> **não** um item opcional. Implementada **sem** o SDK OpenTelemetry: exportador **OTLP/HTTP JSON
> nativo** (`ext-curl`) atrás de `Core\Contracts\Telemetry`, com `NullTelemetry` como fallback e
> `OtlpTelemetry` **fail-open** + circuit breaker em **APCu** (compartilhado entre workers). É
> habilitada por perfil (`TELEMETRY_ENABLED`) e materializada no profile Compose `telemetry`
> (Jaeger). O contrato deve estar **permeado** por core, services e modules — toda operação
> relevante emite span/métrica; ver `wiki/specs/06-observabilidade.md` §Pervasividade.

## Justificativa (alternativas descartadas)

- **OTel obrigatório no dia zero** — descartada: dependência pesada para um requisito que o
  log nativo já cobre.
- **Texto livre (`Log::error("falhou")`)** — descartada: não é observável nem auditável.
- **`var_dump`/`dump` em produção** — descartada.
- **Horizon/Pulse/Telescope** — descartados por **mínimo de dependências** (dashboard/BD próprios);
  fila é nativa no Valkey e a observabilidade é JSON + `X-Request-ID`, com OTel opcional.

## Consequências

**Positivas**
- Observabilidade desde o dia zero **sem** dependência externa.
- Correlação log↔trace quando o OTel está ligado.

**Custos / negativas**
- Sem OTel, não há spans distribuídos (aceitável; JSON + `request_id` cobrem o essencial).
- Redaction exige disciplina/teste.

## Exemplo mínimo (comparativo)

```php
// ❌ Texto livre, sem campos
Log::error("falhou ao salvar");

// ✅ Evento estruturado nativo
log_event('noticia.salvar', [
    'module' => 'noticia', 'action' => 'salvar', 'id' => $noticia->id,
    'user' => $user->id, 'duration_ms' => $ms,
]); // + X-Request-ID no header; erro → RFC 7807
```

## Verificação

- Teste: 1 request → 1 log JSON com todos os campos; `X-Request-ID` ecoado e único.
- Teste: `pii_redact()` mascara CPF/e-mail/telefone.
- (Opcional) exportador OTLP em porta morta → request continua 200; breaker APCu limita o overhead.

## Referências

- `cms/specs/0_projeto/6_acessibilidade_e_performance.md`; `wiki/plan/06-observabilidade-day-zero.md`.
