You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React前端应用集成Prometheus HTTP指标:prom-client无法捕获5xx错误及正确状态码的问题排查

解决Prometheus无法收集5xx错误HTTP指标的问题

我看了你的代码,发现几个关键问题导致5xx错误的指标没被正确收集,下面一步步帮你修复:

1. 先修复/metrics路由的致命错误

你的/metrics路由同时发送了React的index.html和Prometheus指标数据,这会导致响应重复结束的错误,而且Prometheus根本无法正确解析指标。把这个路由改成只返回指标:

app.get('/metrics', (req, res) => {
 res.setHeader('Content-Type', Prometheus.register.contentType);
 res.end(Prometheus.register.metrics());
});

2. 调整中间件顺序,确保错误请求也能触发指标收集

当前的中间件顺序里,错误处理中间件之后才是指标收集逻辑,但你需要确保所有请求(包括错误请求)都能走到指标收集中间件。另外,React SPA的路由 fallback 应该放在业务路由之后、metrics路由之前,避免影响指标接口的访问。

调整后的完整中间件/路由顺序:

  • 先注册记录请求开始时间的中间件
  • 静态资源和favicon中间件
  • 业务路由(/、/bad)
  • React SPA fallback路由(返回index.html)
  • /metrics路由
  • 错误处理中间件
  • 404处理中间件
  • 最后注册指标收集中间件

3. 确保错误处理中间件调用next()

在错误处理中间件里,处理完错误后必须调用next(),这样才能让后续的指标收集中间件执行,捕获到500状态码:

app.use((err, req, res, next) => {
 console.error(err.stack);
 res.status(500).json({ error: err.message });
 next(); // 关键:让指标收集中间件执行
});

修复后的完整server.js代码

const express = require('express')
const Prometheus = require('prom-client');
const favicon = require('express-favicon');
const path = require('path');
const app = express()
const port = process.env.PORT || 8082

// 收集Prometheus默认指标
const metricsInterval = Prometheus.collectDefaultMetrics()

// 定义HTTP请求时长直方图指标
const httpRequestDurationMicroseconds = new Prometheus.Histogram({
 name: 'http_request_duration_ms',
 help: 'Duration of HTTP requests in ms',
 labelNames: ['method', 'route', 'code'],
 buckets: [0.10, 5, 15, 50, 100, 200, 300, 400, 500]
})

// 记录请求开始时间的中间件(第一个注册,确保所有请求都能捕获到)
app.use((req, res, next) => {
 res.locals.startEpoch = Date.now();
 next();
});

// 静态资源和favicon处理
app.use(favicon(__dirname + '/build/favicon.ico'));
app.use(express.static(__dirname));
app.use(express.static(path.join(__dirname, 'build')));

// 业务路由
app.get('/', (req, res, next) => {
 setTimeout(() => {
 res.json({ message: 'Hello World!' });
 next(); // 调用next触发指标收集
 }, Math.round(Math.random() * 200));
});

app.get('/bad', (req, res, next) => {
 next(new Error('My Error')); // 传递错误到错误处理中间件
});

// React SPA路由fallback:所有非API/非metrics的请求返回index.html
app.get('*', (req, res, next) => {
 res.sendFile(path.join(__dirname, 'build', 'index.html'));
 next(); // 调用next触发指标收集
});

// Prometheus指标接口
app.get('/metrics', (req, res) => {
 res.setHeader('Content-Type', Prometheus.register.contentType);
 res.end(Prometheus.register.metrics());
});

// 错误处理中间件(必须带4个参数,Express才会识别为错误处理)
app.use((err, req, res, next) => {
 console.error(err.stack);
 res.status(500).json({ error: err.message });
 next(); // 关键:让指标收集中间件执行
});

// 404处理中间件
app.use((req, res, next) => {
 res.status(404).json({ error: 'Not Found' });
 next();
});

// 指标收集中间件(最后一个注册,确保所有请求都能走到这里)
app.use((req, res, next) => {
 const responseTimeInMs = Date.now() - res.locals.startEpoch;
 // 优先用路由定义的path,避免动态参数导致指标标签爆炸(比如/api/users/123会变成/api/users/:id)
 const route = req.route ? req.route.path : req.path;
 httpRequestDurationMicroseconds
 .labels(req.method, route, res.statusCode)
 .observe(responseTimeInMs);
 next();
});

// 启动服务器
const server = app.listen(port, () => {
 console.log(`Example app listening on port ${port}!`)
})

// 优雅关闭逻辑
process.on('SIGTERM', () => {
 clearInterval(metricsInterval)
 server.close((err) => {
 if (err) {
 console.error(err)
 process.exit(1)
 }
 process.exit(0)
 })
})

为什么之前的代码无法收集5xx指标?

  1. /metrics路由错误:同时发送HTML和指标,导致响应异常,Prometheus无法正常抓取数据,甚至可能让Node.js抛出错误。
  2. 错误处理中间件未调用next():当错误发生时,错误处理中间件处理完后没有继续传递请求到指标收集中间件,导致500请求的指标被遗漏。
  3. 中间件顺序不合理:React SPA的fallback路由如果放在前面,会拦截/metrics请求,导致无法访问指标接口。

现在你可以测试:

  • 访问/bad,会返回500错误,此时/metrics里会出现code="500"的指标
  • 访问不存在的路径,会返回404,指标里也会有code="404"的记录

内容的提问来源于stack exchange,提问作者BIndu_Madhav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:32:28