一次偶发 500 查了三天:我用 requestId 串起 Nuxt、Node API 和小程序日志
支付页偶尔 500,前端、SSR、接口和小程序各报各的错。后来我给每次请求注入统一 requestId,才把零散日志拼回同一条链路。

作者
ZHOUYI
上周支付页偶发 500,小程序弹“请求失败”,Nuxt 服务端只看到上游接口异常,Node 日志里也只是一条普通错误。最难的是它不是每次都出现,用户一点就过去,后台却找不到对应订单。我盯了三天,最后发现问题不在业务逻辑,而在日志没有上下文:每个服务都在喊疼,却没人证明它们说的是同一次请求。
先给请求一个身份证
我第一反应不是上复杂监控,而是给请求加 requestId。入口生成一个唯一 id,后续所有调用都透传。Nuxt SSR 调 API 时带上,Node 收到后写入日志,小程序封装 request 时也带上。这样即使跨端、跨服务,也能用一个 id 把时间线拼起来。
最小改造路径
- Nuxt 的 useFetch/$fetch 统一封装,在请求头里加 X-Request-Id。
- Node API 中间件优先读取请求头,没有就生成一个,并挂到 req 上。
- 小程序请求封装统一 header,把页面级或会话级 requestId 带过去。
- 日志输出时固定带 requestId 字段,具体格式按现有日志库调整,以官方文档为准。
app.use((req, res, next) => {
const id = req.headers['x-request-id'] || crypto.randomUUID();
req.requestId = id;
res.setHeader('X-Request-Id', id);
console.log({ msg: 'request', requestId: id, path: req.path });
next();
});踩坑很快出现。Nuxt 的服务端渲染和浏览器水合不是同一个运行上下文,如果只在客户端生成 id,SSR 阶段发出的第一波请求就会丢。后来我改成:页面首次由服务端渲染时生成并放进响应头,客户端接管后优先复用;如果是客户端直接跳转,再由前端生成。关键不是 id 多高级,而是同一次用户操作尽量共用一个。
把支付和异步任务也串起来
支付场景我还会把 requestId 写进 MongoDB 的订单日志字段,异步关单和重试任务进 Redis 时也携带该字段。再次复现时,我用 requestId 一查,发现小程序发起后,Nuxt SSR 因网络抖动超时,前端自动重试,第二笔请求命中第三方幂等异常,最终返回 500。没有这个 id,我大概率会误判成支付回调问题。
“没有上下文的报错,只能靠猜;有了 requestId,报错才有案发现场。”
这次修复没有什么银弹,核心就是把日志从“各自为战”变成“按请求聚合”。如果你的站点也有偶发错误、支付异常或小程序白屏,先别急着加告警,先把 requestId 打通。它不会让 bug 消失,但会让排查从猜故事变成看证据。