Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Logs com Winston no Node.js

Atualizado em: 23 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Implementar logs com Winston no Node.js permite combinar formatos, níveis e múltiplos destinos em uma única configuração. A aplicação pode escrever JSON no console, separar erros em arquivo, encaminhar eventos a um serviço externo e aplicar metadados comuns como ambiente, versão e nome do serviço.

Essa flexibilidade é útil em sistemas com requisitos diferentes de transporte e retenção. Porém, cada destino adiciona custo e possibilidade de falha. Logging não deve bloquear o event loop, duplicar eventos, preencher o disco ou expor credenciais.

Neste guia, você aprenderá a criar loggers, configurar formatos, transports, níveis, exceções, request IDs, redaction, rotação, containers, testes e shutdown.

O que é Winston?

Winston é uma biblioteca de logging para Node.js baseada em formatos e transports. A documentação oficial do Winston apresenta API, níveis e destinos. O projeto logform contém os formatos utilizados pela biblioteca.

Para entender stdout e stderr, consulte Console no Node.js. Para uma alternativa focada em JSON de alto desempenho, veja Logs com Pino no Node.js.

Instalando

npm install winston

Logger básico

const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console()
  ]
});

logger.info('Aplicação iniciada');

Em produção, JSON facilita coleta e pesquisa.

Campos estruturados

logger.info('Pedido criado', {
  orderId: 842,
  amount: 159.9,
  currency: 'BRL'
});

Use propriedades separadas em vez de concatenar valores na mensagem.

Níveis padrão

Winston usa níveis npm por padrão:

  • error;
  • warn;
  • info;
  • http;
  • verbose;
  • debug;
  • silly.

O logger registra o nível configurado e os mais importantes.

Níveis personalizados

const levels = {
  fatal: 0,
  error: 1,
  warn: 2,
  info: 3,
  debug: 4,
  trace: 5
};

Se personalizar, alinhe cores, alertas e plataforma de coleta. Não crie níveis difíceis de interpretar.

Timestamp

format: winston.format.combine(
  winston.format.timestamp(),
  winston.format.json()
)

Use UTC e deixe a interface converter para o fuso do operador.

Metadados padrão

defaultMeta: {
  service: 'billing-api',
  version: process.env.APP_VERSION,
  environment: process.env.NODE_ENV
}

Campos estáveis permitem filtrar eventos de múltiplos serviços.

Erros com stack

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.errors({ stack: true }),
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [new winston.transports.Console()]
});

Ao capturar:

try {
  await chargeCustomer();
} catch (error) {
  logger.error('Falha na cobrança', {
    error,
    customerId
  });
  throw error;
}

Teste o formato real, porque serializar Error sem o formato adequado pode perder propriedades.

Formatos em desenvolvimento

const developmentFormat = winston.format.combine(
  winston.format.colorize(),
  winston.format.timestamp(),
  winston.format.printf(info => {
    return `${info.timestamp} ${info.level}: ${info.message}`;
  })
);

Não use colorize no JSON de produção.

Console transport

new winston.transports.Console({
  stderrLevels: ['error']
})

Em containers, console costuma ser o único transport necessário. A plataforma coleta stdout e stderr.

File transport

new winston.transports.File({
  filename: '/var/log/my-api/error.log',
  level: 'error'
})

Arquivos exigem permissões, espaço, rotação e backup apropriados.

Separando logs

transports: [
  new winston.transports.Console(),
  new winston.transports.File({
    filename: 'error.log',
    level: 'error'
  }),
  new winston.transports.File({
    filename: 'combined.log'
  })
]

O evento de erro pode aparecer em todos os destinos. Isso é esperado, mas pode gerar cobrança duplicada em plataformas externas.

Rotação de arquivos

O core do Winston não resolve toda política de rotação. Pacotes como winston-daily-rotate-file podem criar arquivos por data e tamanho.

Em containers, prefira stdout para não manter estado local.

Transport remoto

Enviar diretamente para rede aumenta acoplamento. Se o destino estiver lento, logs podem acumular ou falhar. Agentes externos geralmente são mais resilientes.

Falha do transport

logger.on('error', error => {
  process.stderr.write(
    `Logger failure: ${error.message}\n`
  );
});

Evite tentar registrar a falha do próprio logger com o mesmo logger, criando recursão.

Formato condicional

const format = process.env.NODE_ENV === 'production'
  ? winston.format.json()
  : developmentFormat;

Redaction

Winston não deve receber dados sensíveis. Crie um formato que remova campos:

const redact = winston.format(info => {
  if (info.password) info.password = '[REDACTED]';
  if (info.token) info.token = '[REDACTED]';

  if (info.headers?.authorization) {
    info.headers.authorization = '[REDACTED]';
  }

  return info;
});

Combine antes do JSON:

format: winston.format.combine(
  redact(),
  winston.format.timestamp(),
  winston.format.json()
)

Redaction não substitui seleção

É mais seguro construir um objeto pequeno do que registrar request, response ou configuração completos.

Request ID

