Files
DeskcommCRM/instrumentation-client.ts
T
Rafael MelgaçoandClaude Opus 5 77b4a4863b fix(sentry): a comunidade recebe erro, não sessão — o 429 que reprovava o e2e
O `olhar-telas-do-epico` reprovava com 429 em todas as 7 telas, e a pista que eu
mesmo deixei no workflow apontava para o limitador em memória. Medido: errado.

No job real, 120 quedas para memória, 103 no bucket `auth:login:ip` contra teto de
1000 — o limitador não barrou nada. O 429 vinha do túnel `/monitoring`, e o
instrumento jogava fora justamente a URL que identificava o dono (a mensagem do
browser para requisição barrada não diz quem respondeu, e o relatório não guarda
trace). Com a URL capturada, o percurso de 7 telas mediu: 19 requisições, 17
respostas, TODAS 429, com `x-sentry-rate-limits: 60::organization:suspended` —
categoria vazia, isto é, todas. E o corpo de cada envelope era `{"type":"session"}`
com `errors: 0`.

A política de `isCommunityDsn` estava declarada e não estava em vigor: as duas
amostragens foram a zero e a terceira torneira ficou fora da conta.
`browserSessionIntegration` é DEFAULT do @sentry/browser com `lifecycle: "route"`,
então cada navegação fechava uma sessão e abria outra. Passou porque
`integrations: [x]` SOMA aos defaults do SDK — só a forma de função os substitui, e
a diferença não aparece em tipo, em lint nem em teste que não abra um browser.

Consertos:
- `integracoesDoCliente` tira a `BrowserSession` quando o DSN é o da comunidade, e
  mantém tudo para quem aponta o próprio Sentry (mesma assimetria das amostragens).
- o `init` passa `integrations` como função.
- o gerador do `.env.e2e` escreve `SENTRY_DSN=off`: a suíte não manda dado para o
  Sentry de produção do projeto, e a cor do CI não depende do estado de cobrança de
  um terceiro. Consequência aceita: a suíte deixa de exercitar a política, então
  quem a guarda é o gate.
- a spec registra a URL do erro de console.

Medido depois, mesmo percurso, DSN da comunidade ainda ativo: 0 requisições ao
túnel, spec verde em 44s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WKf64hXUbFatzofJHZREr7
2026-08-10 09:53:25 -03:00

42 lines
1.6 KiB
TypeScript

// This file configures the initialization of Sentry on the client.
// The added config here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
import { resolveSentryDsn, isCommunityDsn, integracoesDoCliente } from "./lib/sentry/dsn";
import { sentryScrubHooks } from "./lib/sentry/scrub";
const sentryDsn = resolveSentryDsn(
typeof window !== "undefined" ? window.__PUBLIC_ENV__?.SENTRY_DSN : undefined,
);
const community = isCommunityDsn(sentryDsn);
Sentry.init({
dsn: sentryDsn,
// FORMA DE FUNÇÃO, não de array: array SOMA aos defaults do SDK, e era assim
// que a `BrowserSession` (default) seguia ligada apesar da política abaixo. A
// função RECEBE os defaults e o retorno os substitui — é o único jeito de tirar
// uma integração default sem enumerar as outras dez à mão.
integrations: (padraoDoSdk) => [
...integracoesDoCliente(padraoDoSdk, community),
Sentry.replayIntegration(),
],
// No Sentry da comunidade, só erro (issue #100): sem trace, sem replay de
// sessão e sem sessão de release health (ver integracoesDoCliente). O replay DE
// ERRO continua, porque é o que explica o stack trace — e o replayIntegration()
// sem argumentos já aplica maskAllText/blockAllMedia.
tracesSampleRate: community ? 0 : 1,
enableLogs: true,
replaysSessionSampleRate: community ? 0 : 0.1,
replaysOnErrorSampleRate: 1.0,
sendDefaultPii: false,
...sentryScrubHooks,
});
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;