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

Console no Node.js: Guia Prático

Atualizado em: 11 de agosto de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

O objeto Console no Node.js é uma das ferramentas mais usadas durante desenvolvimento, mas também participa de logs, diagnósticos, scripts de linha de comando e observabilidade em produção. Métodos como console.log(), console.error(), console.time() e console.table() parecem simples, porém seu comportamento depende dos streams de saída, da formatação dos valores e do ambiente onde o processo executa.

Em aplicações pequenas, imprimir mensagens pode ser suficiente. Em serviços com alto volume, múltiplos workers ou requisitos de auditoria, logs sem estrutura, sem níveis e sem contexto dificultam investigações. Além disso, serializar objetos grandes ou escrever milhares de linhas por segundo pode aumentar latência e consumo de CPU.

Neste guia, você aprenderá a usar o console global, criar instâncias personalizadas, separar stdout e stderr, formatar valores, medir duração, agrupar mensagens, respeitar backpressure, evitar segredos e evoluir de logs simples para registros estruturados.

O que é o Console no Node.js?

O console global é uma instância da classe Console. Seus métodos escrevem principalmente em process.stdout e process.stderr. A documentação oficial de Console descreve métodos, opções e diferenças de comportamento.

Para entender os streams usados na saída, consulte Objeto Process no Node.js e Streams no Node.js.

console.log e console.info

console.log('Servidor iniciado');
console.info('Porta:', 3000);

Os dois métodos normalmente escrevem em stdout. Eles aceitam vários argumentos e utilizam regras de formatação semelhantes a util.format().

console.log('Usuário %s possui %d itens', 'Ana', 4);

Em produção, prefira mensagens estáveis e campos separados. Textos que mudam constantemente dificultam busca e agregação.

console.error e console.warn

console.error('Falha ao conectar ao banco');
console.warn('Cache indisponível');

Esses métodos escrevem em stderr. Separar saída normal de erros é importante em scripts Unix, containers e plataformas que coletam os dois streams de forma diferente.

node script.js >resultado.log 2>erros.log

Uma mensagem em stderr não define automaticamente um código de saída. Use process.exitCode quando o comando deve terminar com falha.

Formatação com placeholders

O console reconhece marcadores como:

  • %s para string;
  • %d ou %i para número inteiro;
  • %f para ponto flutuante;
  • %j para JSON;
  • %o e %O para objetos;
  • %% para o caractere de porcentagem.
console.log('Duração: %d ms', 42);
console.log('Configuração: %o', config);

A documentação de util.format detalha as regras. O artigo sobre Módulo Util no Node.js mostra outras funções de inspeção.

Inspecionando objetos

console.dir(object, {
  depth: 4,
  colors: process.stdout.isTTY
});

console.dir() permite controlar profundidade, cores e outras opções de inspeção. Evite imprimir objetos completos de requisição, conexão ou usuário. Eles podem conter tokens, cookies, senhas e referências circulares.

console.table

console.table([
  { route: '/users', requests: 120 },
  { route: '/orders', requests: 85 }
]);

O formato tabular é útil para scripts administrativos e depuração local. Em logs de produção, tabelas ocupam várias linhas e são difíceis de processar automaticamente.

Contadores

console.count('cache-miss');
console.count('cache-miss');
console.countReset('cache-miss');

Contadores são mantidos na memória da instância atual. Eles não substituem métricas persistentes e são reiniciados com o processo.

Medição de tempo

console.time('database-query');
await repository.findUsers();
console.timeEnd('database-query');

console.timeLog() registra valores intermediários:

console.time('import');
await readFile();
console.timeLog('import', 'arquivo lido');
await transform();
console.timeEnd('import');

Os labels precisam ser consistentes. Em código concorrente, usar o mesmo label para várias requisições pode gerar conflitos. Para instrumentação real, prefira Performance Hooks no Node.js ou um sistema de métricas.

Agrupando mensagens

console.group('Inicialização');
console.log('Configuração carregada');
console.log('Banco conectado');
console.groupEnd();

console.groupCollapsed() possui utilidade maior em navegadores; em terminais, o comportamento pode ser semelhante ao grupo comum. Grupos são úteis em ferramentas interativas, mas pouco adequados para logs estruturados.

Assertions com console.assert

console.assert(port > 0, 'Porta inválida:', port);

Quando a condição é falsa, uma mensagem é escrita. Esse método não substitui validação nem lança necessariamente uma exceção. Para testes, use Assert no Node.js.

Criando uma instância personalizada

const { Console } = require('node:console');
const fs = require('node:fs');

const output = fs.createWriteStream('./application.log');
const errorOutput = fs.createWriteStream('./errors.log');

const logger = new Console({
  stdout: output,
  stderr: errorOutput
});

logger.log('Aplicação iniciada');
logger.error('Falha simulada');

A classe permite direcionar saída para arquivos, sockets ou streams personalizados. Ainda é necessário tratar erros dos streams e realizar fechamento no shutdown.

Opções de inspeção

const logger = new Console({
  stdout: process.stdout,
  stderr: process.stderr,
  inspectOptions: {
    depth: 3,
    colors: process.stdout.isTTY,
    maxArrayLength: 50
  }
});

Limitar profundidade e tamanho de arrays reduz volume acidental. Essas opções não substituem sanitização.

stdout, stderr e sincronismo

O comportamento de escrita pode variar conforme o destino e a plataforma. Um terminal, arquivo e pipe não são equivalentes. Não presuma que todas as mensagens foram persistidas imediatamente.

Chamar process.exit() logo depois de escrever pode truncar saída:

console.error('Erro fatal');
process.exit(1);

