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.

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:
Bashnpm install morgan
Now, integrate it into your Express server. In your main entry file (usually app.js or index.js), register it as middleware:
JAVASCRIPTconst 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:
JAVASCRIPTmorgan.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:
- Don't log secrets: Never log
req.bodyorreq.headersif they contain passwords, API keys, or sensitive PII (Personally Identifiable Information). - Use JSON in production: Log formats like JSON are essential for log aggregators (like Datadog, Splunk, or ELK).
- Log levels: Standardize your logs by severity (INFO, WARN, ERROR). For more on this, review these error handling and logging patterns.
Practice Exercise
- Add
morganto your existing project. - Configure it to use the
'combined'format (a standard Apache format) and observe how the output differs from'dev'. - 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.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.

