C++ 怎么在 Prometheus 中自定义 Counter、Gauge 和 Histogram 监控指标

冬明大大_7285

冬明大大_7285

2026-11-03

688人浏览

原创

Counter是单调递增的累计指标,仅可增加或重启时归零,适用于请求总数等;Gauge是可增可减的瞬时值指标,反映当前状态,如活跃连接数或内存使用量。

c++ 怎么在 prometheus 中自定义 counter、gauge 和 histogram 监控指标

怎么用 cpp-prometheus 定义 Counter 和 Gauge

cpp-prometheus 是 C++ 生态中最常用的 Prometheus 客户端,它不依赖 Boost,轻量且线程安全。Counter 适合累计值(如请求总数),Gauge 适合可增可减的瞬时值(如当前活跃连接数)。关键点是:必须在全局或静态生命周期内创建指标对象,不能在函数栈上临时构造。

常见错误是把 prometheus::Counter 或 prometheus::Gauge 声明在局部作用域里,导致对象析构后指标注册失效,Prometheus 抓取时返回空或报错 no metrics found。

  • 使用 prometheus::Registry 注册指标,通常一个进程只需一个全局 registry
  • 通过 registry.Add() 添加指标,返回智能指针(std::shared_ptr),确保生命周期可控
  • Counter 调用 .Increment(),Gauge 支持 .Set()、.Increment()、.Decrement()
  • 避免在多线程中对同一 Gauge 频繁 .Set() —— 内部有原子操作但高并发下仍有开销,不如先算好再设一次
// 全局 registry 和指标
auto registry = std::make_shared<:registry>();
auto http_requests_total = registry->Add<:counter>(
    prometheus::Counter::Builder().Name("http_requests_total").Help("Total HTTP requests.").Register());
auto active_connections = registry->Add<:gauge>(
    prometheus::Gauge::Builder().Name("active_connections").Help("Current active connections.").Register());

// 在 handler 中
http_requests_total->Increment();
active_connections->Set(current_conn_count);
</:gauge></:counter></:registry>

怎么给 Histogram 加 label 并正确观测延迟

Histogram 不是直接记录原始值,而是按预设 bucket 划分区间并计数。C++ 客户端默认 bucket 是 {.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}(单位秒),如果业务延迟普遍在毫秒级,这些 bucket 会全部落在第一个桶里,导致直方图完全失真。

必须显式调用 .Bucket(...) 构造器重设 bucket,单位统一用秒(float),别错用毫秒整数。

  • label 必须在构建时声明,例如 .Label("method"),之后观测时用 .Observe(value, {{"method", "GET"}})
  • 不要在循环里反复调用 .Observe() 带不同 label 组合 —— 每种 label 组合都会生成独立时间序列,label 维度爆炸会拖垮 Prometheus 存储和查询
  • 如果只有少量 method 类型(如 GET/POST),label 合理;如果 method 来自 URL path 参数,应改用通用 label(如 "endpoint")或聚合到 "other"
auto request_latency_ms = registry->Add<:histogram>(
    prometheus::Histogram::Builder()
        .Name("http_request_duration_seconds")
        .Help("HTTP request latency in seconds.")
        .Label("method")
        .Bucket(.001, .005, .01, .025, .05, .1)  // 注意:单位是秒,对应 1ms–100ms
        .Register());

// 正确:label 值固定、有限
request_latency_ms->Observe(elapsed_seconds, {{"method", "GET"}});

// 错误:path 动态拼接 → 产生无限 series
// request_latency_ms->Observe(elapsed_seconds, {{"path", req.path()}});
</:histogram>

怎么暴露指标 endpoint 并避免线程阻塞

cpp-prometheus 自带 prometheus::Exposer,但它默认用阻塞式 socket 实现 HTTP server,不支持 HTTPS、gzip、超时控制,更关键的是:若某个抓取请求卡住(如客户端网络异常),整个 exposer 线程会 hang 住,后续所有指标无法更新。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

生产环境强烈建议把指标导出交给主 HTTP 框架(如 cpp-httplib、crow 或 nginx 反向代理),只用 cpp-prometheus 管理指标数据,不负责 HTTP 服务。

  • 调用 registry->Collect() 获取 std::vector<:metricfamily>,再手动序列化为文本格式
  • 序列化必须用 prometheus::TextSerializer,不能自己拼字符串 —— label 值含空格、引号、反斜杠时会破坏格式,Prometheus 解析失败报 text format parsing error
  • 如果坚持用 Exposer,务必设置 exposer.Start(port, "127.0.0.1") 绑定本地回环,禁止公网暴露;并监控其线程状态(比如定期 check 是否 alive)
