Morgan é um middleware HTTP logger para Node.js — ele registra automaticamente todas as requisições que chegam na sua aplicação Express. Se você precisa saber quem está acessando seu servidor, quais rotas estão sendo chamadas e quanto tempo cada resposta demora, o Morgan faz isso por você. Ele é essencial em produção, onde você precisa monitorar tráfego, depurar problemas e guardar logs para auditoria ou análise.
Se você já usou Apache ou Nginx, sabe que eles têm logs de acesso por padrão. No Express, a gente adiciona esse comportamento com o morgan, plugado via app.use (se esse conceito ainda não está claro, veja o que é o app.use no Express). Ele é simples, leve e altamente configurável.
Instalando o morgan com npm
A primeira coisa é instalar o Express e o morgan:
npm install express morgan
Se você usa Yarn:
yarn add express morgan
Com as dependências instaladas, você importa os dois no seu arquivo principal:
const express = require('express')
const morgan = require('morgan')
const app = express()
app.get('/', (req, res) => {
res.send('Olá DevPleno')
})
app.listen(3000, () => console.log('Servidor rodando na porta 3000'))
Agora o servidor está funcionando, mas ainda sem log. Vamos adicionar o morgan como middleware.
Formatos prontos: dev, combined, common, tiny
O morgan vem com vários formatos predefinidos que cobrem os casos mais comuns. Você escolhe qual usar no momento de adicionar o middleware:
app.use(morgan('dev'))
O formato dev é colorido e feito para desenvolvimento. Ele mostra o método HTTP, a URL, o status, o tempo de resposta e o tamanho da resposta:
GET / 200 3.456 ms - 13
POST /api/users 201 12.334 ms - 245
Outros formatos:
- combined: formato padrão do Apache. Inclui IP do cliente, timestamp, método, URL, status, user-agent e referer. Ideal para produção.
- common: similar ao combined, mas sem referer e user-agent.
- tiny: versão minimalista. Mostra só método, URL, status, tamanho e tempo de resposta.
Exemplo com combined:
app.use(morgan('combined'))
Resultado:
::1 - - [20/Oct/2026:14:32:10 +0000] "GET / HTTP/1.1" 200 13 "-" "Mozilla/5.0..."
Para desenvolvimento, use dev. Para produção, prefira combined ou common, porque eles registram informações mais completas.
Criando um formato com tokens
Se os formatos prontos não atendem, você cria o seu próprio usando tokens. Tokens são variáveis que o morgan substitui automaticamente:
app.use(morgan(':method :url :status :response-time ms'))
Resultado:
GET / 200 4.123 ms
POST /api/login 401 15.678 ms
Tokens mais usados:
:method— método HTTP (GET, POST, etc.):url— URL requisitada:status— código de status HTTP:response-time— tempo de resposta em milissegundos:res[content-length]— tamanho da resposta em bytes:remote-addr— IP do cliente:date[clf]— data no formato Common Log Format:user-agent— navegador ou cliente que fez a requisição
Você também pode criar tokens customizados. Por exemplo, para logar o corpo da requisição (útil em POST):
morgan.token('body', (req) => JSON.stringify(req.body))
app.use(express.json())
app.use(morgan(':method :url :status - :body'))
Cuidado ao logar o body em produção — pode expor dados sensíveis. Use apenas em desenvolvimento ou filtre campos confidenciais.
Salvando o log em arquivo
Por padrão, o morgan escreve no console (stdout). Em produção, é melhor salvar os logs em arquivo. Para isso, usamos o módulo fs nativo do Node:
const fs = require('fs')
const path = require('path')
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
)
app.use(morgan('combined', { stream: accessLogStream }))
O { flags: 'a' } faz o Node adicionar no final do arquivo em vez de sobrescrever. Agora toda requisição é registrada em access.log, e o console fica limpo.
Se você quiser rotacionar logs (criar um arquivo novo a cada dia, por exemplo), use bibliotecas como rotating-file-stream:
npm install rotating-file-stream
const rfs = require('rotating-file-stream')
const accessLogStream = rfs.createStream('access.log', {
interval: '1d',
path: path.join(__dirname, 'logs')
})
app.use(morgan('combined', { stream: accessLogStream }))
Isso cria um arquivo novo a cada dia, com nome access-2026-10-20.log, por exemplo.
Ignorando rotas com skip
Às vezes você não quer logar certas requisições — healthcheck, arquivos estáticos, rotas de monitoramento. Para isso, use a opção skip:
app.use(morgan('combined', {
skip: (req, res) => req.url === '/health'
}))
Agora requisições para /health não aparecem no log. Você pode combinar condições:
app.use(morgan('combined', {
skip: (req, res) => res.statusCode < 400
}))
Esse exemplo só loga erros (status 400+). Útil para monitorar problemas sem poluir o log com requisições bem-sucedidas.
morgan, winston ou pino?
Se você está começando, o morgan resolve. Ele é simples, direto e faz uma coisa bem: logar requisições HTTP.
Mas projetos maiores geralmente precisam de um sistema de logging mais completo. É aí que entram winston e pino:
- winston: biblioteca de logging geral. Permite múltiplos destinos (console, arquivo, banco de dados, serviços externos como Papertrail ou Loggly), níveis de log (info, warn, error), formatação customizada e transporte assíncrono.
- pino: focado em performance. É um dos loggers mais rápidos do Node.js, ideal para aplicações de alta carga. Tem integração com Fastify e funciona bem com morgan.
O morgan é específico para requisições HTTP. O winston e o pino são para logging geral da aplicação (erros de banco, eventos de negócio, etc.). Em produção, é comum usar morgan para requisições e winston ou pino para tudo mais.
Você também pode integrar os dois. Exemplo com pino:
const pino = require('pino')
const logger = pino()
app.use(morgan('combined', {
stream: {
write: (message) => logger.info(message.trim())
}
}))
Agora os logs do morgan vão para o pino, e você tem um sistema unificado.
Confira o video:
Curta o DevPleno no Facebook, inscreva-se no canal e cadastre seu e-mail para não perder nenhuma novidade. Deixe suas dúvidas e sugestões nos comentários. Abraço!
Mestre em Sistemas de Informação pela USP e criador do DevPleno. Iniciou sua carreira como professor com apenas 18 anos em um curso técnico, foram 11 anos em sala de aula formando desenvolvedores fullstack no sul de Minas Gerais.