Middleware Logging Patterns — Complete Implementation Guide
In this tutorial, you will learn about Middleware Logging Patterns. We cover key concepts, practical examples, and best practices to help you master this topic.
Middleware logging patterns capture every request entering your application, recording method, URL, timing, status code, and errors for debugging, monitoring, and auditing purposes.
What You'll Learn
By the end of this tutorial, you will build logging middleware that records structured request data, measures response times, handles errors gracefully, and integrates with centralized logging systems.
Why It Matters
Without logging middleware, you have no visibility into who accesses your API, how long requests take, or when errors occur. DodaTech's production services log every request to detect anomalies and debug issues.
Real-World Use
DodaZIP's file conversion API logs every upload, conversion, and download request to track usage patterns and identify performance bottlenecks across different file formats.
Logging Learning Path
flowchart LR
A[Middleware Basics] --> B[Logging Middleware]
B --> C[Structured Logging]
C --> D[Monitoring Integration]
B --> E{You Are Here}
style E fill:#f90,color:#fff
Building a Basic Request Logger
A basic logging middleware records the HTTP method, URL, and timestamp for every request. This is the simplest form of Observability.
const express = require("express");
const app = express();
function requestLogger(req, res, next) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
}
app.use(requestLogger);
app.get("/", (req, res) => res.send("Home"));
app.get("/about", (req, res) => res.send("About"));
app.listen(3000);
Expected output when visiting /about:
[2026-06-28T10:30:00.000Z] GET /about
Adding Response Timing
A more advanced logger measures how long each request takes. This helps identify slow endpoints before they affect users.
const express = require("express");
const app = express();
function timingLogger(req, res, next) {
const start = process.hrtime.bigint();
res.on("finish", () => {
const duration = Number(process.hrtime.bigint() - start) / 1e6;
console.log(
`${req.method} ${req.url} ${res.statusCode} ${duration.toFixed(2)}ms`
);
});
next();
}
app.use(timingLogger);
app.get("/slow", (req, res) => {
setTimeout(() => res.send("Done"), 500);
});
app.listen(3000);
Expected output for GET /slow:
GET /slow 200 502.34ms
Structured JSON Logging
Structured logging outputs JSON objects instead of plain text, making it machine-parseable and easy to search in log aggregation tools like Elasticsearch.
const express = require("express");
const app = express();
function structuredLogger(req, res, next) {
const start = Date.now();
res.on("finish", () => {
const logEntry = {
timestamp: new Date().toISOString(),
method: req.method,
url: req.url,
status: res.statusCode,
duration: Date.now() - start,
ip: req.ip,
userAgent: req.headers["user-agent"]
};
console.log(JSON.stringify(logEntry));
});
next();
}
app.use(structuredLogger);
app.get("/api/data", (req, res) => {
res.json({ data: "sample" });
});
app.listen(3000);
Expected output:
{"timestamp":"2026-06-28T10:30:00.000Z","method":"GET","url":"/api/data","status":200,"duration":12,"ip":"::1","userAgent":"curl/8.0"}
Common Mistakes
Logging sensitive data — Never log passwords, tokens, or personal information. Use a filter list to redact sensitive fields.
Blocking the request with sync operations — Avoid synchronous file writes in logging middleware. Use async logging or offload to a queue.
Not logging errors separately — Error logs should be distinguishable from regular access logs for alerting and debugging.
Logging too much data — Logging full request bodies for large payloads wastes storage. Log body size instead.
Forgetting to handle the finish event — Logging before the response is complete gives incorrect status codes and timing.
Practice Questions
Why should you log on the
finishevent rather than before the response? Thefinishevent fires after the response is sent, giving accurate status codes and timing.What is the advantage of structured JSON logging over plain text? JSON logs are machine-parseable, searchable in log aggregation tools, and easier to analyze programmatically.
How do you avoid logging sensitive information? Filter or redact sensitive fields from the log entry before writing it.
Challenge: Build logging middleware that only logs requests that take longer than 1 second.
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
const duration = Date.now() - start;
if (duration > 1000) {
console.warn(`Slow request: ${req.method} ${req.url} took ${duration}ms`);
}
});
next();
});
FAQ
Mini Project
Build a complete logging middleware system with structured JSON output, request timing, slow request alerts, and a request ID generator.
const express = require("express");
const crypto = require("crypto");
const app = express();
app.use((req, res, next) => {
req.id = crypto.randomUUID();
next();
});
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => {
const duration = Date.now() - start;
const entry = {
reqId: req.id,
timestamp: new Date().toISOString(),
method: req.method,
url: req.url,
status: res.statusCode,
duration,
slow: duration > 2000
};
if (entry.slow) {
console.warn(JSON.stringify(entry));
} else {
console.log(JSON.stringify(entry));
}
});
next();
});
app.get("/fast", (req, res) => res.send("ok"));
app.get("/slow", (req, res) => setTimeout(() => res.send("ok"), 2500));
app.listen(3000);
What's Next
Now that you understand logging middleware, explore implementing authentication as middleware in Express. Then learn about building robust error-handling middleware for production.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro