Logging with morgan & pino

Logging is how a running server tells you what it's doing: morgan automatically records every HTTP request, while pino writes fast, structured JSON logs with severity levels that your tools can search, filter, and alert on.

Learn Logging with morgan & pino in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll see why console.log isn't enough in production, write structured logs with pino, control verbosity with levels, tag requests with child loggers, and add morgan as drop-in HTTP request logging — the logging stack real Node services rely on.

What You'll Learn in This Lesson

1️⃣ console.log vs a Real Logger

console.log prints free text with no level, no structure, and no off switch. A real logger like pino writes structured JSON : each line is an object with a level , a time , your message, and any extra fields you attach. That JSON is what log tools ingest so you can search, filter, and alert on it.

Notice the shape below: logger.info(obj, msg) merges your data object into the line. And a child logger created with logger.child({' '}) stamps that field onto everything it logs.

2️⃣ Levels: Controlling the Noise

Every log has a level ranking its importance, from trace (10) up to fatal (60). You set a minimum level, and the logger silently drops anything below it. That single setting is how you run chatty debug logs locally but only info and above in production.

3️⃣ morgan: Logging Every Request

You don't want to hand-write a log line in every route. morgan is middleware that does it for you: add app.use(morgan('tiny')) once and every incoming request is logged automatically with its method, URL, status, response size, and timing.

(The exact response time varies per run — yours will show a different millisecond value.)

In production you usually want one consistent format. The classic pattern pipes morgan's HTTP lines into pino, so your access logs and your application logs are all structured JSON — easy to ship to one place:

Your turn. Fill in the two ___ blanks so the child logger writes the expected JSON line, then run it.

No blanks this time — just a brief and an outline. Write it yourself, run it, and check your output. This ties together child loggers, data fields, and level filtering in one short program.

📋 Quick Reference — Logging

Practice quiz

In what format does pino write its log lines by default?

  • Structured JSON
  • Plain English sentences
  • XML
  • CSV

Answer: Structured JSON. pino emits structured JSON, one object per line.

What does logger.child({ requestId: 'abc-123' }) create?

  • A new process
  • A logger that tags every line with that field
  • A separate file
  • A network connection

Answer: A logger that tags every line with that field. A child logger carries fixed fields (like requestId) onto every line it writes.

With a logger configured at level 'warn', which call is NOT printed?

  • logger.error('x')
  • logger.fatal('x')
  • logger.info('x')
  • logger.warn('x')

Answer: logger.info('x'). info (30) is below warn (40), so it is filtered out.

What is the numeric level pino uses for 'info'?

  • 10
  • 50
  • 60
  • 30

Answer: 30. pino levels: trace=10, debug=20, info=30, warn=40, error=50, fatal=60.

What is morgan used for?

  • Logging an HTTP request line automatically per request
  • Connecting to a database
  • Parsing JSON bodies
  • Hashing passwords

Answer: Logging an HTTP request line automatically per request. morgan is HTTP-request logging middleware for Express.

How is morgan added to an Express app?

  • app.get(morgan)
  • app.use(morgan('tiny'))
  • morgan.listen(app)
  • app.morgan()

Answer: app.use(morgan('tiny')). You register it as middleware: app.use(morgan('tiny')).

Which pino level is the most severe?

  • info
  • warn
  • fatal
  • error

Answer: fatal. fatal (60) is the highest, most severe level.

Why prefer a real logger like pino over console.log in production?

  • console.log is unavailable in Node
  • It is slower
  • It changes the language
  • It produces structured, level-aware, machine-parsable logs

Answer: It produces structured, level-aware, machine-parsable logs. Structured JSON with levels is far easier to filter and search than plain console output.

What is the 'tiny' value passed to morgan?

  • A compact predefined log format preset
  • A file path
  • A port number
  • A log level

Answer: A compact predefined log format preset. 'tiny' is one of morgan's built-in format presets (method url status size - response-time).

Which rule decides whether a pino message is printed?

  • Only error and above ever print
  • Only messages at or above the configured level print
  • Every message always prints
  • Only the first message prints

Answer: Only messages at or above the configured level print. pino prints messages whose level is at or above the configured level.