Request Middleware
Problem
Every log entry produced during an HTTP request should carry the request ID and authenticated user ID — without threading a logger parameter through every function that runs during the request.
Solution
ts
import { Logit } from '@vielzeug/logit';
const httpLog = Logit.scope('http');
export function requestLogger(req, res, next) {
// pin request-scoped fields to every log call in this handler
const reqLog = httpLog.withBindings({ requestId: req.id, ip: req.ip });
const start = Date.now();
const label = `${req.method} ${req.path}`;
res.on('finish', () => {
const duration = `${Date.now() - start}ms`;
const level = res.statusCode >= 500 ? 'error' : res.statusCode >= 400 ? 'warn' : 'info';
reqLog[level]({ status: res.statusCode, duration }, label);
});
next();
}Pitfalls
withBindings()returns a new instance — it does not mutate in place. Assigning it back to a module-level variable replaces the logger for all callers. UseAsyncLocalStorageto scope a logger per request.- The request ID must be generated before the logger is created for the request. Middleware that runs after the logging middleware means early log entries do not carry the request ID.
- Using
console.logalongside the structured logger bypasses the transport and formatter, producing mixed formats in log aggregators.