// 用 httplib 提供 /metrics endpoint
server.Get("/metrics", [&](const auto& req, auto& res) {
    auto families = registry->Collect();
    prometheus::TextSerializer serializer;
    std::stringstream ss;
    serializer.Serialize(families, ss);
    res.set_content(ss.str(), "text/plain; version=0.0.4");
});

为什么 Counter 的值有时“倒退”或归零

Counter 语义上不应下降,但 C++ 客户端本身不做单调性校验。实际出现“倒退”基本只有两个原因:进程重启导致内存中 Counter 重置为 0;或者多个进程共用同一指标名但没加 instance label,Prometheus 做 federation 或 remote_write 时发生覆盖。

另一个隐蔽问题是:用 prometheus::Counter::Add(n) 传入负数 —— 编译能过,运行时不会报错,但违反 Counter 语义,下游计算 rate() 时会出现负速率,Grafana 图表断崖式下跌。

  • 永远用 .Increment() 或 .Increment(1.0),避免手写 .Add(-1)
  • 多实例部署时,在 registry 构建前,给所有指标加 .Label("instance"),值设为 hostname:port 或唯一 ID
  • 如果必须跨进程共享计数(如分布式任务完成数),Counter 不适用,该换用外部存储(Redis + Lua)或 Pushgateway(仅限批处理场景)

真正棘手的是 histogram 的 bucket 边界和 label 组合,这两处一旦上线就很难调整 —— Prometheus 不支持删除或 rename 指标,旧 series 会长期残留。上线前务必用真实流量压测,确认 bucket 分布合理、label cardinality 可控。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
c++和c语言的区别有哪些
c++和c语言的区别有哪些

c++和c语言的区别:1、面向对象编程(OOP)支持不同;2、新增特性不同;3、标准库不同;4、编译方式不同;5、命名空间不同等等。想了解更多c++的相关内容,可以阅读本专题下面的文章。

2024.03.14

2288

9

c++和python学习顺序推荐
c++和python学习顺序推荐

一般建议先学习C++,再学习Python,因为这样可以逐步从较为底层的编程语言向更高级的语言过渡。想了解更多python的相关内容,可以阅读本专题下面的文章。

2024.03.14

1039

6

python和c++学习性价比分析
python和c++学习性价比分析

Python易于学习,广泛应用于Web开发、数据科学和人工智能等领域,但性能较低。C语言性能高,适用于对性能要求较高的场景,如游戏开发和系统编程,但学习曲线陡峭,错误处理复杂。想了解更多python的相关内容,可以阅读本专题下面的文章。

2024.03.14

447

5

c语言和c++一样吗
c语言和c++一样吗

c语言和c++是两种不同的编程语言,虽然有相似之处,但存在显著差异。c语言专注于过程式编程和系统级开发,以简洁、高效著称。c++作为c语言的超集,引入了面向对象编程,增强了代码组织和管理能力,但学习曲线也更陡峭。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

347

5

c语言和c++先学哪个好
c语言和c++先学哪个好

初学者选择学习c语言还是c++语言,需要根据个人学习目标、背景以及编程兴趣和预期应用方向来决定。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

426

5

c语言和c++的区别和联系
c语言和c++的区别和联系

c语言和c++是计算机科学领域应用广泛的编程语言。虽然它们有着相似的基础,但它们在语言类型、语法功能和内存管理方面存在着显著差异。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

600

5

c++软件中文更改教程
c++软件中文更改教程

对于 ide,可通过打开设置,找到语言设置,选择中文,并保存更改。对于非 ide 应用程序,可查找设置或选项,选择语言设置,更改为中文,并保存更改。想了解更多c++的相关内容,可以阅读本专题下面的文章。

2024.03.21

1429

9

python和java和c++学习性价比分析
python和java和c++学习性价比分析

Python以其易学性、丰富的库和活跃的社区而著称,适合数据科学、人工智能和Web开发。Java以其跨平台性、企业级应用开发和Android应用开发而闻名。C++以其底层控制能力、高效性能和游戏开发而著称。选择哪种语言取决于个人兴趣、职业方向和特定需求。想了解更多python和java和c++的相关内容,可以阅读本专题下面的文章。

2024.03.22

1237

7

c++和c语言学习顺序推荐
c++和c语言学习顺序推荐

对于初学者,建议先学习C语言,掌握编程基础后再转入C++,便于理解面向对象编程概念。有编程经验者可直接学习C++,快速接触高级编程技术。想了解更多c++和c语言的相关内容,可以阅读本专题下面的文章。

2024.03.25

1325

9

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ 手册教程
C++ 手册教程

共41课时 | 78.6万人学习

尚学堂ios初级视频教程
尚学堂ios初级视频教程

共77课时 | 21.4万人学习

C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习