Logs em Node.js para produção
Logs são o principal instrumento de diagnóstico em produção. Em Node.js, console.log está longe do ideal para produção — use Winston para logs estruturados em JSON com níveis de severidade, contexto por requisição e integração nativa com Docker. Com logs bem estruturados, você encontra erros em segundos em vez de minutos.
Configurar Winston para produção
Winston é o logger mais popular para Node.js. Configure para emitir JSON em produção e formato legível em desenvolvimento:
// src/logger.ts
import winston from 'winston'
const isProduction = process.env.NODE_ENV === 'production'
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || (isProduction ? 'info' : 'debug'),
format: isProduction
? winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json() // JSON em produção
)
: winston.format.combine(
winston.format.colorize(),
winston.format.simple() // legível em desenvolvimento
),
transports: [
new winston.transports.Console(), // sempre stdout/stderr
],
// Capturar exceções e rejections não tratadas
exceptionHandlers: [new winston.transports.Console()],
rejectionHandlers: [new winston.transports.Console()],
})
// Uso:
logger.info('Servidor iniciado', { port: 3000 })
logger.error('Falha ao conectar', { error: err.message, stack: err.stack })Logs por requisição HTTP com Express
Adicione contexto de request_id, método, rota e duração em cada log de requisição:
// src/middleware/request-logger.ts
import { Request, Response, NextFunction } from 'express'
import { randomUUID } from 'crypto'
import { logger } from '../logger'
export function requestLogger(req: Request, res: Response, next: NextFunction) {
const requestId = randomUUID()
const start = Date.now()
// Adicionar request_id ao request para uso em logs subsequentes
req.requestId = requestId
res.on('finish', () => {
const duration = Date.now() - start
logger.info('HTTP request', {
request_id: requestId,
method: req.method,
path: req.path,
status: res.statusCode,
duration_ms: duration,
user_agent: req.headers['user-agent'],
ip: req.ip,
})
})
next()
}
// app.ts
app.use(requestLogger)
// Output em produção (JSON):
// {"level":"info","message":"HTTP request","method":"GET","path":"/users","status":200,"duration_ms":23}Capturar erros não tratados
Exceções e rejections não capturadas são os erros mais difíceis de debugar em produção — certifique-se de logá-los antes do processo encerrar:
// src/index.ts
import { logger } from './logger'
// Exceções síncronas não capturadas
process.on('uncaughtException', (error: Error) => {
logger.error('Uncaught exception — processo será encerrado', {
error: error.message,
stack: error.stack,
})
// Dar tempo para o logger fazer flush antes de encerrar
setTimeout(() => process.exit(1), 1000)
})
// Promises rejeitadas sem .catch()
process.on('unhandledRejection', (reason: unknown) => {
logger.error('Unhandled promise rejection', {
reason: reason instanceof Error ? reason.message : String(reason),
stack: reason instanceof Error ? reason.stack : undefined,
})
})
// Sinal de encerramento gracioso (SIGTERM do Docker stop)
process.on('SIGTERM', () => {
logger.info('SIGTERM recebido — encerrando servidor...')
server.close(() => {
logger.info('Servidor encerrado com sucesso')
process.exit(0)
})
})Filtrar e buscar logs no Docker
Comandos para trabalhar com logs de containers em produção:
# Ver últimas 100 linhas
docker compose logs app --tail=100
# Seguir em tempo real
docker compose logs app -f
# Filtrar apenas erros (logs em JSON)
docker compose logs app 2>&1 | grep '"level":"error"'
# Buscar por request_id específico
docker compose logs app 2>&1 | grep '"request_id":"abc-123"'
# Filtrar por período (requer Docker 20.10+)
docker logs --since="2026-06-07T10:00:00" --until="2026-06-07T11:00:00" nome_container
# Parsear logs JSON com jq (apt install jq)
docker compose logs app 2>&1 | grep '^{' | jq 'select(.level == "error") | {time: .timestamp, msg: .message, path: .path}'Evitar os erros mais comuns de logging
Práticas que causam problemas em produção e como evitá-las:
// ❌ NÃO faça em produção:
// 1. Logar objetos sensíveis
logger.info('Usuário autenticado', { user, password }) // expõe senha
// 2. console.log em vez de logger
console.log('debug:', req.body) // sem nível, sem JSON, sem timestamp
// 3. Strings de erro sem stack trace
logger.error('Erro: ' + err.message) // perde o stack trace
// 4. Logar a cada tick/iteração
setInterval(() => logger.debug('tick'), 100) // ~35k logs/hora
// ✅ FAÇA:
// 1. Sanitizar antes de logar
const { password, token, ...safeUser } = user
logger.info('Usuário autenticado', { user: safeUser })
// 2. Sempre usar o logger configurado
logger.debug('Request body', { body: req.body })
// 3. Passar o objeto Error completo
logger.error('Falha na operação', { error: err })
// Winston extrai message e stack automaticamente com format.errors()
// 4. Logar apenas eventos significativos
// A cada N iterações, ou apenas quando o estado muda$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.