Morgan 请求日志实战:Express HTTP 访问日志从入门到生产化
Morgan 是 Express 应用记录 HTTP 请求日志的标准中间件。本文讲解 Morgan 的五种预定义格式、自定义 token、JSON 输出改造、与 Winston 的配合,以及请求日志接入观测云实现状态码监控与慢请求分析的落地路径。
Morgan 是 Node.js/Express 生态中专责 HTTP 请求日志的中间件,自动记录每个进入请求的方法、URL、状态码、响应耗时、客户端 IP 与 UA 等信息。 没有它,Express 应用对流量是"盲"的——哪些接口被访问、响应多快、错误率多少,全都无从得知。
核心要点速览
- 一行接入:
app.use(morgan('combined')),立即获得 Apache 风格的完整访问日志; - 五种预定义格式:combined/common/dev/short/tiny,开发用
dev,生产用自定义 JSON; - 自定义 token 可注入 request_id 等字段,实现请求级追踪;
- 生产环境让 Morgan 输出 JSON,由 DataKit 采集进观测云做状态码统计、慢请求分析与告警。
为什么需要 Morgan?
裸 Express 应用对请求毫无记录:看不到哪些路由被访问、无法统计状态码、没有响应耗时数据、可疑请求无痕可查。Morgan 作为中间件挂在请求链路上,每个请求处理完毕自动产生一条访问日志,一行代码补齐可观测性最基础的一环。
快速上手
npm install morgan
import express from 'express';
import morgan from 'morgan';
const app = express();
app.use(morgan('combined'));
访问接口后输出 Apache combined 风格日志:
::1 - - [25/Aug/2026:10:15:14 +0000] "GET /about HTTP/1.1" 304 - "-" "Mozilla/5.0 ..."
五种预定义格式怎么选?
| 格式 | 内容 | 适用场景 |
|---|---|---|
combined |
Apache 完整格式(IP、时间、请求、状态码、大小、Referer、UA) | 兼容传统分析工具 |
common |
combined 去掉 Referer/UA | 精简访问记录 |
dev |
彩色单行:方法 URL 状态码 耗时 | 本地开发 |
short |
短格式 | 简单服务 |
tiny |
最小格式 | 快速冒烟 |
切换只需改字符串:app.use(morgan('dev'))。
自定义 token:给每个请求一个 ID
内置 token(:method、:url、:status、:response-time 等)之外,可以自定义。最有价值的一个是请求 ID:
import { v4 as uuidv4 } from 'uuid';
app.use((req, res, next) => { req.id = uuidv4(); next(); });
morgan.token('id', (req) => req.id);
app.use(morgan(':id :method :url :status :res[content-length] - :response-time ms'));
e17c72f8-cb0c-4559-9d4f-3d22b75e03d6 GET / 304 - - 16.537 ms
有了 request_id,就能把访问日志与应用内部日志、下游调用串成一条线。
生产化:输出 JSON
文本格式进平台还要切割,不如让 Morgan 直接产出 JSON——传入自定义格式函数即可:
app.use(
morgan((tokens, req, res) => JSON.stringify({
request_id: tokens.id(req, res),
method: tokens.method(req, res),
url: tokens.url(req, res),
status: parseInt(tokens.status(req, res), 10),
response_time_ms: parseFloat(tokens['response-time'](req, res)),
client_ip: tokens['remote-addr'](req, res),
user_agent: tokens['user-agent'](req, res),
}))
);
每条请求日志即一个标准 JSON 对象。需要同时保留应用日志时,把 Morgan 的 stream 选项接到 Winston/Pino 上,让请求日志与应用日志同一通道、同一格式输出:
app.use(morgan('combined', { stream: { write: (msg) => logger.info(msg.trim()) } }));
也可以按状态码分流:4xx/5xx 走 logger.error,其余走 logger.info,后续按级别筛选更直接。
接入观测云:请求日志能挖出什么?
观测云落地:JSON 请求日志写 stdout/文件后由 DataKit 采集上报(容器用 stdout 采集,虚拟机用磁盘文件采集),Pipeline 提取标准字段 time、并按状态码区间映射 status(5xx→error、4xx→warning)。随后请求日志立刻变成可运营的数据:
- 看板:查看器中按
url分组统计 QPS、按status看错误率、按response_time_ms找慢接口,一键导出到仪表板 ; - 告警:监控器(日志检测)配置"5xx 数量超阈值"或"某核心接口 P 耗时段异常"规则,告警策略推送钉钉/企业微信/飞书 ;
- 追踪:
request_id字段贯穿查询,若同时接入了观测云 APM,还可与链路数据互相跳转 。
总结
Morgan 的生产化三步:开发用 dev 格式求快,生产改 JSON 格式求结构化,加 request_id token 求可追踪。再往后的事情——统计、看板、告警——交给观测云。
常见问题(FAQ)
Q:Morgan 和应用日志库(Winston/Pino)是什么关系?
分工不同:Morgan 只管 HTTP 请求日志,应用内的业务/错误日志由 Winston/Pino 负责。实践中常把 Morgan 的输出通过 stream 导入 Winston/Pino,统一格式与通道。
Q:Morgan 还在维护吗?
更新频率很低,但功能早已稳定——请求日志中间件本身不需要频繁迭代,Express 生态至今没有公认替代品,可放心使用。
Q:高并发下 Morgan 会影响性能吗?
开销很小(每个请求一次格式化输出)。真正要注意的是输出目的地:生产写 stdout 即可,切勿同步写数据库或远程 HTTP。
Q:已有 Nginx 访问日志,还需要 Morgan 吗?
建议保留。Nginx 日志看不到应用内部视角(如路由参数、业务用户 ID),Morgan 在应用层可携带更丰富的上下文;两者在观测云中按 source 区分,互为补充。
系列阅读:Winston 日志实战指南 | Node.js 日志库八款对比