Back to Blog
Lesson 34 of the Node.js: Build Your First Server & CLI course
Node.jsAugust 22, 20264 min read

Adding Logging: Monitoring API Activity with Morgan Middleware

Learn to implement request logging in your Express API using Morgan. Master custom formats and production-ready observability to debug your server effectively.

Node.jsExpressLoggingMorganObservabilityBackend
Close-up of a computer screen displaying HTML, CSS, and JavaScript code

Previously in this course, we covered implementing input validation to ensure our incoming data is clean. Now that we've secured our inputs, we need to know what's happening to our server in real-time.

In production, you cannot simply attach a debugger to a running process. Instead, you rely on logging—the practice of recording events, errors, and metadata about your application's state. Effective logging is the foundation of observability, allowing you to reconstruct exactly what happened when a request fails or a user reports a bug.

Why We Use Morgan for Request Logging

While you could write your own middleware to print console.log statements for every request, it quickly becomes messy. You need to handle timestamps, HTTP methods, status codes, response times, and remote IP addresses consistently.

Morgan is the standard HTTP request logger middleware for Express. It abstracts the boilerplate of capturing request-response metadata, letting you focus on analyzing the traffic. If you're interested in broader strategies, you can explore strategic logging and observability to see how these logs fit into a larger system.

Implementing Morgan in Your API

First, install the dependency in your project:

Bash
npm install morgan

Now, integrate it into your Express server. In your main entry file (usually app.js or index.js), register it as middleware:

JAVASCRIPT
const express = require(CE9178">'express');
const morgan = require(CE9178">'morgan');
const app = express();

// Use CE9178">'dev' format for concise, color-coded output during development
app.use(morgan(CE9178">'dev'));

app.get(CE9178">'/', (req, res) => {
  res.send(CE9178">'API is running');
});

app.listen(3000, () => console.log(CE9178">'Server running on port 3000'));

When you start your server and hit the root endpoint, you'll see output like this in your terminal: GET / 200 4.234 ms - 14

Configuring Custom Log Formats

The 'dev' format is perfect for your local environment because it is human-readable. However, in production, you often want structured logs (like JSON) so that external services can parse them easily.

You can pass a custom string to Morgan to define exactly what you want to track:

JAVASCRIPT
// Example: Custom format for production-style logs
app.use(morgan(CE9178">':method :url :status :response-time ms - :res[content-length]'));

You can even create a custom token if you need to log something specific, like an authenticated user ID:

JAVASCRIPT
morgan.token(CE9178">'user', (req) => req.user?.id || CE9178">'anonymous');

app.use(morgan(CE9178">':method :url :status - User: :user'));

Production Logging Considerations

When moving to production, keep these principles in mind:

  1. Don't log secrets: Never log req.body or req.headers if they contain passwords, API keys, or sensitive PII (Personally Identifiable Information).
  2. Use JSON in production: Log formats like JSON are essential for log aggregators (like Datadog, Splunk, or ELK).
  3. Log levels: Standardize your logs by severity (INFO, WARN, ERROR). For more on this, review these error handling and logging patterns.

Practice Exercise

  1. Add morgan to your existing project.
  2. Configure it to use the 'combined' format (a standard Apache format) and observe how the output differs from 'dev'.
  3. Create a custom format string that includes the current date and time in the log output.

Common Pitfalls

  • Logging in the wrong order: Always place app.use(morgan(...)) before your routes. If you place it after, requests that fail or hit non-existent routes might not be logged correctly.
  • Performance overhead: Logging every single request is standard, but be careful if you are logging massive payloads. Only log metadata, not the full request body, to keep your I/O performance high.
  • Duplicate logs: If you have multiple layers of proxying (like Nginx or a cloud load balancer), your logs might show up twice. Ensure you understand where your logs are being captured.

FAQ

Q: Should I use console.log for debugging? A: Use console.log for quick local testing, but always switch to proper logging middleware for your production API infrastructure.

Q: Does Morgan impact API performance? A: Negligibly. It is highly optimized, but logging to a file or standard output does consume system resources. In high-traffic systems, ensure your log destination (disk or stream) is fast.

Q: How do I view these logs in the cloud? A: On platforms like Render or Heroku, your standard output (logs) is automatically captured and displayed in their dashboard.

Recap

We've integrated morgan to turn our raw server activity into actionable data. By moving from simple console prints to structured, middleware-based logging, you've taken a major step toward professional observability. This data will be vital as you begin to debug production issues in later modules.

Up next: We will secure our API access by implementing CORS configuration to manage how browsers interact with our server.

Similar Posts