跳转至

OpenTelemetry C++ SDK

本文采用 SDK Instrumentation:在应用代码中初始化 SDK、创建 Span 并配置 Exporter,通过 DataKit 将链路发送到 观测云。这不是零代码注入,安装依赖或设置环境变量不会自动采集所有框架调用。本文仅启用 Trace,不涉及 Kubernetes。

C++ + OpenTelemetry SDK -> OTLP/HTTP -> DataKit -> 观测云

前置条件

  • 以下构建命令适用于 Debian/Ubuntu,需具备安装开发依赖的权限。
  • 使用支持 C++17 的编译器、CMake 3.16 或更高版本、Git、libcurl、Protobuf 开发库及 protoc、nlohmann-json。
  • 示例固定使用 OpenTelemetry C++ v1.23.0。升级时应一起核对 SDK、编译器、依赖库与 ABI 兼容性。
  • 已安装 DataKit,并使用目标工作空间的安装命令配置好上报地址和 Token;应用可访问 DataKit HTTP 端口 9529

一、开启 OpenTelemetry 采集器

在 DataKit 主机上进入配置目录。仅在配置不存在时复制示例,已有文件请直接调整:

cd /usr/local/datakit/conf.d/opentelemetry
sudo cp -n opentelemetry.conf.sample opentelemetry.conf

确认 opentelemetry.conf 中包含以下配置。自定义标签通过 customer_tags 保留:

[[inputs.opentelemetry]]
  customer_tags = ["team", "app.operation"]

  [inputs.opentelemetry.http]
    http_status_ok = 200
    trace_api = "/otel/v1/traces"
    metric_api = "/otel/v1/metrics"
    logs_api = "/otel/v1/logs"

本机接入使用 127.0.0.1:9529。跨主机接入时,在 DataKit 主配置 datakit.conf[http_api].listen 中设置应用可访问的监听地址,并限制网络访问范围。HTTP 监听地址不在采集器文件中设置。

重启并检查 DataKit:

sudo datakit service restart
curl http://127.0.0.1:9529/v1/ping

/v1/ping 只验证 HTTP 服务可达,不代表链路已入库。完整说明参见 OpenTelemetry 采集器。DataKit 负责工作空间认证,示例应用不直接配置工作空间 Token。

二、应用接入 OpenTelemetry

安装依赖

在开发机上安装依赖并获取官方源码。以下目录名应尚未存在:

sudo apt-get update
sudo apt-get install -y build-essential cmake git libcurl4-openssl-dev \
  libprotobuf-dev protobuf-compiler nlohmann-json3-dev
mkdir otel-cpp-demo
cd otel-cpp-demo
git clone --branch v1.23.0 --depth 1 --recurse-submodules --shallow-submodules \
  https://github.com/open-telemetry/opentelemetry-cpp.git

otel-cpp-demo 根目录创建 CMakeLists.txtWITH_OTLP_HTTP 开启 HTTP Exporter,关闭测试和示例可减少构建时间:

