写在前面
在我们公司,订单团队想看"订单创建失败数",支付团队关心"支付超时率",库存团队盯着"库存扣减失败"......但他们不会写 PromQL,也不懂 Grafana 的 Panel 配置。
每次都要找 SRE 帮忙建 Dashboard:
- 业务团队:等 SRE 响应,少则半天,多则几天
- SRE 团队:重复劳动,一个 Dashboard 配 10 分钟,10 个业务组就是 100 分钟
- 后续维护:新增指标要手动改 Dashboard,删除指标要手动清理,Dashboard 数量爆炸
我们决定换个思路:让业务团队自助配置,让 SRE 从"Dashboard 工程师"中解放出来。
于是设计了一套基于"指标分组"的可视化方案:
- 管理员配置一次指标组,业务组自助查看
- 新增指标自动出现在菜单中,无需手动维护
- 系统自动适配时间范围、多维标签、指标类型
本文分享我们的设计思路与关键实现。
一、我们想解决什么问题?
1.1 痛点场景
假设你的 Prometheus 已经采集了 100 个业务指标:
order_create_failed_total (订单创建失败)
order_timeout_total (订单超时)
payment_failed_total (支付失败)
stock_insufficient_total (库存不足)
...(还有 96 个)
不同的业务组只关心自己的指标,但现状是:
- 所有指标混在一起,难以快速定位
- 每个业务组都要去 Grafana 手动创建 Dashboard
- 新增指标后,需要手动维护 Dashboard
我们想要的效果:
- 订单团队打开页面 → 点击"订单监控" → 自动展示订单相关的所有指标
- 支付团队打开页面 → 点击"支付监控" → 自动展示支付相关的所有指标
- 无需学习 PromQL,无需配置 Grafana
1.2 我们的解决方案(6步搞定)
光说不练假把式,下面是我们的完整思路:
步骤1:把指标列出来
系统自动从已有的告警规则中提取所有 Prometheus 指标,展示在管理页面:
✓ order_create_failed_total (订单创建失败)
✓ order_timeout_total (订单超时)
✓ payment_failed_total (支付失败)
✓ stock_insufficient_total (库存不足)
步骤2:按业务分组
管理员创建"指标组",把相关指标加进去:
📊 指标组:订单监控
├── order_create_failed_total
├── order_timeout_total
└── order_cancelled_total
💳 指标组:支付监控
├── payment_failed_total
├── payment_timeout_total
└── refund_error_total
配置时间:30 秒
步骤3:菜单自动生成
刷新页面后,左侧导航自动出现二级菜单:
markdown
系统菜单
└── Metrics查看
├── 订单监控 ← 自动生成,菜单名 = 指标组名
├── 支付监控 ← 自动生成
└── 库存监控 ← 自动生成
关键:无需手动编辑菜单配置,无需重启应用。
步骤4:点击就能看图
点击"订单监控"菜单,系统自动做三件事:
① 查询 Prometheus
查询:order_create_failed_total
查询:order_timeout_total
查询:order_cancelled_total
② 根据类型智能展示
- Counter(计数器)→ 显示整数:246 次
- Gauge(瞬时值)→ 显示整数:128 个连接
- Summary/Histogram → 显示小数:1.25 秒
③ 根据标签自动分曲线
同一个指标如果有多个标签,自动画成多条曲线:
ini
order_create_failed_total
├─ error_type=network, region=east → 蓝色曲线
├─ error_type=database, region=west → 红色曲线
└─ error_type=timeout, region=north → 绿色曲线
步骤5:选择时间范围
页面顶部提供时间选择器,系统自动调整采样粒度:
| 时间范围 | 采样间隔 | 为什么? |
|---|---|---|
| 15分钟 | 15秒/点 | 数据密集,精细查看 |
| 1小时 | 30秒/点 | 平衡性能和可读性 |
| 3小时 | 1分钟/点 | 避免数据点过多 |
| 6小时 | 2分钟/点 | 长期趋势观察 |
好处:图表不会密密麻麻,性能也不会差。
步骤6:角色分离
- 管理员(SRE):后台配置指标组,一次配置,全员使用
- 业务组:打开页面,点击自己关心的菜单,立即查看
业务组不需要:学 PromQL、配 Grafana、维护 Dashboard。
1.3 效果对比
| 对比项 | Grafana | 我们的方案 |
|---|---|---|
| 新增监控 | 创建Dashboard → 添加Panel → 写PromQL → 调样式(10分钟) | 添加到指标组(30秒) |
| 业务分组 | 手动创建多个Dashboard | 自动生成菜单 |
| 学习成本 | 需要学PromQL语法 | 点击菜单即可 |
| 维护成本 | 每个业务组一个Dashboard,数量爆炸 | 零维护,自动化 |
二、为什么不用 Grafana?
2.1 Grafana 的局限
Grafana 是优秀的工具,但在我们的场景下有些"重":
- 学习曲线陡峭:需要掌握 PromQL、Dashboard/Panel 概念
- 静态配置:每次新增指标需要手动添加 Panel,配置以 JSON 保存
- 缺少业务视角:默认按技术维度(服务、实例、端点),难以按业务场景分组
Grafana 更适合需要高度自定义、复杂查询的专业运维团队。
2.2 借鉴 SkyWalking 的思路
SkyWalking 在 APM 领域的可视化做得很好:
- 多维度自动分组(基于 Trace Tag)
- 智能图例展示(自动拼接标签)
- 根据指标类型智能选择展示方式
我们借鉴了这些思路,但适配到业务告警场景,无需 Agent。
三、如何处理不同类型的指标?
Prometheus 有四种指标类型,需要区别对待:
3.1 Counter 和 Gauge:显示整数
javascript
// Counter(只增不减):订单数、错误次数
// Gauge(可增可减):连接数、内存使用量
if (metricType === 'counter' || metricType === 'gauge') {
displayValue = Math.round(value) // 246 而不是 246.66
}
3.2 Summary 和 Histogram:保留小数
javascript
// 响应时间、百分位数等需要精确展示
displayValue = value.toFixed(2) // 1.25 秒
3.3 多维度标签自动分曲线
同一个指标可能有多个维度(Label),自动分组展示:
javascript
// 遍历 Label,拼接成图例
const nameParts = []
Object.keys(metric).forEach(k => {
nameParts.push(`${k}=${metric[k]}`)
})
const displayName = nameParts.join(', ')
// 结果:
// "payment_method=alipay, error_type=timeout, region=east"
// "payment_method=wechat, error_type=balance, region=west"
效果:一张图自动展示多条曲线,每条曲线对应一个标签组合,无需手动配置。
四、核心设计:"指标组"概念
4.1 为什么需要"指标组"?
我们需要一个介于 Dashboard 和 Panel 之间的抽象:
- Dashboard 太重:一个 Dashboard = 完整的可视化页面,配置复杂
- Panel 太细:单个图表,没有业务语义
- 指标组刚好:承载业务语义(订单监控、支付监控),又足够灵活
业务场景举例:
sql
📊 指标组:订单监控
├── 关键词规则:日志匹配 "OrderCreateFailed"
├── 关键词规则:日志匹配 "OrderTimeout"
└── 查询规则:SELECT COUNT(*) FROM orders WHERE status='cancelled'
4.2 动态菜单如何实现?
数据流:
配置指标组 → 保存到数据库 → 前端加载 → 动态生成菜单
动态特性:
- 新增指标组 → 菜单自动出现
- 删除指标组 → 菜单自动消失
- 修改指标组 → 菜单标题自动更新
- 启用/禁用 → 菜单显示/隐藏
关键:配置即生效,无需重启应用。
五、关键技术实现
5.1 前端怎么做?动态菜单实现
① 数据库表设计(简化版)
sql
-- 指标组表
CREATE TABLE alarm_metric_group (
id INT PRIMARY KEY,
group_name VARCHAR(100), -- 指标组名称
icon VARCHAR(50), -- 图标
enabled TINYINT(1) -- 是否启用
);
-- 指标组明细表
CREATE TABLE alarm_metric_group_item (
id INT PRIMARY KEY,
group_id INT, -- 指标组ID
rule_type VARCHAR(20), -- 规则类型:keyword/query
rule_id INT, -- 规则ID
metric_name VARCHAR(200) -- Prometheus指标名称
);
② 前端状态管理(Vuex)
javascript
// 应用启动时加载指标组
const actions = {
async loadGroups({ commit }) {
const res = await getMetricGroupList({ enabled: 1 })
commit('SET_GROUPS', res.data || [])
}
}
③ 动态菜单渲染(核心逻辑)
javascript
// Sidebar 组件
computed: {
menuRoutes() {
// 静态菜单
const staticMenus = [
{ name: 'RuleManage', path: '/rule-manage', meta: { title: '规则管理' }}
]
// 动态菜单:从 Store 获取指标组
const dynamicMenus = this.$store.state.metricGroups.list.map(group => ({
name: `MetricView_${group.id}`,
path: `/metric-view/${group.id}`,
meta: {
title: group.group_name, // 菜单名 = 指标组名
icon: group.icon
}
}))
// 合并:创建"Metrics查看"父菜单
if (dynamicMenus.length > 0) {
const parent = {
name: 'MetricsView',
meta: { title: 'Metrics查看' },
children: dynamicMenus // 动态子菜单
}
return [...staticMenus, parent]
}
return staticMenus
}
}
关键点:
- 使用
computed属性,数据变化时自动重新渲染 - 路由参数化:
/metric-view/:id,通过 ID 区分指标组
5.2 后端怎么查?接口设计
接口1:查询启用的指标组
ini
GET /api/metric-groups?enabled=1
返回:
json
{
"code": 200,
"data": [
{"id": 1, "group_name": "订单监控", "icon": "shopping-cart"},
{"id": 2, "group_name": "支付监控", "icon": "wallet"}
]
}
接口2:查询指标组的详细指标
bash
GET /api/metric-groups/:id/metrics
核心 SQL(关联查询):
sql
SELECT
i.metric_name,
CASE
WHEN i.rule_type = 'keyword' THEN k.rule_name
WHEN i.rule_type = 'query' THEN q.query_name
END AS rule_name
FROM alarm_metric_group_item i
LEFT JOIN alarm_keyword_rule k ON i.rule_type = 'keyword' AND i.rule_id = k.id
LEFT JOIN alarm_query_rule q ON i.rule_type = 'query' AND i.rule_id = q.id
WHERE i.group_id = ?
接口3:查询 Prometheus 指标数据
sql
GET /api/metrics/overview?start=xxx&end=yyy&step=15
后端根据时间参数查询 Prometheus,返回时序数据。
5.3 时间范围自适应
设计思路:时间范围越大,采样间隔越粗,避免数据点过多。
javascript
getTimeRangeParams() {
const now = Math.floor(Date.now() / 1000)
let start, step
switch (this.timeRange) {
case '15m': start = now - 15*60; step = 15; break // 15秒
case '1h': start = now - 60*60; step = 30; break // 30秒
case '3h': start = now - 3*60*60; step = 60; break // 1分钟
case '6h': start = now - 6*60*60; step = 120; break // 2分钟
}
return { start, end: now, step }
}
六、完整流程示意
6.1 配置流程(管理员操作)
配置时间:30 秒
6.2 查看流程(业务组操作)
查看时间:< 2 秒
七、适用场景与局限性
7.1 适合的场景
- 业务告警监控:已有告警规则,需要按业务组分组查看
- 日志分析场景:基于关键词规则匹配日志
- 数据库查询监控:基于 SQL 查询生成指标
- 中小规模团队:指标组 < 50 个,单组规则 < 100 条
7.2 不适合的场景
- 复杂的 PromQL 查询:需要聚合、计算、rate() 等复杂运算 → 用 Grafana
- 高度自定义 Dashboard:需要特殊布局、自定义图表类型 → 用 Grafana
- 分布式追踪:需要 Trace 级别的监控 → 用 SkyWalking
7.3 性能考虑
当前设计的性能边界:
- 指标组数量:建议 < 50 个
- 单个指标组规则:建议 < 100 条
- 平均响应时间:< 2 秒
可能的瓶颈:
- 指标组过多时,菜单加载较慢 → 可增加缓存
- 大量指标同时查询可能超时 → 可限制并发数
- 大量图表同时渲染可能卡顿 → 可使用虚拟滚动
八、写在最后
这个方案上线后,业务团队的反馈是:"终于不用等 SRE 了,自己点点就能看。"
而 SRE 团队也从"Dashboard 工程师"回归到真正的稳定性建设中------不再被 10 分钟一个的配置请求打断。
它不是万能的:
- 不适合复杂的 PromQL 查询
- 不适合高度自定义的图表
- Grafana 依然是专业用户的首选
但如果你也面临:
- 业务组太多,每个都要单独配 Dashboard
- 指标太多,找起来很费劲
- 非技术人员不会用 Grafana
或许这个轻量级思路值得参考。
欢迎讨论:你们是怎么让非运维人员用好 Prometheus 的?留言聊聊你的方案。