Node.js Express中Morgan中间件无法获取req/res及应用配置问题
问题排查与解决方案
错误原因
Morgan 的 stream 选项不支持直接传入函数,它要求的值必须是一个具备 write() 方法的流对象(比如 fs.createWriteStream 的返回值)。你之前把返回流的函数直接赋值给 stream,Morgan 会将这个函数当作流对象尝试调用 write(),而函数本身没有该方法,因此触发 TypeError。
对比 skip 选项:skip 本身就是为接收函数设计的(Morgan 会在请求处理时自动调用该函数并传入 req、res 参数),所以你在 skip 里能正常获取流是因为函数被正确执行了,但 stream 不支持这种用法。
正确实现方式
需要将 Morgan 包装成自定义中间件,在中间件内部获取 app 配置的流,再初始化 Morgan 实例并执行。
步骤1:app.js 中配置日志流
如果已经提前创建好写入流,直接配置到 app 中:
const fs = require('fs'); const express = require('express'); const app = express(); // 创建日志写入流(追加模式) const logStream = fs.createWriteStream('./access.log', { flags: 'a' }); // 将流配置到 app 中 app.set('logStream', logStream); // 引入自定义日志中间件 const customLogger = require('./middleware/customLogger'); app.use(customLogger);
步骤2:编写自定义日志中间件(middleware/customLogger.js)
const morgan = require('morgan'); // 自定义日志格式 const customLogFormat = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'; module.exports = (req, res, next) => { // 从 app 配置中获取日志流 const logStream = req.app.get('logStream'); // 初始化 Morgan 实例,传入自定义格式和流 const logger = morgan(customLogFormat, { stream: logStream, // 如需跳过某些请求,可添加 skip 逻辑 // skip: (req, res) => res.statusCode < 400 }); // 执行 Morgan 中间件 logger(req, res, next); };
可选:若配置的是日志文件名而非流
如果 app 中配置的是日志文件路径而非已创建的流,可在中间件中创建流并缓存,避免重复打开文件句柄:
const morgan = require('morgan'); const fs = require('fs'); const path = require('path'); // 缓存流对象,避免每次请求重复创建 let cachedLogStream = null; const customLogFormat = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'; module.exports = (req, res, next) => { if (!cachedLogStream) { const logFileName = req.app.get('logFileName'); cachedLogStream = fs.createWriteStream(path.resolve(logFileName), { flags: 'a' }); } const logger = morgan(customLogFormat, { stream: cachedLogStream }); logger(req, res, next); };
内容的提问来源于stack exchange,提问作者Alex Marienko
相关产品推荐
相关产品推荐

