Guia completo de monitoramento em produção para Next.js: Sentry, logs e alertas

Sexta-feira, 21h17, o celular vibrou.
O grupo no WeChat já estava em pânico: “a página de pagamento não abre”, “meu pedido acabou de falhar”, “ficou tudo em branco”. Rodei localmente. Tudo perfeito. Olhei os logs do servidor e só havia uma linha: “Internal Server Error”. O usuário dizia que, depois de clicar no botão de pagamento, a página travava, mas eu não conseguia reproduzir de jeito nenhum.
Passei aquele fim de semana vasculhando logs de deploy da Vercel, publicando versões de teste de novo e, no fim, descobri que o SDK de pagamento de terceiros às vezes dava timeout em produção. O processo inteiro parecia procurar uma agulha no escuro.
Seu app Next.js pode rodar liso no ambiente de desenvolvimento, mas virar uma caixa de surpresas depois do deploy: SSR que às vezes retorna 500, funções de borda que falham sem explicação, APIs cujo tempo de resposta dispara sem você saber onde está o gargalo.
A causa é simples: falta um sistema completo de monitoramento em produção. Este guia mostra, passo a passo, como montar monitoramento para Next.js: do rastreamento de erros com Sentry aos logs estruturados, do monitoramento de desempenho à configuração de alertas. Não é uma pilha de teoria; são configurações e trechos de código que você consegue copiar e adaptar. Depois dele, no próximo problema em produção, você tende a saber antes do usuário.
Por que o Next.js precisa de uma estratégia própria de monitoramento
A característica de “três ambientes” do Next.js
Uma aplicação frontend tradicional roda só no navegador, e os erros aparecem diretamente no console do DevTools. Com Next.js é diferente: o mesmo app roda ao mesmo tempo em três lugares completamente distintos:
- Cliente (Browser): componentes React no navegador do usuário
- Servidor (Node.js): renderização SSR, API Routes e Server Actions
- Rede de borda (Edge Runtime): middleware e funções de borda
Uma funcionalidade de pagamento pode envolver validação de formulário no cliente -> autenticação no middleware -> chamada via Server Action -> API Route consultando o banco de dados -> resposta renderizada no cliente. Se qualquer etapa falhar, o monitoramento tradicional do navegador não enxerga o quadro completo.
No ano passado encontrei um bug bem estranho: usuários relatavam que “a página carregava muito devagar e depois mostrava 500”. O painel Network do navegador confirmava que a requisição estava lenta, mas não dizia exatamente onde. Só depois de integrar o rastreamento distribuído do Sentry descobrimos que, durante a renderização no servidor, uma API de terceiros tinha saído dos 200ms habituais para 8 segundos. Monitoramento só no cliente nunca encontraria esse tipo de problema.
O “efeito caixa-preta” do SSR
Quando a renderização no servidor falha, o usuário geralmente vê apenas uma página 500 vazia. Sem stack trace, sem contexto, sem nada.
Pior ainda são os erros de Hydration. Talvez você já tenha visto este aviso:
Warning: Expected server HTML to contain a matching <div> in <div>
Esse tipo de erro pode ser discreto no ambiente de desenvolvimento, mas em produção pode quebrar toda a interação da página. Sem um sistema de monitoramento, você descobre passivamente quando alguém diz que “a página não responde ao clique”.
Segundo dados da Vercel, erros relacionados a SSR representam cerca de 35% dos problemas em produção com Next.js. Isso fala só de erros, sem contar desempenho: por exemplo, quando o SSR de um componente começa a demorar mais, o usuário sente que a página “ficou lenta”, mas você não sabe onde está o gargalo.
Os quatro pilares de um monitoramento completo
Uma estratégia confiável de monitoramento para Next.js precisa cobrir estes pontos:
Rastreamento de erros
Não basta capturar exceções. Você precisa saber quem disparou o erro (informações do usuário), em qual ambiente (dispositivo, navegador, rede), quais ações foram feitas (breadcrumbs) e quais parâmetros de requisição estavam relacionados.
Monitoramento de desempenho
O LCP (Largest Contentful Paint) passou de 2,5 segundos? A resposta de uma API ficou mais lenta? Qual consulta ao banco travou a requisição inteira?
Gerenciamento de logs
Logs estruturados que permitem buscar rapidamente por tempo, usuário e ID da requisição. No desenvolvimento, saída bonita para depurar; em produção, envio para uma plataforma de logs para análise centralizada.
Configuração de alertas
Se a taxa de erro passar de um limite, avise o time imediatamente. Se aparecer um novo tipo de erro, envie para o Slack. Se houver regressão de desempenho, dispare um alerta automático.
Com esses quatro pilares, quando algo dá errado em produção você deixa de operar no escuro. Vamos atacar um por um.
Integração prática com Sentry: da instalação à configuração avançada
Integração rápida em 5 minutos
O suporte do Sentry a Next.js já está bem maduro, e o assistente oficial faz a maior parte da configuração. Na prática, leva mesmo uns 5 minutos:
# Instalar o SDK
npm install @sentry/nextjs
# Rodar o assistente de configuração
npx @sentry/wizard@latest -i nextjs
O assistente pergunta algumas coisas, como o DSN do projeto no Sentry e se você quer fazer upload de Source Maps, e depois cria automaticamente três arquivos:
sentry.client.config.ts- ambiente do navegadorsentry.server.config.ts- servidor Node.jssentry.edge.config.ts- Edge Runtime
Ele também modifica o next.config.js e adiciona o plugin webpack do Sentry. Depois de rodar o assistente, o monitoramento básico já está conectado.
Mas isso é só o começo. Produção precisa de uma configuração mais cuidadosa.
Pontos de captura de erro no App Router
Se você usa App Router, há alguns pontos que merecem atenção especial:
Tratamento global de erros
Crie app/global-error.tsx. Essa é a última linha de defesa do App Router:
'use client';
import * as Sentry from '@sentry/nextjs';
import { useEffect } from 'react';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Envia para o Sentry
Sentry.captureException(error);
}, [error]);
return (
<html>
<body>
<div style={{ padding: '2rem', textAlign: 'center' }}>
<h2>Algo deu errado</h2>
<p>Já registramos este erro e vamos corrigir o quanto antes</p>
<button onClick={() => reset()}>Tentar novamente</button>
</div>
</body>
</html>
);
}
Captura de erros em Server Actions
Server Actions são um dos recursos mais fortes do App Router, mas o tratamento de erros costuma ser esquecido:
'use server';
import * as Sentry from '@sentry/nextjs';
export async function createOrder(formData: FormData) {
return await Sentry.withServerActionInstrumentation(
'createOrder', // nome da action, aparece no Sentry
{
recordResponse: true, // registra dados da resposta
},
async () => {
// Sua lógica de negócio
const productId = formData.get('productId');
const order = await db.order.create({
data: { productId, userId: getCurrentUserId() },
});
return order;
}
);
}
Depois desse wrapper, qualquer erro dentro da Server Action é reportado automaticamente, e o tempo de execução também fica rastreável.
Otimização para produção
Depois de integrar o Sentry, a primeira fatura do mês pode assustar, porque a configuração padrão reporta todos os eventos. Ajuste a taxa de amostragem:
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Taxa de amostragem do rastreamento de desempenho
// Desenvolvimento 100%, produção 10% (ajuste conforme o tráfego)
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Amostragem do Session Replay
replaysSessionSampleRate: 0.1, // grava 10% das sessões normais
replaysOnErrorSampleRate: 1.0, // grava 100% das sessões com erro
// Identificação do ambiente
environment: process.env.NEXT_PUBLIC_VERCEL_ENV || 'development',
// Ignorar erros específicos
ignoreErrors: [
// Erro injetado por extensões do navegador
'ResizeObserver loop limit exceeded',
// Erros de scripts de terceiros
/chrome-extension/,
/^Non-Error promise rejection/,
],
});
Guia para definir tracesSampleRate:
- UV diário < 10 mil: 0.2 - 0.5
- UV diário entre 10 mil e 100 mil: 0.1 - 0.2
- UV diário > 100 mil: 0.05 - 0.1
Em um projeto com cerca de 30 mil UV diários, eu uso 0.15. Isso consome mais ou menos 60% da cota mensal do Sentry: cobertura suficiente sem estourar o orçamento.
Source Maps: depurável sem expor o código
O JavaScript em produção normalmente fica minificado e ofuscado. A stack de erro acaba parecendo isto:
at r.render (app.js:1:23456)
Não dá para entender nada. Source Maps mapeiam o código ofuscado de volta para o código original, mas expor Source Maps publicamente pode vazar o código-fonte.
A abordagem do Sentry é: fazer upload dos Source Maps para os servidores do Sentry. O navegador do usuário não acessa esses arquivos; só o próprio Sentry usa para reconstruir a stack.
Configure isso no CI/CD. Exemplo com GitHub Actions:
# .github/workflows/deploy.yml
- name: Upload Source Maps to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: your-org
SENTRY_PROJECT: your-project
run: npm run build
O plugin do Sentry no next.config.js cuida do upload automaticamente. Lembre-se de adicionar SENTRY_AUTH_TOKEN ao GitHub Secrets e nunca commitar esse token no repositório.
Recursos avançados: Session Replay e rastreamento distribuído
Session Replay é um dos recursos de que mais gosto: ele reproduz as ações do usuário, quase como assistir a uma gravação do problema.
Uma vez um usuário disse que “não conseguia clicar no botão de pagamento”. Ao ver o Session Replay, descobri que ele usava um iPad na horizontal e que o botão ficava coberto pelo teclado virtual. Só logs de erro nunca mostrariam isso.
Ativar é simples. Adicione isto à configuração do cliente:
import * as Sentry from '@sentry/nextjs';
import { Replay } from '@sentry/nextjs';
Sentry.init({
integrations: [
new Replay({
maskAllText: false, // ocultar todos os textos?
blockAllMedia: true, // bloquear todas as mídias?
maskAllInputs: true, // ocultar inputs de formulário para evitar vazamento de dados sensíveis
}),
],
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
Rastreamento distribuído acompanha o ciclo de vida completo de uma requisição. O usuário clica no botão -> o frontend envia a requisição -> a API Route consulta o banco -> o resultado volta para renderização no frontend. Você enxerga o tempo gasto em cada etapa.
A configuração também não é complicada: garanta que frontend e backend usem uma configuração compatível de Sentry.init. O SDK do Sentry passa automaticamente o cabeçalho sentry-trace nas requisições.
Contexto personalizado: deixe o erro mais útil
Por padrão, o Sentry só sabe que “um erro aconteceu”. Com contexto personalizado, o relatório fica muito mais valioso:
import * as Sentry from '@sentry/nextjs';
// Define informações do usuário
Sentry.setUser({
id: user.id,
email: user.email,
username: user.username,
// Não coloque senha ou outros dados sensíveis aqui!
});
// Adiciona contexto de negócio
Sentry.setContext('purchase', {
orderId: '12345',
amount: 99.99,
paymentMethod: 'credit_card',
});
// Adiciona tags para facilitar filtros
Sentry.setTag('feature', 'checkout');
Sentry.setTag('ab_test', 'variant_b');
Assim, quando o erro acontece, você sabe imediatamente qual usuário foi afetado e em qual cenário de negócio.
Caso real: uma vez percebemos que um erro de pagamento aparecia com frequência acima do esperado. Ao olhar o contexto no Sentry, todos os eventos vinham de usuários com ab_test: variant_b. Localizamos um bug na nova versão do fluxo de pagamento do teste A/B e desativamos a variante na hora, evitando uma perda maior.
Gerenciamento de logs: faça os logs trabalharem por você
console.log deixou de ser suficiente
No começo eu também espalhava console.log por todo lado. Na depuração local parece ótimo, mas em produção isso trava rápido:
- Não dá para filtrar: encontrar a requisição de um usuário no meio de 100 mil linhas de log? Boa sorte.
- Não dá para agregar: quer saber quantas consultas ao banco demoraram mais de 1 segundo na última hora? Não há estatística.
- Não dá para alertar: apareceu “Payment failed” nos logs? Ninguém fica sabendo.
Logs estruturados resolvem esses problemas. Em vez de imprimir uma string solta, você emite objetos JSON. Cada log carrega timestamp, nível, ID da requisição, ID do usuário e outros metadados. Depois, qualquer campo pode ser usado para busca e agregação.
Pino vs Winston: qual escolher
No ecossistema Node.js, as duas grandes bibliotecas de log são Pino e Winston. Cada uma tem seu espaço:
| Característica | Pino | Winston |
|---|---|---|
| Desempenho | Muito rápido; logs assíncronos com custo quase nulo | Um pouco mais lento, mas ainda suficiente |
| Facilidade de uso | Configuração simples, funciona bem logo de cara | Mais recursos e bom ecossistema de plugins |
| Extensibilidade | Expande via Transport | Inclui vários Transports |
| Comunidade | Recomendado na documentação do Next.js | Biblioteca tradicional, documentação muito completa |
Minha sugestão:
- Cenários de alta concorrência (QPS > 1000): escolha Pino, a vantagem de desempenho é clara
- Processamento de logs mais complexo (vários formatos e destinos): escolha Winston
- Não sabe qual usar: escolha Pino; até a documentação do Next.js costuma usá-lo
Configuração prática com Pino
Instale primeiro:
npm install pino
npm install pino-pretty --save-dev # saída mais legível no desenvolvimento
Crie um logger global:
// lib/logger.ts
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
// Formata o nível do log
formatters: {
level: (label) => ({ level: label.toUpperCase() }),
},
// No desenvolvimento, usa pino-pretty para melhorar a leitura
transport: process.env.NODE_ENV === 'development'
? {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss',
ignore: 'pid,hostname',
},
}
: undefined,
});
export { logger };
No desenvolvimento, a saída fica colorida e fácil de ler. Em produção, ela sai como JSON para ser processada por plataformas de log.
Uso em uma API Route
O ponto central é dar a cada requisição um correlationId. Esse ID conecta todos os logs relacionados à mesma requisição:
// app/api/products/route.ts
import { logger } from '@/lib/logger';
import { randomUUID } from 'crypto';
export async function GET(request: Request) {
// Gera um ID único para a requisição
const correlationId = request.headers.get('x-correlation-id') || randomUUID();
// Cria um logger filho que já carrega o correlationId
const log = logger.child({ correlationId });
try {
log.info({ url: request.url }, 'Processando requisição de produtos');
const products = await db.product.findMany();
log.info({ count: products.length }, 'Produtos buscados com sucesso');
return Response.json(products);
} catch (error) {
log.error({ error: error.message, stack: error.stack }, 'Falha ao buscar produtos');
throw error;
}
}
Assim, todos os logs com o mesmo correlationId podem ser vinculados. Na hora de investigar, basta buscar esse ID e ver a cadeia completa da requisição.
Boas práticas de níveis de log
Log de menos não ajuda; log demais esconde o que importa. Eu costumo separar assim:
ERROR - problema que exige ação imediata
- Falha de conexão com banco de dados
- Falha ao chamar gateway de pagamento
- Exceção em lógica crítica de negócio
log.error({ error, userId, orderId }, 'Falha ao processar pagamento');
WARN - situação anormal, mas recuperável
- Chamada de API que só deu certo depois de retry
- Ativação de lógica de fallback
- Limite de cota próximo
log.warn({ retryCount: 3 }, 'Retry de API externa concluído com sucesso');
INFO - pontos importantes do negócio
- Login/logout de usuário
- Criação/conclusão de pedido
- Alteração importante de configuração
log.info({ userId, ip }, 'Usuário fez login');
DEBUG - informações detalhadas de depuração
- Parâmetros e retorno de funções
- Estados intermediários
- Medições de tempo
log.debug({ params }, 'Chamando API externa');
Em produção, use INFO como padrão. Quando houver problema, suba temporariamente para DEBUG para investigar.
Agregação e análise de logs
No desenvolvimento local, pino-pretty basta. Em produção, você precisa de uma plataforma de logs. Algumas opções comuns:
Vercel Logs
Se o deploy está na Vercel, o sistema de logs vem embutido e sem configuração. O limite é que ele guarda só 7 dias e a busca é simples.
Datadog
Solução corporativa com APM, logs e monitoramento no mesmo pacote. Configuração:
import { datadogLogs } from '@datadog/browser-logs';
datadogLogs.init({
clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN,
site: 'datadoghq.com',
forwardErrorsToLogs: true,
sampleRate: 100,
});
Logtail/BetterStack
Boa relação custo-benefício, com foco em análise de logs. Suporta busca em tempo real, regras de alerta e painéis personalizados.
Em projetos pessoais eu uso Logtail: é simples de configurar e oferece 1 GB de logs gratuitos por mês. Em projetos de time, uso Datadog. É caro, mas muito completo.
Campos importantes em um log
Um bom log deve incluir estes campos:
{
"timestamp": "2025-12-20T15:00:06.123Z", // timestamp
"level": "INFO", // nível do log
"correlationId": "abc-123-def", // ID de correlação da requisição
"userId": "user_456", // ID do usuário
"action": "create_order", // ação de negócio
"duration": 234, // duração (ms)
"status": "success", // status
"metadata": { // metadados adicionais
"orderId": "order_789",
"amount": 99.99
}
}
Esse tipo de log responde à pergunta: quem fez o quê, quando, e com qual resultado.
Monitoramento de desempenho: otimização guiada por dados
Core Web Vitals: as métricas que o Google leva em conta
O Google usa Core Web Vitals como fator de ranqueamento, então você também deveria acompanhar:
- LCP (Largest Contentful Paint): tempo do maior conteúdo visível; ideal < 2,5s
- FID (First Input Delay) / INP (Interaction to Next Paint): tempo de resposta da interação; < 100ms / < 200ms
- CLS (Cumulative Layout Shift): deslocamento cumulativo de layout; < 0,1
O Next.js já inclui envio de Web Vitals. Basta adicionar algumas linhas em app/layout.tsx:
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function WebVitalsReporter() {
useReportWebVitals((metric) => {
// Envia para o Sentry
if (window.Sentry) {
window.Sentry.captureMessage(`Web Vital: ${metric.name}`, {
level: 'info',
tags: {
web_vital: metric.name,
},
contexts: {
web_vitals: {
value: metric.value,
rating: metric.rating,
},
},
});
}
// Ou envia para sua plataforma de analytics
fetch('/api/analytics/web-vitals', {
method: 'POST',
body: JSON.stringify(metric),
});
});
return null;
}
Depois, importe no layout raiz:
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<body>
<WebVitalsReporter />
{children}
</body>
</html>
);
}
Rastreamento de desempenho em APIs
Desempenho do frontend é só metade da história. Se a API no backend estiver lenta, o usuário também espera. O Performance Monitoring do Sentry rastreia cada requisição de API:
// app/api/products/[id]/route.ts
import * as Sentry from '@sentry/nextjs';
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
// Cria uma transaction
return await Sentry.startSpan(
{
op: 'api.request',
name: 'GET /api/products/[id]',
},
async () => {
// Rastreia a consulta ao banco
const product = await Sentry.startSpan(
{
op: 'db.query',
name: 'Fetch product from database',
},
async () => {
return await db.product.findUnique({
where: { id: params.id },
include: { reviews: true },
});
}
);
if (!product) {
return Response.json({ error: 'Not found' }, { status: 404 });
}
// Rastreia a chamada a uma API externa
const pricing = await Sentry.startSpan(
{
op: 'http.client',
name: 'Fetch pricing from external API',
},
async () => {
const res = await fetch(`https://pricing-api.com/product/${params.id}`);
return res.json();
}
);
return Response.json({ ...product, pricing });
}
);
}
No painel do Sentry, você vê algo assim:
- Requisição completa: 450ms
- Consulta ao banco: 120ms
- Chamada à API externa: 300ms
- Outras lógicas: 30ms
Fica visível de cara: o gargalo está na API externa.
Alerta de consultas lentas
Banco de dados costuma ser gargalo de desempenho. Você pode adicionar monitoramento no middleware do Prisma:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
import { logger } from './logger';
const prisma = new PrismaClient();
// Monitora consultas lentas
prisma.$use(async (params, next) => {
const before = Date.now();
const result = await next(params);
const after = Date.now();
const duration = after - before;
// Consultas acima de 1 segundo são registradas como WARN
if (duration > 1000) {
logger.warn({
model: params.model,
action: params.action,
duration,
args: params.args,
}, 'Consulta lenta ao banco detectada');
// Também envia para o Sentry
Sentry.captureMessage('Consulta lenta ao banco', {
level: 'warning',
tags: { model: params.model, action: params.action },
extra: { duration, args: params.args },
});
}
return result;
});
export { prisma };
Com isso, nenhuma consulta lenta passa despercebida.
Monitoramento real de usuários vs monitoramento sintético
Monitoramento real de usuários (RUM)
Coleta dados de desempenho de usuários reais com Sentry, Datadog e ferramentas parecidas. A vantagem é refletir cenários reais, com redes e dispositivos diferentes. A desvantagem é ser passivo: você só descobre depois que o problema aconteceu.
Monitoramento sintético (Synthetic Monitoring)
Usa Checkly, Pingdom e similares para simular acessos periódicos ao seu site a partir de diferentes regiões. A vantagem é encontrar problemas de forma ativa. A desvantagem é não cobrir todos os cenários de usuário.
O melhor é combinar os dois:
- RUM acompanha métricas de experiência do usuário
- Monitoramento sintético cuida de disponibilidade e fluxos críticos, como login e pagamento
Eu uso Checkly para acessar a página inicial e a API de login a cada 5 minutos a partir de 5 regiões. Se qualquer uma delas falha ou passa do timeout, o alerta chega na hora.
Configuração de alertas: descubra o problema no primeiro minuto
Integração com Slack: avise o time rapidamente
A integração do Sentry com Slack é simples: Settings -> Integrations -> Slack. Depois da autorização, escolha o canal que receberá as notificações.
Mas a configuração padrão pode enviar todos os erros e virar ruído rapidamente. Você precisa configurar regras de alerta:
Nas configurações do projeto no Sentry:
- Alerts -> Create Alert Rule
- Escolha a condição de disparo:
- Alerta por taxa de erro: “mais de 50 erros em 10 minutos”
- Alerta de erro novo: “primeira ocorrência do erro envia notificação imediata”
- Regressão de desempenho: “P95 de resposta da API > 1s”
- Escolha a ação: Send a notification via Slack
Formato da mensagem no Slack:
🚨 Production Error Spike
Project: my-nextjs-app
Environment: production
Error: TypeError: Cannot read property 'id' of undefined
Events: 127 events in 10 minutes
View in Sentry: https://sentry.io/...
O link leva direto ao Sentry, com detalhes e stack trace.
Classificação de alertas: evite fadiga
Nem todo problema tem a mesma urgência. Minha estratégia de classificação:
P0 - Crítico (ação imediata)
- Serviço totalmente indisponível
- Falha no pagamento
- Conexão com banco de dados perdida
Disparo: telefone (PagerDuty) + Slack @channel
P1 - Importante (resposta em até 1 hora)
- Função central com erro
- Pico na taxa de erros (mais de 100 em 10 minutos)
- P95 de resposta da API > 3s
Disparo: mensagem no canal de desenvolvimento no Slack
P2 - Normal (tratar em horário útil)
- Erro de pequeno alcance (< 10 vezes/hora)
- Função não crítica com problema
- Erro em script de terceiros
Disparo: resumo diário por e-mail
Exemplo de configuração (Sentry Alert Rule):
// Alerta P0: falha de pagamento
{
conditions: [
{ type: 'event.tag', key: 'feature', value: 'payment' },
{ type: 'event.level', value: 'error' }
],
frequency: 'every event', // notifica em todos os eventos
actions: [
{ type: 'slack', channel: '#critical-alerts', mention: '@channel' },
{ type: 'pagerduty', service: 'payments' }
]
}
// Alerta P1: pico na taxa de erros
{
conditions: [
{ type: 'event.count', value: 100, interval: '10m' }
],
frequency: 'once per issue', // notifica uma vez por problema
actions: [
{ type: 'slack', channel: '#alerts-dev' }
]
}
Técnicas para reduzir ruído de alerta
Logo depois de integrar monitoramento, você pode ser inundado por alertas. Algumas técnicas ajudam:
1. Ignore problemas conhecidos
Erros de hot reload no desenvolvimento e exceções de scripts de terceiros podem ser filtrados:
// sentry.client.config.ts
Sentry.init({
ignoreErrors: [
// Erros de extensões do navegador
/chrome-extension/,
/moz-extension/,
// Scripts de terceiros
/google-analytics/,
// Hot reload no desenvolvimento
/HMR/,
],
denyUrls: [
// Ignora erros de scripts de domínios específicos
/extensions\//i,
/^chrome:\/\//i,
],
});
2. Agrupe alertas repetidos
Notifique o mesmo erro só uma vez dentro de 10 minutos, em vez de inundar o canal. O “Issue Grouping” do Sentry já agrupa automaticamente erros parecidos.
3. Defina períodos de silêncio
Durante um deploy, pode haver um pico breve de erros. Você pode usar “Mute for 10 minutes”.
4. Use fingerprint
Personalize regras de agrupamento para juntar erros com a mesma causa raiz:
Sentry.captureException(error, {
fingerprint: ['database-connection-error', databaseName],
});
Assim, erros de conexão em bancos diferentes ficam separados, o que facilita a localização do problema.
Caso prático: implantando uma estratégia completa de monitoramento
Arquitetura de monitoramento para e-commerce
No ano passado, ajudei um e-commerce a reformular o monitoramento. Esta foi a solução completa:
Contexto:
- UV diário médio de 80 mil
- QPS acima de 3000 nos picos
- Principais problemas: falhas ocasionais no pagamento e página inicial lenta
Arquitetura de monitoramento:
┌─────────────┐
│ Next.js │
│ frontend/SSR │
└──────┬──────┘
│
├─ Sentry (erros + desempenho)
├─ Pino (logs estruturados) → Datadog
├─ Web Vitals → Sentry
└─ Checkly (monitoramento sintético)
Configurações principais:
- Rastreamento de comportamento do usuário
// lib/tracking.ts
import * as Sentry from '@sentry/nextjs';
export function trackCheckoutStep(step: string, data: any) {
Sentry.addBreadcrumb({
category: 'checkout',
message: `Etapa do checkout: ${step}`,
data,
level: 'info',
});
}
// Chame durante o fluxo de compra
trackCheckoutStep('add_to_cart', { productId, price });
trackCheckoutStep('proceed_to_payment', { cartTotal });
trackCheckoutStep('payment_submitted', { method: 'credit_card' });
Quando o pagamento falha, você consegue ver o caminho completo do usuário até ali.
- Monitoramento de pagamento
// app/api/payment/route.ts
export async function POST(request: Request) {
const log = logger.child({ action: 'payment' });
try {
const result = await processPayment(data);
log.info({ orderId, amount, method }, 'Pagamento concluído com sucesso');
return Response.json({ success: true, orderId });
} catch (error) {
log.error({ error, orderId, userId }, 'Falha no pagamento');
// Alerta P0
Sentry.captureException(error, {
tags: { feature: 'payment', severity: 'critical' },
level: 'fatal',
});
return Response.json({ error: 'Payment failed' }, { status: 500 });
}
}
Qualquer falha de pagamento avisa o time imediatamente.
- Definição de linha de base de desempenho
Use o Sentry Performance Monitoring para criar a linha de base:
- LCP da página inicial < 2s
- LCP da página de produto < 2,5s
- P95 da API
/api/products< 500ms
Quando passar da linha de base, dispare alerta automático.
Resultado:
- Tempo médio de detecção de incidentes caiu de 40 minutos para 3 minutos
- Taxa de falha em pagamentos caiu de 0,8% para 0,2%
- LCP da página inicial, depois de otimizações, caiu de 3,2s para 1,8s
Checklist de monitoramento
Para fechar, aqui vai uma lista para revisar seu projeto:
**Monitoramento de erros**
- [ ] Sentry configurado e testado
- [ ] Source Maps enviados com sucesso
- [ ] global-error.tsx criado (App Router)
- [ ] Server Actions com tratamento de erro
- [ ] Regras de ignore configuradas para filtrar ruído
**Gerenciamento de logs**
- [ ] Biblioteca de logs integrada (Pino/Winston)
- [ ] Produção emitindo logs em JSON
- [ ] Logs incluem correlationId
- [ ] Nível de log definido corretamente (INFO em produção)
- [ ] Logs conectados a uma plataforma de agregação
**Monitoramento de desempenho**
- [ ] Envio de Web Vitals ativado
- [ ] Core Web Vitals dentro da meta (LCP<2,5s, INP<200ms, CLS<0,1)
- [ ] APIs críticas com rastreamento de desempenho
- [ ] Monitoramento de consultas lentas configurado
- [ ] Monitoramento sintético configurado (opcional)
**Configuração de alertas**
- [ ] Alertas por Slack/e-mail testados
- [ ] Regras de alerta classificadas por prioridade
- [ ] Regras para redução de ruído configuradas
- [ ] Todo o time conhece o fluxo de alerta
- [ ] Incidentes P0 têm responsáveis claros
**Melhoria contínua**
- [ ] Revisão semanal dos dados de monitoramento
- [ ] Análise de tendência de erros (quais erros estão aumentando)
- [ ] Detecção de regressão de desempenho (quais páginas ficaram lentas)
- [ ] Otimização periódica das regras de alerta
Conclusão
Monitoramento tira você do modo apagar incêndio e coloca a produção sob controle.
Recapitulando o sistema que montamos:
- Sentry cuida de rastreamento de erros e monitoramento de desempenho, além de poder reproduzir sessões de usuário
- Pino oferece logs estruturados e conecta toda a cadeia da requisição por correlationId
- Web Vitals acompanha métricas de experiência do usuário que impactam diretamente o SEO
- Alertas no Slack fazem o time saber do problema rapidamente, com classificação para evitar fadiga
Mais importante que a ferramenta é a mudança de mentalidade: monitoramento não é “bom ter”; é o airbag da produção. Você não espera o acidente para instalar o airbag. Do mesmo modo, não deveria esperar a produção quebrar para lembrar de monitoramento.
Comece hoje. Se seu projeto ainda não tem monitoramento:
- Neste fim de semana, reserve 2 horas para integrar o Sentry e configurar o rastreamento básico de erros
- Na semana seguinte, adicione logs estruturados e correlationId
- Na outra semana, configure alertas no Slack e linhas de base de desempenho
Não tente acertar tudo de uma vez. Coloque primeiro o monitoramento básico de erros para rodar e depois evolua aos poucos. Depois de cada incidente em produção, pergunte: “o monitoramento poderia ter detectado esse problema antes?” Melhorando continuamente, o monitoramento vira um dos apoios mais confiáveis do time.
Por fim, se este guia ajudou você, compartilhe com o time. Monitoramento é responsabilidade coletiva, não uma batalha individual.
Que seu app Next.js rode com estabilidade e sem quedas. Mas, como a realidade costuma discordar, monitoramento importa de verdade.
Fluxo completo para configurar monitoramento em produção no Next.js
Passo a passo completo da integração com Sentry até logs estruturados, monitoramento de desempenho e alertas
⏱️ Estimated time: 3 hr
- 1
Step 1: Integrar o rastreamento de erros com Sentry
Instalação:
```bash
npm install @sentry/nextjs
```
Inicialização:
```bash
npx @sentry/wizard@latest -i nextjs
```
Configuração do cliente:
```ts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Configuração do servidor:
```ts
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Pontos-chave:
• Configure cliente e servidor separadamente
• Use tracesSampleRate para controlar a taxa de amostragem
• Configure as variáveis de ambiente - 2
Step 2: Configurar logs estruturados
Use correlationId para vincular uma requisição:
```ts
// middleware.ts
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const correlationId = request.headers.get('x-correlation-id') || uuidv4()
const response = NextResponse.next()
response.headers.set('x-correlation-id', correlationId)
return response
}
```
Uso nos logs:
```ts
import { headers } from 'next/headers'
export async function handler() {
const headersList = headers()
const correlationId = headersList.get('x-correlation-id')
console.log({
correlationId,
message: 'Ação do usuário',
timestamp: new Date().toISOString(),
})
}
```
Pontos-chave:
• Gere um ID único para cada requisição
• Inclua correlationId em todos os logs
• Facilite o rastreamento de toda a cadeia da requisição - 3
Step 3: Configurar monitoramento de desempenho
Sentry APM:
```ts
Sentry.init({
tracesSampleRate: 1.0, // 100% de amostragem
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
})
```
Monitoramento de desempenho personalizado:
```ts
const transaction = Sentry.startTransaction({
op: 'http.server',
name: 'API Route',
})
try {
// Lógica de negócio
await processRequest()
} finally {
transaction.finish()
}
```
Pontos-chave:
• Use tracesSampleRate para controlar a taxa de amostragem
• Monitore o tempo de resposta das APIs
• Identifique gargalos de desempenho - 4
Step 4: Configurar alertas
Alertas no Sentry:
• Configure regras de alerta no Sentry Dashboard
• Defina limites de erro
• Configure canais de notificação (Slack, e-mail etc.)
Integração com Slack:
```ts
// Configuração no Sentry Dashboard
// Webhook URL: https://hooks.slack.com/services/...
```
Alertas por e-mail:
• Configure no Sentry Dashboard
• Defina destinatários
• Configure condições de alerta
Pontos-chave:
• Defina limites de alerta razoáveis
• Evite fadiga de alertas
• Responda aos alertas rapidamente
FAQ
Por que o Next.js precisa de uma estratégia própria de monitoramento?
O mesmo app roda em três lugares ao mesmo tempo:
• Cliente (Browser): componentes React no navegador do usuário
• Servidor (Node.js): renderização SSR, API Routes e Server Actions
• Rede de borda (Edge Runtime): middleware e funções de borda
O monitoramento frontend tradicional enxerga apenas erros do cliente. Ele não vê erros do servidor nem da borda.
Efeito de caixa-preta do SSR:
• Quando a renderização no servidor falha, o usuário só vê uma página 500
• Não há stack trace nem contexto
• É preciso usar rastreamento distribuído com Sentry para encontrar o problema
Caso real:
• O usuário relata: "a página carrega muito devagar e depois mostra 500"
• O painel Network do navegador mostra que a requisição está lenta, mas não mostra onde está a lentidão
• Depois de integrar o Sentry, ficou claro que uma chamada a uma API de terceiros no servidor tinha saído de 200ms para 8 segundos
Solução: você precisa de um sistema completo de monitoramento que cubra cliente, servidor e rede de borda.
Como integrar o Sentry?
```bash
npm install @sentry/nextjs
```
Inicialização:
```bash
npx @sentry/wizard@latest -i nextjs
```
Configuração do cliente:
```ts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Configuração do servidor:
```ts
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Pontos-chave:
• Configure cliente e servidor separadamente
• Use tracesSampleRate para controlar a taxa de amostragem
• Configure as variáveis de ambiente (NEXT_PUBLIC_SENTRY_DSN, SENTRY_DSN)
Como configurar logs estruturados?
Gere no middleware:
```ts
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const correlationId = request.headers.get('x-correlation-id') || uuidv4()
const response = NextResponse.next()
response.headers.set('x-correlation-id', correlationId)
return response
}
```
Use nos logs:
```ts
import { headers } from 'next/headers'
export async function handler() {
const headersList = headers()
const correlationId = headersList.get('x-correlation-id')
console.log({
correlationId,
message: 'Ação do usuário',
timestamp: new Date().toISOString(),
})
}
```
Vantagens:
• Cada requisição recebe um ID único
• Todos os logs carregam o correlationId
• Fica mais fácil rastrear toda a cadeia da requisição
• A localização do problema é mais rápida
Ponto-chave: gere no middleware e use nos logs para facilitar a correlação.
Como configurar monitoramento de desempenho?
```ts
Sentry.init({
tracesSampleRate: 1.0, // 100% de amostragem (em produção, recomenda-se 0.1)
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
})
```
Monitoramento de desempenho personalizado:
```ts
const transaction = Sentry.startTransaction({
op: 'http.server',
name: 'API Route',
})
try {
// Lógica de negócio
await processRequest()
} finally {
transaction.finish()
}
```
Métricas monitoradas:
• Tempo de resposta das APIs
• Tempo de consultas ao banco de dados
• Tempo de chamadas a APIs de terceiros
• Tempo de carregamento da página
Pontos-chave:
• Use tracesSampleRate para controlar a taxa de amostragem (em produção, recomenda-se 0.1)
• Monitore caminhos críticos
• Identifique gargalos de desempenho
Como configurar alertas?
• Configure regras de alerta no Sentry Dashboard
• Defina limites de erro (por exemplo: mais de 10 erros em 5 minutos)
• Configure canais de notificação (Slack, e-mail etc.)
Integração com Slack:
• Configure a Webhook URL no Sentry Dashboard
• Defina condições de alerta
• Teste o alerta
Alertas por e-mail:
• Configure no Sentry Dashboard
• Defina destinatários
• Configure condições de alerta
Sugestões de regras de alerta:
• Taxa de erros acima do limite
• Tempo de resposta acima do limite
• Tipos específicos de erro
• Surgimento de um erro novo
Pontos-chave:
• Defina limites de alerta razoáveis
• Evite fadiga de alertas
• Responda aos alertas rapidamente
Sugestão: coloque primeiro o monitoramento básico de erros para rodar e depois evolua aos poucos.
Quais são as melhores práticas de monitoramento?
1. Neste fim de semana, reserve 2 horas para integrar o Sentry e configurar o rastreamento básico de erros
2. Na semana seguinte, adicione logs estruturados e correlationId
3. Na outra semana, configure alertas no Slack e uma linha de base de desempenho
Não tente resolver tudo de uma vez. Coloque primeiro o monitoramento básico de erros para rodar e depois evolua aos poucos.
Melhoria contínua:
• Depois de cada incidente em produção, pergunte: "o monitoramento poderia ter detectado esse problema antes?"
• Ajuste os limites de alerta conforme a situação real
• Revise a configuração de monitoramento periodicamente
Métricas-chave:
• Taxa de erros
• Tempo de resposta
• Escopo de impacto sobre usuários
• Tempo de recuperação
Recomendações:
• Monitoramento é responsabilidade do time, não de uma pessoa só
• Compartilhe dados de monitoramento regularmente
• Melhore o sistema de monitoramento de forma contínua
Lembre-se: monitoramento não é uma tarefa única, mas um processo contínuo.
22 min de leitura · Publicado em: 20 dez 2025 · Atualizado em: 14 jul 2026
Guia completo Next.js
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Saindo da Vercel: guia completo para hospedar Next.js com Docker
Cansou das contas altas da Vercel? Este guia mostra como hospedar Next.js com Docker, incluindo configuração standalone, proxy reverso e correções para renderização por streaming, com economia anual de US$ 300 a US$ 500.
Parte 42 de 51
Próximo
Modo escuro no Next.js: guia completo com next-themes
Aprenda passo a passo a implementar modo escuro sem flashes no Next.js com next-themes, incluindo código completo, explicação do funcionamento e solução de problemas comuns.
Parte 44 de 51



Comentários
Entre com GitHub para comentar