如何通过Morgan实现请求与响应的日志拆分?
Great question! Morgan's flexibility makes splitting request and response logging straightforward—you just need to leverage its immediate option and pay attention to middleware ordering. Here's a step-by-step breakdown tailored to your use case:
Key Background: How Morgan Triggers Logs
By default, Morgan waits until the response finishes loading (when the finish event fires on the response object) to write logs. But when you set immediate: true, Morgan logs the moment a request hits your server—before any route handlers or business logic run. This is the core trick to separating request and response logs.
Step 1: Register Two Morgan Instances (In the Right Order)
Express executes middleware in the exact order you register them, so follow this sequence:
- First, add the request logging middleware (with
immediate: true) to capture incoming requests immediately. - Place your route handlers, authentication middleware, body parsers, etc., next.
- Finally, add the response logging middleware (using Morgan's default behavior, no
immediateflag) to log details after the response is sent to the client.
Example Code (Matching Your Winston Setup)
Here’s how to adapt your existing code into two distinct loggers:
// Request logger: Runs the second a request arrives app.use(morgan('[:date[clf]] :remote-addr :method :url HTTP/:http-version :remote-user :referrer :user-agent', { immediate: true, stream: { write: (message) => { winston.info(`REQUEST: ${message.trim()}`); } } })); // --- Your route handlers and other middleware go here --- // app.get('/api/data', yourRouteHandler); // app.use(bodyParser.json()); // Response logger: Runs after the response is sent to the client app.use(morgan('[:date[clf]] :remote-addr :url :status :res[content-length] :response-time ms', { stream: { write: (message) => { winston.info(`RESPONSE: ${message.trim()}`); } } }));
Breakdown of the Setup
- Request Log: The
immediate: trueflag ensures we only log request-specific details (no response data like:statusor:response-time—those don’t exist yet). Prefixing withREQUEST:makes it easy to distinguish these logs in your Winston output. - Response Log: Without the
immediateflag, Morgan waits for the response to complete, so we can safely include response-specific fields like status code, content length, and response time. Prefixing withRESPONSE:clarifies these logs.
Why Order Can’t Be Reversed
If you swap the order, the response logger (default behavior) would still work, but the request logger would run after your route handlers—defeating the purpose of capturing the request as it arrives. Always place the immediate: true logger first.
Bonus: Extending to Other Use Cases
You can expand this pattern to match the other combinations you mentioned:
- Write all request logs to a file: Replace the request logger’s
streamwithfs.createWriteStream('./requests.log', { flags: 'a' }) - Output only error responses to the console: Add a
skipfunction to the response logger (like the official example):app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400, stream: process.stdout // Logs errors directly to console }));
内容的提问来源于stack exchange,提问作者falsarella

