一、背景
在业务系统监控中,Prometheus 指标越来越常见。很多应用、Exporter 或中间件都会直接暴露 /metrics 接口,格式类似:
text
xxxx_function_call_count{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 219310
xxxx_function_total_time{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 658011
这类指标直接接入 Prometheus 是比较自然的,但在很多企业现场,已有监控体系还是以 Zabbix 为主,告警、通知、权限、资产管理、运维流程也都围绕 Zabbix 建设。
所以就会出现一个实际需求:
应用暴露的是 Prometheus 指标,但需要接入 Zabbix 统一监控,并最终在 Grafana 中统一展示。
本文以 XXXX 系统的函数调用监控为例,介绍如何在 Zabbix 7.0 中采集 Prometheus 格式指标,并通过 Grafana 进行可视化。
本次重点采集两个指标:
text
xxxx_function_call_count
xxxx_function_total_time
其中:
text
xxxx_function_call_count:函数调用次数
xxxx_function_total_time:函数耗时
二、原始 Prometheus 指标示例
应用暴露的 /metrics 中,部分原始数据如下:
text
xxxx_function_total_time{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 658011
xxxx_function_total_time{func_id="L0100001",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 3.354317e+06
xxxx_function_total_time{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 1.442499e+07
xxxx_function_total_time{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_rzrq",xxxx_user="xxxxrzrq"} 5.386871e+06
xxxx_function_call_count{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 219310
xxxx_function_call_count{func_id="L0100001",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 1.080552e+06
xxxx_function_call_count{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 334051
xxxx_function_call_count{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_rzrq",xxxx_user="xxxxrzrq"} 42173
这里有一个很重要的点:
text
同一个 func_id 可能在不同 job、xxxx_user 下重复出现。
例如:
text
func_id="L0107011",job="xxxx_jzjy",xxxx_user="xxxx01"
func_id="L0107011",job="xxxx_rzrq",xxxx_user="xxxxrzrq"
因此,在 Zabbix 中设计监控项 key 时,不能只使用 func_id,否则会出现重复、冲突或取错值。
推荐唯一维度为:
text
func_id + xxxx_user
如果一个 Zabbix 主机采集多个业务主机的数据,还需要加上:
text
host
三、整体设计思路
最终设计如下:
text
HTTP 主监控项
↓
Prometheus 转 JSON,过滤出目标指标
↓
自动发现规则 1:发现 call_count
↓
监控项原型 1:函数调用次数
自动发现规则 2:发现 total_time
↓
监控项原型 2:函数耗时
可选:
↓
计算类型监控项:函数平均耗时
目标是:
text
一次采集 Prometheus 数据
自动发现多个 func_id
自动生成调用次数和耗时监控项
Grafana 根据 Zabbix 数据源做可视化
四、Zabbix 主监控项配置
1. 新建 HTTP agent 主监控项
在 Zabbix 中创建一个主监控项,例如:
text
名称:XXXX Prometheus Raw
键值:xxxx.prometheus.raw
类型:HTTP agent
信息类型:文本
URL:http://<业务地址>:<端口>/metrics
更新间隔:60s
URL 示例:
text
http://192.168.30.175:9100/metrics
实际端口按现场应用暴露的 Prometheus 端口填写。
五、Prometheus 转 JSON 只保留目标指标
Zabbix 7.0 的 Prometheus 转 JSON 预处理支持 Prometheus pattern,不是完整 PromQL,但可以使用类似 Prometheus label matcher 的写法。
我们只需要采集两个指标:
text
xxxx_function_call_count
xxxx_function_total_time
因此主监控项的预处理可以配置为:
text
Prometheus 转 JSON
参数填写:
text
{__name__=~"xxxx_function_call_count|xxxx_function_total_time"}
这行配置的含义是:
text
只匹配指标名为 xxxx_function_call_count 或 xxxx_function_total_time 的数据。
错误写法:
text
{xxxx_function_call_count|xxxx_function_total_time}
这个会报错:
text
无效的 Prometheus 模式
配置完成后,可以点击 Zabbix 预处理测试,确认返回 JSON 中包含如下两类数据:
text
xxxx_function_call_count
xxxx_function_total_time

六、为什么不建议只写一个指标
一开始可能会这样配置:
text
Prometheus 转 JSON:
xxxx_function_call_count
这样确实可以获取调用次数,但主监控项处理后的数据只剩:
text
xxxx_function_call_count
后续如果再想从这个主项中获取:
text
xxxx_function_total_time
就获取不到了。
因此,如果计划一个主项同时服务多个自动发现和多个监控项原型,主项预处理阶段就不能只保留一个指标。
这样主项处理后的 JSON 中同时包含两类指标,后续再通过不同自动发现规则区分。
七、自动发现规则设计
由于 call_count 和 total_time 是两个不同指标,后续还要分别生成不同的监控项,建议创建两个自动发现规则。
自动发现规则 1:函数调用次数发现
text
名称:XXXX Function Call Count Discovery
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.call_count.discovery
预处理使用 JSONPath,从主项 JSON 中筛选出调用次数指标:
text
$[?(@.name=="xxxx_function_call_count")]

LLD 宏配置:
text
{#FUNC_ID} $.labels.func_id
{#JOB} $.labels.job
{#xxxx_USER} $.labels.xxxx_user
自动发现规则 2:函数耗时发现
text
名称:XXXX Function Total Time Discovery
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.total_time.discovery
预处理 JSONPath:
text
$[?(@.name=="xxxx_function_total_time")]
LLD 宏配置:
text
{#FUNC_ID} $.labels.func_id
{#JOB} $.labels.job
{#xxxx_USER} $.labels.xxxx_user
这样两个自动发现规则虽然使用同一个主监控项,但各自只处理自己的指标。
八、监控项原型配置
1. 调用次数监控项原型
放在 XXXX Function Call Count Discovery 下。
text
名称:KCBP函数调用次数:{#JOB}/{#xxxx_USER}/{#FUNC_ID}
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.call_count[{#FUNC_ID},{#JOB},{#xxxx_USER}]
信息类型:数字(无正负)
主项已经是 Prometheus 转 JSON 后的 JSON,那么这里可以使用 JSONPath 提取,也可以调整结构为中间相关项。
json
$.[?(@.labels.xxxx_user == "{#xxxx_USER}" && @.labels.func_id == "{#FUNC_ID}")].value.first()
2. 耗时监控项原型
放在 XXXX Function Total Time Discovery 下。
text
名称:KCBP函数耗时:{#JOB}/{#xxxx_USER}/{#FUNC_ID}
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.total_time[{#FUNC_ID},{#JOB},{#xxxx_USER}]
信息类型:数字(浮点)
Json Path:
json
$.[?(@.labels.xxxx_user == "{#xxxx_USER}" && @.labels.func_id == "{#FUNC_ID}")].value.first()
九、Grafana 可视化设计
Grafana 使用 Zabbix 数据源后,可以基于自动发现出来的监控项做面板展示。
十、常见问题
1. 为什么自动发现要分两个?
因为 call_count 和 total_time 是两个不同指标,后续生成的监控项名称、key、单位、业务含义都不同。
虽然也可以一个发现规则里混合处理,但容易出现:
text
同一个 func_id + job + xxxx_user 被发现两次
后续监控项原型如果设计不好,会导致重复、冲突或取值混乱。
因此推荐:
text
call_count 一个发现规则
total_time 一个发现规则
2. 为什么 key 不能只写 func_id?
因为同一个 func_id 会出现在不同 job 和 xxxx_user 下。
错误示例:
text
xxxx.function.call_count[{#FUNC_ID}]
推荐:
text
xxxx.function.call_count[{#FUNC_ID},{#JOB},{#xxxx_USER}]
如果还有多个 host:
text
xxxx.function.call_count[{#xxxx_HOST},{#FUNC_ID},{#JOB},{#xxxx_USER}]
3. 这种方式是否减少了 HTTP 拉取的数据量?
不一定。
如果 Zabbix 主项 URL 是:
text
http://<exporter>:<port>/metrics
那么 Zabbix 仍然会先拉取完整 /metrics,然后通过 Prometheus 转 JSON 筛选目标指标。
这种方式减少的是:
text
Zabbix 后续处理和保存的数据量
但不减少:
text
HTTP 传输的数据量
如果要真正减少拉取数据量,可以考虑让 Zabbix 请求 Prometheus Server 的 /federate 接口,只返回指定指标。
示例:
text
http://prometheus:9090/federate?match[]={__name__=~"xxxx_function_(call_count|total_time)"}
实际使用时需要注意 URL 编码。
十一、总结
本文介绍了一种在 Zabbix 7.0 中采集 Prometheus 指标,并通过 Grafana 进行可视化的实践方式。
核心经验有两点:
第一,Prometheus 转 JSON 中如果要匹配多个指标,应该使用:
text
{__name__=~"xxxx_function_call_count|xxxx_function_total_time"}
而不是简单写:
text
{xxxx_function_call_count|xxxx_function_total_time}
第二,自动发现时要注意指标维度,不能只用 func_id,而应该使用:
text
func_id + job + xxxx_user
通过这种方式,可以在不改造业务系统的情况下,把 Prometheus 指标纳入 Zabbix 统一监控体系,同时借助 Grafana 实现更灵活的可视化展示。