Logging em aplicações JavaScript: melhores práticas
Logging não é apenas registrar eventos em um arquivo. Em aplicações Node.js modernas, é uma ferramenta poderosa de monitoramento, depuração e auditoria. Sem logging de qualidade, encontrar a causa de uma queda no servidor de produção se torna um jogo de adivinhação. Neste artigo, vamos analisar as melhores práticas de logging em JavaScript (Node.js) que ajudarão você a construir sistemas confiáveis e sustentáveis.
Por que o console.log padrão é uma má prática
Muitos desenvolvedores iniciantes usam console.log para depuração. No entanto, em produção, isso leva a problemas:
- Falta de níveis: você não consegue separar mensagens informativas de erros críticos.
- Sem estrutura: a saída no console é apenas uma string, difícil de fazer parse.
- I/O bloqueante: a escrita síncrona pode desacelerar a aplicação.
- Falta de rotação: os arquivos de log crescerão infinitamente.
Em vez disso, use bibliotecas especializadas, como winston ou pino. Elas fornecem configuração flexível de transports (saída para arquivo, console, serviços externos) e escrita assíncrona.
Escolha da biblioteca de logging: winston vs pino
No mercado Node.js, existem dois líderes claros. Vamos analisar suas características:
Winston
A biblioteca mais popular. Suporta múltiplos transports (arquivo, HTTP, MongoDB, Elasticsearch) e formatos customizados. Ideal para aplicações monolíticas.
const winston = require('winston');
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), new winston.transports.Console({ format: winston.format.simple() }) ]});
logger.info('Servidor iniciado na porta 3000');logger.error('Erro de conexão com o BD', { db: 'mongo', code: 500 });Pino
Biblioteca ultrarrápida, 5 a 10 vezes mais rápida que winston. Ideal para sistemas de alta carga e microsserviços. Por padrão, gera saída em JSON.
const pino = require('pino');
const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: { target: 'pino/file', options: { destination: './app.log' } }});
logger.info({ user: 'admin' }, 'Usuário entrou no sistema');logger.error(new Error('Timeout'), 'Erro na requisição');Dica: para a maioria dos projetos, escolha winston por sua flexibilidade. Se a performance for crítica, use pino.
Níveis de logging: como não se afogar em mensagens
O uso correto dos níveis é a base de um bom logging. Em Node.js, adota-se a seguinte hierarquia (do menos para o mais crítico):
- trace / silly — informação de depuração extremamente detalhada (geralmente desativada em produção).
- debug — mensagens de depuração para desenvolvedores.
- info — confirmação de operação normal (requisições, inicialização de serviços).
- warn — situação anormal, mas a aplicação continua funcionando (ex.: limite de requisições excedido).
- error — erro que requer atenção (falha de requisição, falha de serviço externo).
- fatal — erro crítico, após o qual a aplicação não pode continuar funcionando.
Configure o nível de logging através de variáveis de ambiente:
const LOG_LEVEL = process.env.LOG_LEVEL || 'info';const logger = winston.createLogger({ level: LOG_LEVEL });Em desenvolvimento, use debug; em produção, use info.