Prefira definir process.exitCode = 1 e permitir que o processo conclua o fluxo, ou aguarde o fechamento dos streams quando necessário.

Backpressure

A API de console não oferece controle explícito de retorno para cada chamada. Em alto volume, escrever diretamente no stream permite observar backpressure:

async function writeLine(stream, value) {
  if (stream.write(`${value}\n`)) return;

  await new Promise(resolve => {
    stream.once('drain', resolve);
  });
}

Para logs de produção, bibliotecas especializadas implementam buffering, serialização e transporte de forma mais eficiente.

Logs estruturados

Em vez de concatenar texto:

console.log(
  `Usuário ${userId} acessou ${route} em ${duration} ms`
);

Use um objeto com campos previsíveis:

console.log(JSON.stringify({
  level: 'info',
  message: 'request_completed',
  userId,
  route,
  durationMs: duration,
  timestamp: new Date().toISOString()
}));

JSON por linha facilita ingestão por plataformas de logs. Evite valores dinâmicos como nomes de campo e mantenha um schema estável.

Níveis de log

O console não filtra níveis automaticamente. Crie uma camada simples:

const levels = {
  debug: 10,
  info: 20,
  warn: 30,
  error: 40
};

const configuredLevel = levels[
  process.env.LOG_LEVEL || 'info'
];

function log(level, message, fields = {}) {
  if (levels[level] < configuredLevel) return;

  const output = JSON.stringify({
    level,
    message,
    ...fields,
    timestamp: new Date().toISOString()
  });

  const stream = level === 'error'
    ? process.stderr
    : process.stdout;

  stream.write(`${output}\n`);
}

Para sistemas maiores, use uma biblioteca madura com redaction, serializers e transportes.

Contexto por requisição

Logs são mais úteis quando incluem request ID, trace ID e operação. AsyncLocalStorage pode preservar contexto:

const store = requestStorage.getStore();

log('info', 'database_query_finished', {
  requestId: store?.requestId,
  operation: 'find-user',
  durationMs
});

Veja AsyncLocalStorage no Node.js para propagação segura.

Redação de segredos

Nunca registre:

  • senhas;
  • tokens de acesso;
  • cookies de sessão;
  • chaves de API;
  • dados completos de cartão;
  • variáveis de ambiente inteiras;
  • corpos completos sem filtragem.
function sanitizeHeaders(headers) {
  const copy = { ...headers };

  for (const key of ['authorization', 'cookie', 'x-api-key']) {
    if (copy[key]) copy[key] = '[REDACTED]';
  }

  return copy;
}

A sanitização deve ocorrer antes da serialização. Teste campos proibidos para evitar regressões.

Erros e stack traces

try {
  await operation();
} catch (error) {
  console.error({
    name: error.name,
    message: error.message,
    stack: error.stack
  });
}

Nem todo erro é uma instância padrão. Normalize valores desconhecidos. Em produção, considere se a stack expõe caminhos internos ou detalhes sensíveis.

Console em múltiplos processos

Cluster e containers podem escrever simultaneamente. Linhas muito grandes podem se misturar dependendo do transporte. Use uma linha JSON por evento e inclua instance ID, PID e worker ID.

O guia de Cluster no Node.js mostra como vários processos atendem a mesma aplicação.

Custo de serialização

Mesmo quando o nível está desativado, construir objetos ou converter JSON antes da checagem consome CPU:

if (isDebugEnabled()) {
  log('debug', 'large_payload', {
    payload: buildExpensiveSummary(data)
  });
}

Evite serializar estruturas enormes em caminhos críticos. Use amostragem e resumos.

Testando logs

Injete streams de memória:

const { Writable } = require('node:stream');

function createCollector() {
  const lines = [];

  const stream = new Writable({
    write(chunk, encoding, callback) {
      lines.push(chunk.toString());
      callback();
    }
  });

  return { stream, lines };
}

Crie uma instância Console com esse stream e verifique mensagens, campos e ausência de segredos.

Logs não substituem métricas e traces

Logs explicam eventos individuais. Métricas mostram tendências e traces conectam operações distribuídas. Use cada ferramenta para sua finalidade. O conteúdo de OpenTelemetry no Node.js apresenta traces e métricas.

Erros comuns

  • Registrar objetos completos: segredos e volume excessivo aparecem.
  • Usar console.log para tudo: níveis e destinos ficam misturados.
  • Chamar process.exit imediatamente: mensagens podem ser truncadas.
  • Construir logs caros desativados: CPU é consumida sem benefício.
  • Usar tabelas em produção: processamento automático fica difícil.
  • Não incluir contexto: eventos não podem ser correlacionados.
  • Tratar logs como métricas: agregações ficam lentas e caras.

Boas práticas para produção

  • Use JSON por linha.
  • Separe stdout e stderr.
  • Defina níveis configuráveis.
  • Inclua request ID e trace ID.
  • Remova segredos antes de escrever.
  • Limite profundidade e tamanho de objetos.
  • Evite serialização cara em caminhos críticos.
  • Respeite backpressure em alto volume.
  • Teste o schema e a redaction.
  • Combine logs, métricas e traces.

Conclusão

O Console no Node.js oferece ferramentas úteis para desenvolvimento, scripts e diagnósticos, incluindo formatação, inspeção, contadores, grupos e medição de tempo. A classe Console também permite direcionar mensagens para streams personalizados.

Em produção, qualidade depende mais da disciplina que do método utilizado. Estrutura estável, níveis, contexto, sanitização e controle de volume tornam logs pesquisáveis e seguros. Quando a aplicação cresce, uma camada de logging especializada deve complementar ou substituir chamadas diretas ao console.

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