logger.info('Requisição concluída', {
  requestId,
  method: req.method,
  route: req.route?.path,
  statusCode: res.statusCode,
  durationMs
});

Não use URL completa com query sensível.

Child logger

const requestLogger = logger.child({
  requestId,
  component: 'http'
});

O child inclui metadados em eventos subsequentes.

AsyncLocalStorage

Armazene um child logger por requisição:

const { AsyncLocalStorage } = require('node:async_hooks');
const storage = new AsyncLocalStorage();

function currentLogger() {
  return storage.getStore()?.logger || logger;
}

Consulte AsyncLocalStorage no Node.js.

Express middleware

app.use((req, res, next) => {
  const startedAt = performance.now();
  const requestId = crypto.randomUUID();

  res.setHeader('x-request-id', requestId);

  storage.run({
    logger: logger.child({ requestId })
  }, () => {
    res.on('finish', () => {
      currentLogger().info('HTTP concluído', {
        method: req.method,
        path: req.route?.path,
        statusCode: res.statusCode,
        durationMs: performance.now() - startedAt
      });
    });

    next();
  });
});

Health checks

Ignore ou amostre rotas muito frequentes para reduzir ruído. Consulte Health Checks no Node.js.

Exceptions handler

exceptionHandlers: [
  new winston.transports.File({
    filename: 'exceptions.log'
  })
]

Registrar não torna seguro continuar após exceção não capturada. Inicie shutdown.

Rejection handler

rejectionHandlers: [
  new winston.transports.File({
    filename: 'rejections.log'
  })
]

A política deve ser explícita. Promises rejeitadas sem captura podem indicar estado inconsistente.

Exit on error

A opção exitOnError controla comportamento após exceções tratadas pelo logger. Entenda o fluxo e não dependa de defaults para disponibilidade.

Graceful shutdown

Winston é uma stream de objetos. Para fechar:

await new Promise(resolve => {
  logger.on('finish', resolve);
  logger.end();
});

Nem todo transport possui a mesma semântica. Teste a configuração real.

Consulte Graceful Shutdown no Node.js.

Containers e systemd

Escreva JSON no console. Kubernetes, Docker ou journald adicionam metadados e fazem coleta.

Veja systemd com Node.js.

Correlação com traces

logger.info('Operação concluída', {
  traceId,
  spanId,
  orderId
});

Consulte OpenTelemetry no Node.js.

Auditoria

Logs operacionais e trilha de auditoria podem usar destinos e retenção diferentes. Auditoria deve registrar ator, ação, alvo, horário e resultado, com proteção contra alteração.

Cardinalidade e custo

Campos como requestId são úteis em logs, mas não devem necessariamente virar índices. Configure a plataforma para indexar apenas campos usados em busca.

Volume

Reduza:

  • logs de sucesso repetitivos;
  • objetos grandes;
  • health checks;
  • debug em produção;
  • stack de erros conhecidos;
  • duplicidade entre transports.

Amostragem

Mantenha todos os erros e amostre eventos de alta frequência. Não amostre eventos de segurança ou auditoria sem regra formal.

Performance

Cada formato e transport executa trabalho. Pretty printing, serialização profunda e escrita em múltiplos destinos aumentam latência. Faça benchmark.

Backpressure

Transport lento pode acumular mensagens. Monitore memória, erros de escrita e tempo de flush.

Testes

Use um transport em memória ou stream customizada. Verifique:

  • JSON válido;
  • nível;
  • timestamp;
  • defaultMeta;
  • requestId;
  • stack de erro;
  • redaction;
  • duplicidade;
  • falha de transport;
  • shutdown.

Teste de segredo

test('não registra token', () => {
  logger.info('Login', {
    token: 'secret-value'
  });

  assert.equal(output.includes('secret-value'), false);
});

Erros comuns

  • Muitos transports: custo e duplicidade aumentam.
  • Arquivo em container: logs desaparecem ou enchem a camada.
  • Error sem stack: diagnóstico fica incompleto.
  • Corpo completo: dados sensíveis vazam.
  • Pretty em produção: parsing fica mais caro.
  • Sem flush: últimos eventos são perdidos.
  • Logger para erro do logger: ocorre recursão.

Boas práticas

  • Use JSON em produção.
  • Prefira console em containers.
  • Adicione defaultMeta.
  • Configure stack de erros.
  • Use child loggers.
  • Implemente redaction.
  • Controle transports.
  • Monitore falhas e backpressure.
  • Feche no shutdown.
  • Teste ausência de segredos.

Conclusão

Usar logs com Winston no Node.js oferece flexibilidade para combinar formatos, níveis e destinos. A biblioteca é especialmente útil quando a aplicação precisa de políticas distintas para console, arquivos ou integrações.

Essa flexibilidade precisa ser controlada. JSON, campos estáveis, redaction, request IDs e poucos transports evitam custo e vazamentos. Com stdout em containers e testes de shutdown, Winston fornece logs úteis sem comprometer a estabilidade da aplicação.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita