概述
日志描述单次请求发生了什么,指标回答整个服务是否健康。延迟直方图可以计算 p50、p95 和 p99,错误率可以触发 SLO 告警。先保留一小组指标:请求耗时、按状态类别的请求数、需要时的并发请求数,以及 prom-client 的默认运行时指标。标签是设计重点:路由标签必须使用 Express 路由模板 /orders/:id,不能用 req.path 中的真实订单 ID,否则每个 ID 都会生成新时间序列,最终压垮 Prometheus。状态也应按 2xx、4xx、5xx 分组。
步骤
安装 prom-client,只创建一个 Registry:
import client from 'prom-client';
export const register = new client.Registry();
client.collectDefaultMetrics({ register, prefix: 'nodejs_' });
export const httpDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'route', 'status'],
buckets: [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
registers: [register],
});
中间件在 res.on('finish') 中记录 req.route 模板和状态类别。/metrics 路由本身跳过统计,避免递归。端点使用 METRICS_TOKEN 校验 Bearer 令牌,未授权返回 403,成功时设置 register.contentType 并返回 await register.metrics()。
Prometheus 配置增加抓取任务,令牌从文件读取:
scrape_configs:
- job_name: orders-api
scrape_interval: 15s
metrics_path: /metrics
authorization:
type: Bearer
credentials_file: /etc/prometheus/orders-api-token
static_configs:
- targets: ["orders-api.default.svc.cluster.local:3000"]
验证
运行 promtool check config 并用 curl -X POST http://localhost:9090/-/reload 重载。查询 targets API,orders-api 应为 up 且 lastError 为空。生成若干请求并等待两个抓取周期后执行:
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
应返回数值;若为 NaN,说明窗口内没有样本。最后确认不带令牌请求 /metrics 返回 403,带令牌时能看到 http_request_duration_seconds_count。
注意事项
进程内指标在重启后清零,长期历史需要 remote write 或远端存储。拉取模型要求 Prometheus 能访问每个实例,短生命周期批处理任务应使用 Pushgateway。静态 Bearer 令牌是共享密钥,应配合网络策略并定期轮换。不要用 req.originalUrl 或用户输入做标签,也不要把单次 p95 尖峰直接当成事故;低流量窗口的分位数噪声很大。