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¶
- 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).502se nenhum provider ligou; oclient_gradesó traz refs de providersligada(nuncaenabledsem ref). O resultado da injeção é sempre reportado, nunca só logado. - GlitchTip nomeado pelo
app_id(0.17.5). Nome estável → slug estável → find-before-create confiável, sem duplicata (onome-sentença longo derivava slug feio e permitia duplicatas). - SDK de erros =
io.sentry:sentry+io.sentry:sentry-logback(8.42.0). UmSentryAppendernologback.xmllê 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. /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 +
ExceptionHandlercustom — 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
502sai como está — não háErrorResponseProcessor/RFC 7807 neste repo (a convenção real de erro é o map{error, message}), entãoHttpResponse.status(502).body(...)não é reescrito. injectToCoolifysaiu doProvisioningServicee virouCoolifyClient.injectEnvs(...) -> InjectionResult(testável direto por WireMock, inclusive o caminhoinjected=falsedo 401/403).- A injeção reinicia o recurso: após upsertar a env,
injectEnvschamaPOST /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 ostate/last_errorjá persistidos emapp_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 oRateLimiter— mas é uma decisão consciente de expor um endpoint sem auth num serviço de auth. - Rollout: re-provisionar o
authcria o projetoauthno 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.