尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

OpenTelemetry Demo C++ 服务编译全攻略:从环境准备到排错实践

发布时间:2026/9/26 13:20:46

资讯中心
01
ARTICLE

OpenTelemetry Demo C++ 服务编译全攻略:从环境准备到排错实践

OpenTelemetry Demo C++ 服务编译全攻略:从环境准备到排错实践
说实话第一次在 OpenTelemetry Demo 里翻currencyservice的源码目录时我愣了一下——整个 Demo 项目十几个服务Java、Go、Python 都有现成的容器镜像唯独这个 C 写的货币转换服务想跑起来得先自己搞定一堆依赖。最开始我以为是自己环境有问题后来才发现这个服务从一开始就是官方用来展示 C 可观测性链路的样板工程编译流程确实比别的服务曲折不少。这篇就专门聊聊opentelemetry-demo里currency服务的 C 项目编译流程从环境准备、依赖选型、CMake 配置到常见报错排查把我在本地和 CI 上跑通整个流程的经验整理出来。适合想在这个 Demo 基础上做二次开发、或者打算用 C 接 OpenTelemetry 的开发者参考。1. 先弄明白 Currency Service 在这个 Demo 里到底承担什么很多人直接跳到编译环节结果碰到报错就懵了因为根本不了解这个模块的项目结构和依赖关系的来龙去脉。我建议先花十分钟搞清楚它是什么后面编译时会省很多事。1.1 服务定位一个纯粹的 gRPC 计算节点在 OpenTelemetry Demo 的架构里currencyservice的角色非常简单接收前端或其他服务发来的货币转换请求把输入的金额从一个币种按照实时汇率转成目标币种。它对外暴露的是 gRPC 接口proto 定义在项目的pb目录下核心方法就两个——单个货币转换和批量货币转换。这个服务有意思的地方在于它几乎不做存储也没有数据库依赖核心计算全部在内存中完成汇率数据通过内存缓存的CurrencyConversionMap维护。这意味着编译出来的二进制只要跑起来就是一个 standalone 的高性能计算节点非常适合用来演示 C 服务的指标采集、链路追踪和日志关联。1.2 为什么偏偏用它来做 C 可观测性示范从技术选型的角度看货币转换是一个计算密集型的纯函数场景最能体现 C 在低延迟、高吞吐方面的优势。同时它的接口是标准 gRPC天然适合接入 OpenTelemetry 的 gRPC instrumention——你可以在不修改业务逻辑的情况下通过拦截器自动采集每个 RPC 调用的延迟、错误率和元数据。另外一个隐藏原因是这个服务里用了 Boost 的高精度数值计算库来做金额转换cpp_dec_float_50处理浮点误差的逻辑非常典型。也就是说编译这个项目你接触到的不是玩具代码而是真实项目里才会用的依赖组合gRPC、Protobuf、Boost、OpenTelemetry C SDK外加 Abseil 做基础库支持。1.3 编译复杂度的三个根源我总结了一下这个项目编译麻烦的点主要有三个第一依赖链很长。OpenTelemetry C SDK 本身依赖 AbseilgRPC 又依赖 Protobuf 和 AbseilBoost 又是一个相对独立的重量级库。这些依赖交织在一起版本稍微不匹配就会出现诡异的链接错误。第二CMake 配置项非常多。官方仓库的CMakeLists.txt里控制 OpenTelemetry 构建行为的选项有好几十个BUILD_TESTING、WITH_OTLP_HTTP、WITH_OTLP_GRPC、ABSL_ENABLE_INSTALL这些选项组合起来不同搭配直接决定你能不能编出可用的二进制。第三网络环境对构建结果影响巨大。这个项目官方推荐通过 vcpkg 管理依赖vcpkg 默认会把依赖源码下载到本地编译如果网络不稳定很容易在下载 grpc 源码包时断掉导致整个构建缓存失效。所以在动手之前别急着一行cmake命令就开跑先把环境和依赖理清楚。2. 编译之前的环境准备三件容易忽略的事这个项目的官方 README 写得很简略就说了一句用 CMake 构建但实际执行起来你会发现缺了下面任何一个条件编译都会卡住很久。2.1 编译器和 CMake 的最低版本要求OpenTelemetry C SDK 对编译器版本要求不低官方要求 C17 及以上这意味着 GCC 至少要 7.1Clang 至少要 6.0。但实测下来老版本编译器在编译 Abseil 时会遇到奇怪的报错比如std::result_of相关的问题所以我的建议是直接用 GCC 11 以上或者 Clang 14 以上省心很多。CMake 版本方面项目要求 3.24 以上。这个一定要先验证因为很多发行版默认源的 CMake 版本都比较老。不过现在的 CMake 安装很灵活直接去 Kitware 的官网下载预编译二进制或者用 pip 装cmake包都能快速搞定。我自己更喜欢用 pip 方式因为可以按项目目录切换版本不会污染系统环境。你可以先用这两条命令确认基础条件cmake --version g --version如果版本不达标后面的步骤都不用看了先升级。2.2 依赖管理vcpkg 还是系统包管理器这是整个编译流程里最大的一个岔路口。OpenTelemetry Demo 的官方构建脚本用的是 vcpkg这是因为 vcpkg 能把 gRPC、Protobuf、Abseil、Boost 这些依赖固定到一份 manifest 文件里保证所有人编出来的东西行为一致。在我本地的 Ubuntu 环境上我对比了两种方式系统包管理器apt install libgrpc-dev protobuf-compiler libprotobuf-dev libabsl-dev libboost-dev优点是快几分钟搞定缺点是版本不可控Ubuntu 自带的 Protobuf 版本很可能和 OpenTelemetry SDK 要求的版本对不上而且老版本 Ubuntu 根本没有 Abseil 的包。vcpkg完全按项目vcpkg.json里的版本约束来一个依赖一版隔离性好缺点是首次构建要编译源码gRPC 和 Abseil 加起来可能要编半小时以上。我的建议是如果你只是临时跑一下 Demo用系统包管理器能省不少时间但要接受高概率的版本兼容问题如果你想长期维护这个项目老老实实用 vcpkg。我最后选择的是 vcpkg因为后面踩的 Protobuf 版本冲突的坑基本都是系统包版本不匹配引起的。2.3 网络和缓存策略别让构建任务断在半路vcpkg 在构建时会先从 GitHub 拉取源码包然后执行三方库的 CMake 构建。这个过程中最容易出现的问题就是网络抖动导致源码包下载失败。vcpkg 有个特性是下载失败的包会在下一次构建时重试但有时候重试也会失败然后你会看到一堆乱码般的报错。我的处理办法是先把常用依赖包的源码都手动下载好放到 vcpkg 的 downloads 目录里。这听起来有点笨但在团队内网或者网络受限的环境下这是最靠谱的离线构建策略。vcpkg 还支持二进制缓存配置方法很简单export VCPKG_BINARY_SOURCESfiles,/opt/vcpkg_cache,readwrite这样 vcpkg 会把编译好的二进制包缓存到本地目录第二次构建就不再重复编译同一个依赖了。这个配置我强烈建议加上因为 gRPC 的编译在配置缓存前后时间差距可以达到二十分钟。3. 完整编译流程从拉代码到拿到二进制环境准备好之后就可以进入正式的编译流程了。我会按实际的执行顺序把每一步的命令和背后的原理都说清楚。3.1 拉取代码与分支选择OpenTelemetry Demo 的代码托管在 GitHub 仓库里直接 clone 就可以。需要注意的是currencyservice在仓库里的路径是src/currencyservice/不要跑到根目录的CMakeLists.txt里去找它。git clone https://github.com/open-telemetry/opentelemetry-demo.git cd opentelemetry-demo/src/currencyservice分支选择上有一点容易踩坑OpenTelemetry C SDK 的 API 变动比较频繁而 Demo 仓库的 main 分支通常会跟进最新的 SDK 开发版本。如果你用的是 release 分支的 Demo最好让 vcpkg manifest 里锁定的版本来决定 SDK 源码不要自己随意指定一个 SDK release 版本否则很容易出现接口不匹配。3.2 用 CMake 配置构建目录currencyservice使用的是标准的 out-of-source 构建方式也就是说最终编译生成的中间文件和二进制不会污染源码目录。建议新建一个build目录mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease \ -DCMAKE_TOOLCHAIN_FILE/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake \ ..这里有一个细节值得说CMAKE_TOOLCHAIN_FILE必须要指向 vcpkg 的 toolchain 文件这个文件不是给交叉编译用的而是让 CMake 知道依赖包应该从 vcpkg 的 installed 目录里寻找。如果没有加这个参数CMake 会在系统路径里找 gRPC 和 Protobuf大概率会找到错误版本。在 Demo 项目的CMakeLists.txt里OpenTelemetry SDK 的发现方式是通过find_package(opentelemetry-cpp CONFIG REQUIRED)。如果你用的是系统包管理器安装的 OpenTelemetry这个find_package可能会因为找不到配置文件而直接失败。3.3 核心构建命令与中间产物配置成功后直接执行编译。这一步建议多用几个并行任务来加速cmake --build . -j$(nproc)整个编译过程会经历三个阶段第一阶段是编译项目自身生成的 Protobuf 和 gRPC 代码。currencyservice的 CMake 配置里Protobuf和gRPC的插件会自动生成currency_service.grpc.pb.cc和currency_service.pb.cc这些文件。这里最直观的体验就是你会在编译日志里看到一个grpc_cpp_plugin的调用过程。第二阶段是链接静态库。这里要盯住是否有未定义的 Boost 符号报错因为 OpenTelemetry SDK 和 gRPC 都会引入复杂模板稍有不慎链接器就会报一大堆undefined reference。第三阶段是生成最终可执行文件。如果一切顺利你会在build目录下找到名为currencyservice的二进制文件。用ldd检查依赖时会看到一连串的 gRPC、Protobuf、Abseil 相关动态库。3.4 一个完整的 CMake 参数参考表为了方便你对照我把配置阶段常见的参数含义整理成了一张表参数作用我的推荐值CMAKE_BUILD_TYPE编译模式影响优化级别和调试信息生产用Release调试用DebugCMAKE_TOOLCHAIN_FILE指向 vcpkg 的 toolchain 文件用于发现依赖必须配置CMAKE_PREFIX_PATH手动指定依赖库安装路径如果依赖不在默认路径建议配置BUILD_TESTING是否编译测试代码首次构建建议关闭减少编译量WITH_OTLP_GRPC启用 OTLP/gRPC 导出器开启否则无法上报 tracesWITH_OTLP_HTTP启用 OTLP/HTTP 导出器按后端需求选择gRPC_BUILD_GRPCPP_CPP_PLUGIN是否生成 gRPC C 插件必须开启否则 proto 生成会失败这些参数并不是每个都能直接传给你的 CMake 调用有些需要在进行 vcpkg 安装时就通过环境变量指定。这也是新手最容易懵的地方——明明在命令行里写了-DWITH_OTLP_GRPCONCMake 却提示这个选项不存在。因为相关选项实际上是 OpenTelemetry C SDK 的 CMake 参数而不是 Demo 项目自身的参数。4. 编译脚本拆解官方 Dockerfile 里藏的构建思路OpenTelemetry Demo 为了让大家能一键启动在currencyservice目录下提供了一个 Dockerfile。很多人会直接docker compose up用现成镜像但如果你看懂了 Dockerfile 里的构建脚本就相当于看懂了整个编译流程的官方推荐路径。4.1 为什么官方选择多阶段构建官方 Dockerfile 用的是多阶段构建模式。第一个阶段叫 builder里面会安装 vcpkg、拉取依赖源码、执行 CMake 编译第二个阶段是 runtime只把编译好的二进制和运行所需的最小动态库复制进去。这种设计非常符合 C 服务的特性编译环境和运行环境的诉求完全不同。编译环境需要完整的头文件、CMake 工具链、源码缓存体积动辄几个 GB而运行环境只需要二进制和共享库压缩到几十 MB 就能跑。我本地模拟这个流程时也把构建产物单独放到了/opt/build下避免污染运行目录。4.2 给本地环境写一个可复用的构建脚本虽然官方有 Dockerfile但在本地直接编译调试时我建议自己维护一个简单的 shell 脚本。这样每次改动代码后重建时不需要重复输入一堆 CMake 参数。我的脚本大概是这样的#!/bin/bash set -euo pipefail VCPKG_ROOT${VCPKG_ROOT:-/opt/vcpkg} BUILD_DIR${BUILD_DIR:-$(pwd)/build} export VCPKG_BINARY_SOURCESfiles,/opt/vcpkg_cache,readwrite cmake -S . -B $BUILD_DIR \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_TOOLCHAIN_FILE$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \ -DBUILD_TESTINGOFF cmake --build $BUILD_DIR -j$(nproc)这个脚本的核心价值不是省几条命令而是通过VCPKG_BINARY_SOURCES设置二进制缓存把每次从零编译的时间从半个多小时压缩到一两分钟。实测在第二次重建时因为 gRPC 和 Abseil 的二进制包已经有缓存几乎可以直接跳到项目源码自身的编译。4.3 子模块和第三方代码的更新策略currencyservice并没有把 OpenTelemetry C SDK 作为子模块嵌入项目目录而是完全依赖 vcpkg manifest 来拉取。这意味着第三方代码的版本锁定完全由vcpkg.json决定。修改依赖版本的常规做法是编辑vcpkg.json里面的版本号然后重新执行 cmake 配置。这里特别提醒一点vcpkg 的 manifest 模式会在工程根目录自动创建一个vcpkg_installed目录不要把它塞进 git 版本控制。我第一次配置时没加.gitignore结果把几千个第三方库文件都提交上去了非常痛苦。5. 编译期高频报错与完整排查链路老实说我研究这个项目编译流程的一大半时间都花在排错上。下面这些报错基本覆盖了你能遇到的大多数情况。我不直接给答案先说清楚排查思路这样下次遇到类似问题你也能自己定位。5.1 grpc_cpp_plugin 路径找不到报错特征Protoc execution failed gRPC C plugin not found in path这个问题的本质是 CMake 在生成 gRPC 代码时需要调用grpc_cpp_plugin这个可执行文件来自动生成*.grpc.pb.cc文件。如果你是通过系统包管理器装的 gRPC插件通常在/usr/bin/grpc_cpp_plugin如果通过 vcpkg 安装则路径在 vcpkg 的 installed 目录下。排查链路是这样的先确认grpc_cpp_plugin是否存在于系统中find / -name grpc_cpp_plugin -type f 2/dev/null。如果存在在 CMake 配置时指定-DgRPC_CPP_PLUGIN_EXECUTABLE/path/to/grpc_cpp_plugin。如果不存在说明 gRPC 安装不完整需要重新安装 gRPC 的 C 插件组件。这个报错最常见的诱因是只装了 gRPC 的运行时库忘了装开发工具链里的代码生成插件。5.2 Protobuf 版本冲突报错特征Protobuf headers and library versions do not match.这个问题在 OpenTelemetry C SDK 上尤其常见因为 SDK 本身通过 vcpkg 引入了一个特定版本的 Protobuf而你系统里可能有一个不同版本的 Protobuf。CMake 在查找时先找到系统的后面又碰到 vcpkg 的两边头文件不一致就会直接炸掉。排查链路查看cmake --build的完整日志找到实际调用的 protoc 路径。它可能会输出Using protoc at /usr/bin/protoc这样的提示。对比实际 protoc 版本和 CMake 找到的 Protobuf 库版本protoc --version。确保两者一致。根治法是在 CMake 配置时加-DProtobuf_PROTOC_EXECUTABLE/path/to/vcpkg/protoc强制让 protoc 和库来自同一套依赖。我在一次构建中遇到的问题就是系统自带 Protobuf 3.6.1而 vcpkg manifest 需要 3.24.x不加参数直接编结果生成的 pb.cc 文件接口全不对。5.3 Boost 符号未定义报错特征undefined reference to boost::multiprecision::...currencyservice的汇率计算使用了boost::multiprecision::cpp_dec_float_50这是 Boost 的任意精度浮点库。这种情况下链接器必须能找到对应的 Boost 头文件和库而且编译选项要允许使用这个库的含 C14/C17 标准。排查链路确认 Boost 是否安装dpkg -l | grep boost或vcpkg list输出里找 boost。检查 CMake 是否能找到 Boost在配置阶段加-DCMAKE_MESSAGE_LOG_LEVELVERBOSE查看搜索路径。如果 Boost 是通过 vcpkg 安装的检查有没有开启boost-multiprecision这个 feature。这里有个坑值得单独说multiprecision是 header-only 的库大部分情况只需要头文件但如果你的代码里用到了一些需要预编译的 Boost 组件比如 Boost::system就必须额外链接。项目里常见的报错其实是漏了Boost::system这种非 header-only 组件。5.4 OpenTelemetry SDK 版本与 Demo 代码不匹配报错特征error: ‘trace’ is not a member of ‘opentelemetry’这种错误虽然表面是编译错误但根源是版本错位。OpenTelemetry C SDK 的命名空间和接口在演进过程中发生过比较大的调整比如早期版本用opentelemetry::trace后来版本用opentelemetry::sdk::trace。如果 vcpkg 锁定的 SDK 版本和 Demo 代码开发的版本不一致就会出现这种接口没了的情况。排查链路查看vcpkg.json中锁定的 opentelemetry-cpp 版本号。查看 Demo 仓库的提交历史确认最近一次修改 currencyservice 时使用的 SDK 版本。如果两者不一致比较合理的做法是调整 vcpkg.json 版本到项目提交时代附近的版本而不是硬改代码适配新版 SDK。5.5 一份 Ctrl-C / Ctrl-V 可用的排障对照表症状可能原因解决方向protoc 报错找不到插件gRPC 开发组件缺失安装 grpc-cpp-plugin 或重新 vcpkg 安装 grpcpb.cc 文件内函数签名不一致Protobuf 库和 protoc 版本不一致指定Protobuf_PROTOC_EXECUTABLE到同一套Boost 模板实例化报错编译器对 C17 支持不完整升级 GCC 或 Clangopentelemetry 头文件找不到vcpkg toolchain 未生效检查CMAKE_TOOLCHAIN_FILE路径runtime 找不到动态库运行环境缺 LD_LIBRARY_PATHexport LD_LIBRARY_PATHvcpkg_installed/libCMake 找不到 gRPCConfig.cmakegRPC 安装方式不一致统一从 vcpkg 或系统装不要混用编译极慢CPU 跑满首次编译 gRPC/Abseil开启 vcpkg 二进制缓存5.6 编译完成后 VSCode 报红的处理这个其实不算编译报错但编译成功后打开源码目录VSCode 的 IntelliSense 十有八九会给你画满红色波浪线因为 IntelliSense 默认的 include 路径根本不知道 vcpkg 的头文件在哪。解决方式很简单在.vscode/c_cpp_properties.json里指定 includePath把 vcpkg 的 installed 目录加进去例如{ configurations: [ { name: Linux, includePath: [ ${workspaceFolder}/**, /opt/vcpkg/installed/x64-linux/include ], intelliSenseMode: linux-gcc-x64 } ], version: 4 }如果项目里有 CMakePresets.jsonVSCode 的 CMake Tools 插件会自动读取它IntelliSense 也会跟随 CMake 的 target 自动解析 include 路径这种情况下报红的问题会少很多。6. 跑起来之后的验证与可观测性检查编译只是第一步真正验证项目是否成功还得到运行时去看。currencyservice作为可观测性 Demo 的一部分光能启动还算不上完成还得确认链路追踪和指标能正常上报。6.1 最小运行配置编译得到的currencyservice二进制启动时会读取几个环境变量包括服务端口8080、OTLP 接收端地址等。最简启动方式export OTEL_EXPORTER_OTLP_ENDPOINThttp://localhost:4317 export OTEL_RESOURCE_ATTRIBUTESservice.namecurrencyservice ./build/currencyservice启动之后你可以通过 gRPC health check 来确认服务可用。因为该服务实现了 gRPC 健康检查协议所以最简单的方式是写个小客户端或者直接看日志里的启动提示。6.2 验证 OpenTelemetry 的 traces 是否在跑既然项目名字就叫 OpenTelemetry Demo验证可观测性数据链路比验证业务功能更重要。我常用的验证方法是配合grpcurl直接发起一个货币转换请求然后在后端的 Jaeger 或 Collector 面板里看有没有对应的 trace 产生grpcurl -plaintext -d {from_currency: CNY, to_currency: USD, amount: 100} localhost:8080 grpc.health.v1.Health/Check如果你在服务端日志里看到类似Span is exporting的输出说明导出器工作正常。这个环节最容易出的问题是 OTLP endpoint 配错导致 SDK 的批量导出线程一直在重试日志里会刷连接失败的错误。排查这类问题优先看OTEL_EXPORTER_OTLP_ENDPOINT是否可以被服务端访问而不是去看代码。6.3 一个容易被忽略的细节多种导出协议OpenTelemetry C SDK 默认编译时是支持 OTLP/gRPC 和 OTLP/HTTP 两种导出协议的但 Demo 的 CMake 构建默认只启用了其中一种。如果你在启动时把 endpoint 配成了 HTTP 的 4318 端口而二进制只编译了 gRPC 的 4317 导出器就会看到数据始终上不去。建议在 CMake 配置阶段就把WITH_OTLP_HTTP也打开反正多编一个组件的时间并不长。我就在这个问题上栽过跟头以为代码的问题折腾半天最后发现是导出协议不对。7. 我的一些实际操作体会多说几句编译期间的实际感受。第一次编译这个项目时我在没有配置 vcpkg 二进制缓存的情况下完整编译花了接近四十分钟期间 gRPC 和 Abseil 的源码编译日志刷了上万行。如果你在团队里需要帮多个人搭建同样的环境一定要把缓存目录共享出来否则每个人都从零编译一遍时间成本太浪费。另一个我觉得值得养成习惯的地方是编译完不要急着删构建目录。currencyservice的构建目录里有完整的compile_commands.json这对 IDE 的代码跳转和智能提示帮助极大。我一般会把它软链到项目根目录VSCode 的 clangd 插件能自动识别比手动配 includePath 省事得多。最后想说的是这个项目的编译流程虽然曲折但编译一遍下来你基本就能摸清 gRPC、Protobuf、Abseil、Boost 和 OpenTelemetry C SDK 之间的大致关系了。比起直接拉一个镜像跑起来自己动手编一次后面排查线上问题时的底气完全不一样。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。