资讯动态

5分钟给cpp-httplib接上请求追踪:ID、耗时、状态码一条日志全带出来

发布时间:2026/9/10 14:40:18 来源:尧图企业网站定制
5分钟给cpp-httplib接上请求追踪ID、耗时、状态码一条日志全带出来【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址: https://gitcode.com/GitHub_Trending/cp/cpp-httplibcpp-httplib 是一个 header-only 的 C HTTP 库一个httplib.h就能跑起来。但服务一旦上线这个请求是谁发的、跑了多久、最后返回了什么就全靠自己猜。读完这篇文章你只需要改不到 30 行代码就能让每个请求都带着 request ID、耗时和状态码进日志排查时直接按 ID 过滤。5分钟跑通一个最小追踪例子思路很简单请求进门时打一张时间戳和 ID 的标签请求结束时统一结算。cpp-httplib 的四个钩子刚好覆盖这两端——set_pre_routing_handler()在路由前对每个请求生效set_logger()在请求结束响应发出前时触发中间用res.user_data传数据。下面这份代码可以直接编译运行g -stdc17 -O2 trace_demo.cc -o trace_demo -lpthread把httplib.h放在同目录#include httplib.h // header-only 库单头文件搞定 #include atomic #include chrono #include iostream int main() { httplib::Server svr; // 进门登记给每个请求生成 ID并把开始时间塞进 user_data svr.set_pre_routing_handler( [](const httplib::Request req, httplib::Response res) { static std::atomicunsigned long long seq{0}; res.set_header(X-Request-ID, std::to_string(seq)); // ID 同时回写给客户端 res.user_data.set(start, std::chrono::steady_clock::now()); return httplib::Server::HandlerResponse::Unhandled; // 放行继续走业务路由 }); // 出门结算请求结束时 logger 触发此时响应已经生成能算出真实耗时 svr.set_logger([](const httplib::Request req, const httplib::Response res) { auto *start res.user_data.getstd::chrono::steady_clock::time_point(start); auto ms std::chrono::duration_caststd::chrono::milliseconds( std::chrono::steady_clock::now() - *start).count(); std::cout req.remote_addr req.method req.path - res.status ms ms std::endl; }); svr.Get(/hello, [](const httplib::Request , httplib::Response res) { res.set_content(hello, text/plain); }); svr.listen(0.0.0.0, 8080); }启动后访问http://localhost:8080/hello控制台会看到类似这样的输出用curl -i还能在响应头里拿到X-Request-ID127.0.0.1 GET /hello - 200 1ms 127.0.0.1 GET /nope - 404 0ms注意第二条没匹配到路由的 404 也会被记录因为 pre-routing 钩子拦的是所有请求不挑路由。原理速拆谁登记、谁结算把一次请求想象成餐厅流程set_pre_routing_handler()是门口领位员。客人请求还没被分配到具体餐桌路由匹配之前领位员先做三件事发个号request ID、在号牌上写下进门时间user_data.set(start, ...)、决定放不放行返回Handled直接拒绝返回Unhandled放行。res.user_data是号牌本身。它挂在Response上任意类型随便塞后面的处理器都能通过res.user_data.getT(start)取回。官方 cookbook 里就叫它handlers 之间的数据交接袋参考 S12. Pass Data between Handlers with res.user_data。set_logger()是结账柜台。客人离桌后响应生成完毕、即将发往客户端统一结算号牌上的进门时间还在减去现在就是耗时状态码也已经写在res.status里了。对应的真实定义都在httplib.h这一个文件里关键就四行using Logger std::functionvoid(const Request , const Response ); // 请求结束时触发 Server set_pre_routing_handler(HandlerWithResponse handler); // 路由前 Server set_post_routing_handler(Handler handler); // 路由后、发响应前 class UserData; // res.user_data任意类型的口袋为什么不能全塞进 pre-routing 里打印因为那时业务 handler 还没跑res.status还是空的、耗时也算不出。登记和结算必须分开这正是埋点 结算两步的结构。生产环境你会踩的坑1. 耗时永远是 0或者get出来的指针是空的现象日志里耗时列全是 0加断点发现user_data.get...(start)返回nullptr。 原因set和get的类型必须逐字符一致——存成std::chrono::steady_clock::time_point却按const ...time_point取类型 ID 对不上直接返回空。 修复set和getT用同一个精确类型取到后先判空再解引用。2. 想在 pre-routing 里打印状态码结果全是 0现象埋点位置打印res.status输出 0 或 200 的固定值。 原因pre-routing 跑在业务 handler 之前响应根本没生成此时打印状态码没有意义。 修复状态码相关的日志一律挪到set_logger()里埋点钩子只负责登记。3. 高并发下日志行互相串现象QPS 上去后一条日志被拆成两行、和别的请求的输出交错。 原因cpp-httplib 每个 worker 线程同步执行 logger多线程同时写std::cout没有任何锁。 修复logger 里加std::lock_guardstd::mutex lk(log_mtx);或改用自带线程安全的日志库。4. 在 logger 里直接写盘或远程上报QPS 被拖垮现象接入追踪后接口延迟整体上升压测吞吐掉档。 原因logger 运行在请求处理线程上是同步的磁盘 I/O 或网络上报的耗时会原样加到每个请求上。 修复logger 里只做queue.push(record);入队由独立后台线程异步落盘或上报。自建埋点、set_logger 和 OpenTelemetry 的取舍方案适用场景取舍pre-routing user_data logger本文自己打点要 request ID、耗时、慢请求告警代码最少、零依赖但跨服务传播要自己拼请求头只用set_logger()只需要 nginx 风格的 access log十几行搞定但没有请求级预处理无法注入 ID 或计时OpenTelemetry C SDK要和现有 APM 平台、其他语言的分布式链路打通换来标准 traceparent 传播和采样代价是多一套 SDK 依赖和配置如果走分布式方案客户端侧的传播在 cpp-httplib 里也很直接把 trace 上下文放进默认头下游服务读出来即可例如client.set_default_headers({{traceparent, ctx_string}});。到这里开头的承诺已经兑现不到 30 行你的每个请求都有了可追踪的 ID、真实耗时和状态码。下一步就做一件事把示例里的计数器换成 UUID然后curl -i http://localhost:8080/hello对着响应头里的X-Request-ID去日志里 grep 一遍——能对上追踪链路就算真正通了。【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址: https://gitcode.com/GitHub_Trending/cp/cpp-httplib创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价