cmake_minimum_required(VERSION 3.16)
project(otel_cpp_demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(WITH_BENCHMARK OFF CACHE BOOL "" FORCE)
set(WITH_EXAMPLES OFF CACHE BOOL "" FORCE)
set(WITH_OTLP_GRPC OFF CACHE BOOL "" FORCE)
set(WITH_OTLP_HTTP ON CACHE BOOL "" FORCE)
add_subdirectory(opentelemetry-cpp)

add_executable(otel-cpp-demo main.cpp)
target_link_libraries(otel-cpp-demo PRIVATE
    opentelemetry_trace
    opentelemetry_exporter_otlp_http
)

初始化 SDK 并创建 Span

在同一目录创建 main.cpp。通过 Resource 检测器读取环境变量,注册 Provider,并显式结束 Span、刷新和关闭 Provider:

#include <chrono>
#include <memory>
#include <utility>

#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/trace/batch_span_processor_factory.h"
#include "opentelemetry/sdk/trace/batch_span_processor_options.h"
#include "opentelemetry/sdk/trace/provider.h"
#include "opentelemetry/sdk/trace/samplers/parent.h"
#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"

namespace otlp = opentelemetry::exporter::otlp;
namespace sdktrace = opentelemetry::sdk::trace;

int main()
{
    otlp::OtlpHttpExporterOptions options;
    options.content_type = otlp::HttpRequestContentType::kBinary;
    auto exporter = otlp::OtlpHttpExporterFactory::Create(options);
    sdktrace::BatchSpanProcessorOptions batch_options;
    auto processor = sdktrace::BatchSpanProcessorFactory::Create(
        std::move(exporter), batch_options);
    auto resource = opentelemetry::sdk::resource::Resource::Create({});
    auto sampler = std::make_unique<sdktrace::ParentBasedSampler>(
        std::make_shared<sdktrace::TraceIdRatioBasedSampler>(1.0));

    auto provider = std::make_shared<sdktrace::TracerProvider>(
        std::move(processor), resource, std::move(sampler));
    std::shared_ptr<opentelemetry::trace::TracerProvider> api_provider = provider;
    sdktrace::Provider::SetTracerProvider(api_provider);
    auto tracer = provider->GetTracer("otel-cpp-demo");

    auto parent = tracer->StartSpan("checkout");
    {
        auto scope = tracer->WithActiveSpan(parent);
        auto child = tracer->StartSpan("db.lookup");
        child->SetAttribute("app.operation", "lookup");
        child->End();
    }
    parent->End();

    const bool flushed = provider->ForceFlush(std::chrono::seconds(10));
    const bool stopped = provider->Shutdown(std::chrono::seconds(10));
    return flushed && stopped ? 0 : 1;
}

构建与运行

在包含 CMakeLists.txt 的项目根目录构建:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target otel-cpp-demo --parallel 2

在启动应用的同一终端配置以下参数。跨主机时将 127.0.0.1 替换为实际 DataKit 地址:

export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=prod,service.version=1.0.0,team=backend"
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://127.0.0.1:9529/otel/v1/traces"

./build/otel-cpp-demo

长期服务只初始化一次 Provider;关闭时先停止请求处理并结束 Span,再执行 ForceFlush()Shutdown()。不要在每次请求结束后关闭 Provider。

三、数据上报参数

参数或配置 说明
OTEL_SERVICE_NAME service.name;示例使用 order-service,应设置为稳定的服务名。
OTEL_RESOURCE_ATTRIBUTES 逗号分隔的资源属性。示例设置环境、版本和 team;自定义字段应加入 DataKit customer_tags
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Trace 专用完整地址:http://127.0.0.1:9529/otel/v1/traces。优先于通用基础地址。
OTEL_EXPORTER_OTLP_ENDPOINT 可选基础地址:http://127.0.0.1:9529/otel。未设置 Trace 专用地址时,由 Exporter 追加 /v1/traces
OTEL_EXPORTER_OTLP_HEADERS 可选 OTLP 请求头,格式为 key=value,key2=value2。只有接收端或代理要求认证时才配置。

本例构建并创建 OtlpHttpExporter,且设置 content_type = kBinary;更改 OTEL_EXPORTER_OTLP_PROTOCOL 不会把它变成 gRPC Exporter。gRPC 需要另外启用构建选项、链接对应 Exporter 并修改初始化代码。

采样在代码中配置为 ParentBased + 根链路比例 1.0,遵循父 Span 的采样决定。生产环境可把示例中的比例改为 0.1,约采样 10% 的根链路。本例显式设置采样器,不依赖 OTEL_TRACES_SAMPLEROTEL_TRACES_SAMPLER_ARG

示例显式创建 Trace 导出管道,OTEL_TRACES_EXPORTER 不负责选择或关闭它。未创建 Metric、Log Provider,因此不会因设置 OTEL_METRICS_EXPORTEROTEL_LOGS_EXPORTER 就启用对应信号。日志可另行使用 DataKit 日志文件采集

上下文传播与业务接入

WithActiveSpan 的 Scope 只影响当前上下文,不能自动把父级带到其他线程或服务。线程切换需显式传递和恢复 Context;HTTP/RPC 需要结合 HttpTraceContextTextMapCarrier 提取和注入 traceparenttracestate,并为服务端入口设置远程父级。实际业务还需在错误路径设置 Span 状态并保证所有 Span 结束。

验证与排查

  1. 执行示例后,在 观测云 应用性能监测中按 order-service 查询链路,确认有 checkout 与其子 Span db.lookup
  2. 无数据时,检查采集器是否启用、应用环境变量是否生效、HTTP 路径是否包含 /otel/v1/traces,以及 DataKit 与应用的导出错误。
  3. 确认 Span 已结束且 Provider 在退出前完成刷新。强制退出或使用根链路比例 0 会导致无法看到预期数据;网络连通不等于导出成功。

参考文档

文档评价

文档内容是否对您有帮助?