Pular para conteúdo

0011 — Observabilidade de erros (GlitchTip) e resposta honesta do /provision

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-03 · Decidido em: 2026-07-03

Contexto

A constituição X-Adm 0.17.5/0.17.6 ("Feedback §6 do auth") nasceu de dores desta base: provisionar o GlitchTip do próprio auth (que é o broker da Central de Apps) custou várias idas-e-vindas cegas porque o POST /api/setup/provision engolia falha — respondia 200 com client_grade={enabled:true} sem dsn, e a injeção no Coolify falhava só em WARN (token com typo → 401; IP de origem fora do allow-list → 403). Sem o JWT admin, nem o /xadm-setup nem o operador distinguiam sucesso de falha. Em paralelo, o auth não tinha SDK que consumisse o DSN provisionado — o pipe de erro nunca havia sido validado ponta-a-ponta.

Decisão

  1. Resposta honesta do /provision (0.17.6). A resposta carrega, por provider, o estado real: state (ligada|falha) + last_error, e o resultado da injeção no Coolify (injected: true|false|null + injected_reason). 502 se nenhum provider ligou; o client_grade só traz refs de providers ligada (nunca enabled sem ref). O resultado da injeção é sempre reportado, nunca só logado.
  2. GlitchTip nomeado pelo app_id (0.17.5). Nome estável → slug estável → find-before-create confiável, sem duplicata (o nome-sentença longo derivava slug feio e permitia duplicatas).
  3. SDK de erros = io.sentry:sentry + io.sentry:sentry-logback (8.42.0). Um SentryAppender no logback.xml lê o DSN de ${SENTRY_DSN} e reporta todo log ERROR (inclui exceções não-tratadas que o Micronaut já loga) — zero código no negócio. Sem DSN (dev/test) → no-op.
  4. /test — smoke-tests públicos. Superfície não-autenticada, inócua e rate-limited para checar integrações em produção; o 1º teste dispara uma exceção benigna e valida o pipe do GlitchTip.

Alternativas consideradas

  • Módulo micronaut-sentry (comunidade, akyong) — não-oficial e sem compat confirmada com Micronaut 5 (bleeding edge, baseline Java 25). Preterido pelo SDK Sentry puro + appender logback, independente da versão do framework e no idioma da casa (pin explícito de lib externa).
  • Init programático + ExceptionHandler custom — mais código e superfície; captura só o que se roteia (arrisca perder exceções fora do handler). O appender logback cobre tudo que loga ERROR.

Consequências

  • O corpo-relatório do 502 sai como está — não há ErrorResponseProcessor/RFC 7807 neste repo (a convenção real de erro é o map {error, message}), então HttpResponse.status(502).body(...) não é reescrito.
  • injectToCoolify saiu do ProvisioningService e virou CoolifyClient.injectEnvs(...) -> InjectionResult (testável direto por WireMock, inclusive o caminho injected=false do 401/403).
  • A injeção reinicia o recurso: após upsertar a env, injectEnvs chama POST /applications/{uuid}/restart (best-effort) — variável de ambiente não recarrega em processo vivo, então sem o restart o container seguiria com o DSN antigo até o próximo deploy. Restart que falha não desfaz a injeção (injected=true + aviso no motivo). Trade-off: re-rodar o setup reinicia o recurso (breve indisponibilidade) mesmo quando o valor não mudou — candidato a otimização futura (reiniciar só se a env de fato mudou).
  • providers[] reusa o state/last_error já persistidos em app_provider_resources; injected é transitório (sem migration).
  • Superfície pública nova (/test): aceitável por ser read-only, sem segredo e sem mutação, sob o RateLimiter — mas é uma decisão consciente de expor um endpoint sem auth num serviço de auth.
  • Rollout: re-provisionar o auth cria o projeto auth no GlitchTip; o projeto antigo (nomeado pelo nome longo) fica órfão e deve ser deletado à mão.
  • Casa sem recipe Sentry para java-micronaut (só Flutter tem) → candidato a feedback §6.