用户已经遇到问题但你不自知,是业务系统最隐蔽的风险。本文记录从前端错误、接口异常、核心流程到业务指标的监控埋点方案,结合 Sentry + Prometheus + Grafana,让系统问题在用户感知之前就被发现。
比系统宕机更可怕的事
去年有一次,我们的核心功能——用户下单支付——在某个时段成功率降到了 70%。用户反复尝试支付失败,客服电话被打爆。而我们直到运营在群里发了张“今天订单怎么少了”的截图,才知道出了问题。翻日志一看,是第三方支付通道临时限流,而我们的代码没处理好降级,直接把错误抛给了前端。
那次之后我一直在想:如果能在成功率刚下降时就收到告警,几分钟内切到备用通道,损失会小得多。但传统监控只盯着 CPU、内存、磁盘——这些那天一切正常。
业务监控和系统监控是两回事。系统监控告诉你机器活没活着,业务监控告诉你用户能不能正常用。本文是我这半年把业务监控从零搭建起来的完整记录。
业务监控的四层金字塔
我把需要监控的内容分为四层,从底层到顶层依次是:
/ 业务指标 \
/ 核心流程 \
/ 接口监控 \
/ 前端错误 \
/_________________\
- 第一层:前端错误——用户看到什么报错、页面白屏、JS 异常。
- 第二层:接口监控——哪些接口报错、响应慢、成功率低。
- 第三层:核心流程——登录、下单、支付这些关键链路是否通畅。
- 第四层:业务指标——下单量、支付转化率、客单价有没有异常波动。
下面从第一层开始,逐层往上搭。
第一层:前端错误监控
前端错误是离用户最近的一层。用户看到报错,他不会去区分是后端还是前端的锅,只会觉得“这系统不好用”。
方案选型
我对比了三个方案:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 自己写 window.onerror | 简单 | 无 SourceMap、无聚合、无告警 | 只有 1-2 个页面的项目 |
| Sentry SaaS | 功能全、零维护 | 数据在第三方、有免费额度限制 | 多数团队 |
| Sentry 自部署 | 数据可控、功能同 SaaS | 需要维护服务器 | 合规要求高的团队 |
我们用 Sentry SaaS 免费版,每月 5000 个错误事件够用。关键是它的 SourceMap 自动还原和 Session Replay 功能,排查线上问题效率极高。
接入代码
// src/index.tsx
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: 'https://xxx@sentry.io/1',
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration({
maskAllText: true,
blockAllMedia: true,
}),
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.01,
replaysOnErrorSampleRate: 1.0, // 报错时 100% 录屏
});
核心指标
在 Sentry 里重点关注三个面板:
- Issues:按错误类型聚合,看 Top 错误是哪些。
- Performance:LCP、FCP、TBT 等 Web Vitals。
- Replays:报错用户的录屏回放,排查复现步骤。
实际帮了大忙的一次:一个新版本只在 iOS 15.4 上触发 JS 异常。Sentry 告警带着设备型号和完整堆栈,十分钟定位到是 Array.prototype.at() 在低版本 iOS 上不支持。没 Sentry 的话,这个 Bug 可能躺到有足够多用户投诉才会被发现。
第二层:接口监控
接口监控需要回答三个问题:
- 哪些接口在报错?
- 哪些接口变慢了?
- 整体成功率是多少?
我们在 Nginx 层和应用层都做了埋点,互相印证。
Nginx 层
Nginx 的访问日志本身就是一份完整的接口监控数据。用 nginx-prometheus-exporter 把日志转成 Prometheus 指标:
docker run -d \
--name nginx-exporter \
-v /var/log/nginx/access.log:/var/log/nginx/access.log \
-p 9113:9113 \
nginx/nginx-prometheus-exporter:0.11.0 \
-nginx.scrape-uri=http://nginx:8080/stub_status
应用层
Spring Boot 项目用 Micrometer 自动暴露 HTTP 请求指标:
# application.yml
management:
endpoints:
web:
exposure:
include: prometheus,health,metrics
metrics:
export:
prometheus:
enabled: true
然后在 Grafana 里配告警规则:
# prometheus rules/api.yml
groups:
- name: api_alerts
rules:
# 5xx 错误率超过 3%
- alert: High5xxRate
expr: |
rate(http_server_requests_total{status=~"5.."}[5m])
/ rate(http_server_requests_total[5m]) > 0.03
for: 3m
labels:
severity: critical
annotations:
summary: "{{ $labels.uri }} 5xx 错误率 {{ $value | humanizePercentage }}"
# P95 响应时间超过 2 秒
- alert: SlowApi
expr: |
histogram_quantile(0.95, rate(http_server_requests_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.uri }} P95 延迟 {{ $value }}s"
告警分级
不是所有接口都重要。我们对接口做了分级:
| 级别 | 接口 | 告警阈值 | 通知方式 |
|---|---|---|---|
| P0(核心) | 登录、支付、下单 | 错误率 > 1% | 钉钉 @所有人 + 电话 |
| P1(重要) | 商品详情、购物车 | 错误率 > 5% | 钉钉群 |
| P2(一般) | 个人信息、收藏 | 错误率 > 10% | 邮件汇总 |
告警分级之后,不再被不重要的问题打扰,也不会漏掉核心问题。
第三层:核心流程监控
前端和接口的监控是“单点”的,但用户的一个操作可能跨越多个服务和接口。核心流程监控关注的是“链路”——比如从点击“下单”到页面跳转“支付成功”,中间经历了多少个步骤,每一步的成功率和耗时是多少。
自定义埋点
// 前端:在关键步骤打点
const startCheckout = () => {
const traceId = generateTraceId();
// 步骤1: 创建订单
reportEvent('checkout_start', { traceId, step: 'create_order' });
api.createOrder(cartData).then(res => {
reportEvent('checkout_step_done', { traceId, step: 'create_order', duration: Date.now() - startTime });
// 步骤2: 发起支付
reportEvent('checkout_start', { traceId, step: 'pay' });
return api.pay(res.orderId);
}).then(() => {
reportEvent('checkout_complete', { traceId, totalDuration: Date.now() - startTime });
}).catch(err => {
reportEvent('checkout_failed', { traceId, step: err.step, reason: err.message });
});
};
Grafana 漏斗图
在 Grafana 里用 PromQL 算出每一步的转化率,画成漏斗:
下单流程漏斗:
进入下单页:1000 次
点击提交:800 次 (80%)
创建订单成功:750 次 (93.8%)
支付成功:620 次 (82.7%)
整体转化率:62%
如果支付成功率突然从 82% 掉到 50%,告警立刻触发。那次支付通道限流的问题,如果当时有这张漏斗图,不用等运营通知,Grafana 自己就发消息了。
第四层:业务指标监控
这一层关注的是业务数据本身的波动。技术指标(接口成功率、响应时间)正常,不代表业务正常。比如:今天订单量相比昨天同一时段少了 30%,虽然系统完全正常,但业务上可能出了问题——运营配错了活动价格、投放广告停了、或者竞品搞了促销。
指标定义
| 业务指标 | 数据源 | 对比维度 |
|---|---|---|
| 下单量 | 订单表 | 同比昨天同一时段、环比上周 |
| 支付转化率 | 订单表 + 支付日志 | 同上 |
| 客单价 | 订单表 | 同上 |
| 新增用户 | 用户表 | 同上 |
| 核心功能使用率 | 埋点日志 | 同上 |
实现方案
每天定时跑一个脚本,把昨天的业务数据聚合到一张 business_metrics 表,然后暴露给 Prometheus:
-- 每小时执行一次,写入指标表
INSERT INTO business_metrics (metric_name, metric_value, recorded_at)
SELECT
'orders_count' as metric_name,
COUNT(*) as metric_value,
DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00') as recorded_at
FROM orders
WHERE created_at >= DATE_FORMAT(NOW() - INTERVAL 1 HOUR, '%Y-%m-%d %H:00:00')
AND created_at < DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00');
然后用一个简单的 Python 脚本把指标暴露为 Prometheus 格式:
# business_metrics_exporter.py
from flask import Flask
import mysql.connector
import os
app = Flask(__name__)
@app.route('/metrics')
def metrics():
conn = mysql.connector.connect(
host=os.environ['DB_HOST'],
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD'],
database=os.environ['DB_NAME']
)
cursor = conn.cursor()
cursor.execute("""
SELECT metric_name, metric_value
FROM business_metrics
WHERE recorded_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
ORDER BY recorded_at DESC
LIMIT 100
""")
output = []
for metric_name, metric_value in cursor.fetchall():
output.append(f'{metric_name} {metric_value}')
cursor.close()
conn.close()
return '\n'.join(output), 200, {'Content-Type': 'text/plain'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9200)
然后在 Prometheus 里增加采集任务:
scrape_configs:
- job_name: 'business_metrics'
static_configs:
- targets: ['metrics-exporter:9200']
scrape_interval: 30m
这个方案比较简单,适合快速落地。如果团队需要更完善的业务指标管理(比如自动同比、异常检测、多维下钻),也可以参考 gpt108.com 上分享的《业务监控进阶:用 ClickHouse + Grafana 搭建实时业务大盘》,里面包含了更复杂场景的设计思路。
同比告警
光看绝对值不够,要用同比来判断异常:
# prometheus rules/business.yml
groups:
- name: business_alerts
rules:
# 每小时下单量相比昨天同一时段下降超过 30%
- alert: OrderDropWarning
expr: |
(orders_count - orders_count offset 1d) / (orders_count offset 1d) < -0.3
for: 1h
labels:
severity: warning
annotations:
summary: "下单量同比下跌超过 30%"
description: "当前小时订单量 {{ $value | humanize }},较昨日同时段大幅下降"
# 支付转化率异常
- alert: ConversionRateDrop
expr: |
(payment_success_rate - payment_success_rate offset 1d) < -0.1
for: 30m
labels:
severity: critical
annotations:
summary: "支付转化率下跌超过 10 个百分点"
业务指标告警发到运营群,配合技术告警发到技术群。各看各的,快速判断是技术问题还是业务问题。
搭建后的几个真实案例
这套监控体系上线半年,帮我们发现了几次系统监控完全看不出来的问题:
-
支付通道降级:支付成功率从 82% 降到 50%,Prometheus 告警触发后,运维切备用通道,5 分钟恢复。如果没有业务监控,要等运营发现订单变少才能定位到支付环节。
-
前端兼容性 Bug:一个新版本在 iOS 15.4 上白屏,Sentry 自动捕获、还原源码堆栈、录像回放。从用户首次报错到修复上线 15 分钟。
-
数据库慢查询:凌晨跑批任务突然变慢,P95 响应时间告警触发,定位到统计 SQL 走了全表扫描。当天加了索引,后续告警消失。
-
活动配置错误:运营配错了促销规则,客单价从 120 元跌到 60 元。业务指标告警发到运营群,5 分钟内修复。
分层监控的协作模式
不同角色看不同的监控面板:
| 角色 | 关注层 | 主要工具 | 面板内容 |
|---|---|---|---|
| 前端开发 | 第一层 | Sentry | JS 错误、白屏、Web Vitals |
| 后端开发 | 第二层 | Grafana | 接口成功率、延迟、QPS |
| 技术负责人 | 第三层 | Grafana | 核心流程漏斗、SLA |
| 运营/产品 | 第四层 | Grafana | 下单量、转化率、客单价 |
各看各的,出问题时不用在群里互相问“你那边正常吗”。
总结
业务监控和系统监控的关系,就像汽车仪表盘和发动机故障灯。发动机故障灯亮了,你知道车坏了;但仪表盘告诉你车速多少、还剩多少油、还能开多远。
搭建建议自上而下:
- 先上 Sentry:前端错误一键接入,5 分钟搞定。
- 再加 Prometheus + Grafana:接口监控和系统监控一步到位。
- 再补核心流程埋点:找出你的关键业务流程,埋上自定义指标。
- 最后建业务指标:把数据库里跑定时任务,暴露给 Prometheus。
四层全部搭完,你的系统就从一个“黑盒”变成了“透明盒”。用户还没发现问题,你已经知道了。
你的项目里做了几层监控?哪一层让你最有“救了一命”的感觉?欢迎评论区交流。