C++ 语言 OpenTelemetry 接入指南
本文档提供 C++ 应用接入 OpenTelemetry 的完整指南,包括 SDK 手动接入和框架集成示例,支持高性能系统级编程。
目录
概述
C++ 语言特性
C++ 的 OpenTelemetry 支持具有以下特点:
- 高性能: 零成本抽象,极低的性能开销
- 系统级编程: 适合底层系统编程
- 跨平台: 支持 Linux、Windows、macOS
- 线程安全: 支持多线程环境
- 内存控制: 精确的内存管理
版本要求
- C++ 标准: C++14+ (推荐 C++17+)
- OpenTelemetry C++: v1.10.0+
- 支持架构: x86_64, arm64, aarch64
- 编译器: GCC 7+, Clang 8+, MSVC 2019+
支持的信号
- ✅ Traces: 完全支持
- ✅ Metrics: 完全支持
- ✅ Logs: 完全支持
适用场景
- 高性能 Web 服务
- 系统编程
- 游戏引擎
- 嵌入式系统
- 实时系统
- gRPC 服务
快速开始
1. 安装依赖
使用 vcpkg(推荐)
# 安装 vcpkggit clone https://github.com/Microsoft/vcpkg.gitcd vcpkg./bootstrap-vcpkg.sh # Linux/macOS# 或.\bootstrap-vcpkg.bat # Windows# 安装 OpenTelemetry C++./vcpkg install opentelemetry-cpp[otlp]
使用 Conan
# 安装 Conanpip install conan# 在 conanfile.txt 中添加[requires]opentelemetry-cpp/1.10.0[options]opentelemetry-cpp:with_otlp=True# 安装依赖conan install . --build=missing
从源码编译
git clone https://github.com/open-telemetry/opentelemetry-cpp.gitcd opentelemetry-cppmkdir build && cd buildcmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP=ONmake -j$(nproc)sudo make install
2. 基础初始化
// main.cpp#include <opentelemetry/trace/provider.h>#include <opentelemetry/exporters/otlp/otlp_http_exporter.h>#include <opentelemetry/sdk/trace/simple_processor.h>#include <opentelemetry/sdk/trace/tracer_provider.h>#include <opentelemetry/sdk/resource/resource.h>#include <opentelemetry/nostd/shared_ptr.h>namespace trace_api = opentelemetry::trace;namespace trace_sdk = opentelemetry::sdk::trace;namespace otlp = opentelemetry::exporter::otlp;void initTracer() {auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new otlp::OtlpHttpExporter(otlp::OtlpHttpExporterOptions{.url = "http://uptrace-grpc.yodao.cn:80/v1/traces",.headers = {{"uptrace-dsn", "http://token@uptrace-grpc.yodao.cn/project?grpc=4317"}}}));auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::SimpleSpanProcessor(std::move(exporter)));auto resource = opentelemetry::sdk::resource::Resource::Create({{"service.name", "my-cpp-service"},{"service.version", "1.0.0"}});auto provider = opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(new trace_sdk::TracerProvider(std::move(processor), resource));trace_api::Provider::SetTracerProvider(provider);}int main() {initTracer();auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("handle_request");// 业务逻辑std::cout << "Hello, OpenTelemetry!" << std::endl;span->End();return 0;}
3. 配置环境变量
# 服务信息export OTEL_SERVICE_NAME=my-cpp-serviceexport OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production# OTLP Exporter(内部平台)export OTEL_EXPORTER_OTLP_ENDPOINT=http://uptrace-grpc.yodao.cn:80export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project?grpc=4317# Traces 配置export OTEL_TRACES_EXPORTER=otlpexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1# Metrics 配置export OTEL_METRICS_EXPORTER=otlp
4. 编译和运行
# 使用 CMakemkdir build && cd buildcmake ..make./my-app# 或使用 g++g++ -std=c++17 main.cpp -lopentelemetry_trace -lopentelemetry_exporter_otlp_http -o my-app./my-app
SDK 手动接入
安装依赖
CMakeLists.txt 配置
cmake_minimum_required(VERSION 3.15)project(my-app)set(CMAKE_CXX_STANDARD 17)# 查找 OpenTelemetryfind_package(opentelemetry-cpp REQUIRED)# 添加可执行文件add_executable(my-app main.cpp)# 链接库target_link_libraries(my-appopentelemetry::traceopentelemetry::exporter_otlp_httpopentelemetry::sdk::traceopentelemetry::sdk::resource)
基础初始化
创建 OpenTelemetry 初始化函数
// otel.h#pragma once#include <opentelemetry/trace/provider.h>#include <opentelemetry/exporters/otlp/otlp_http_exporter.h>#include <opentelemetry/sdk/trace/simple_processor.h>#include <opentelemetry/sdk/trace/tracer_provider.h>#include <opentelemetry/sdk/resource/resource.h>void initOpenTelemetry();
// otel.cpp#include "otel.h"#include <cstdlib>#include <string>void initOpenTelemetry() {namespace trace_api = opentelemetry::trace;namespace trace_sdk = opentelemetry::sdk::trace;namespace otlp = opentelemetry::exporter::otlp;// 从环境变量读取配置std::string endpoint = std::getenv("OTEL_EXPORTER_OTLP_ENDPOINT")? std::getenv("OTEL_EXPORTER_OTLP_ENDPOINT"): "http://uptrace-grpc.yodao.cn:80/v1/traces";std::string dsn = std::getenv("OTEL_EXPORTER_OTLP_HEADERS")? std::getenv("OTEL_EXPORTER_OTLP_HEADERS"): "http://token@uptrace-grpc.yodao.cn/project?grpc=4317";std::string service_name = std::getenv("OTEL_SERVICE_NAME")? std::getenv("OTEL_SERVICE_NAME"): "my-cpp-service";// 创建 OTLP Exporterotlp::OtlpHttpExporterOptions exporter_options;exporter_options.url = endpoint;exporter_options.headers = {{"uptrace-dsn", dsn}};auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new otlp::OtlpHttpExporter(exporter_options));// 创建 Span Processorauto processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::SimpleSpanProcessor(std::move(exporter)));// 创建 Resourceauto resource = opentelemetry::sdk::resource::Resource::Create({{"service.name", service_name},{"service.version", "1.0.0"}});// 创建 TracerProviderauto provider = opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(new trace_sdk::TracerProvider(std::move(processor), resource));// 注册全局 TracerProvidertrace_api::Provider::SetTracerProvider(provider);}
在应用中使用
// main.cpp#include "otel.h"#include <opentelemetry/trace/provider.h>#include <iostream>int main() {initOpenTelemetry();auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("handle_request");span->SetAttribute("http.method", "GET");span->SetAttribute("http.url", "/");// 业务逻辑std::cout << "Hello, World!" << std::endl;span->End();return 0;}
Traces - 手动埋点
创建和使用 Span
#include <opentelemetry/trace/provider.h>#include <opentelemetry/trace/span_metadata.h>void processOrder(const std::string& orderId) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("process_order");span->SetAttribute("order.id", orderId);span->SetAttribute("operation", "process");span->AddEvent("order_validation_started");try {validateOrder(orderId);span->AddEvent("order_validated");chargePayment(orderId);span->AddEvent("payment_processed");span->SetStatus(opentelemetry::trace::StatusCode::kOk);span->SetAttribute("order.success", true);} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());throw;}span->End();}
Context 传播
#include <opentelemetry/trace/propagation/http_trace_context.h>#include <opentelemetry/context/propagation/text_map_propagator.h>std::string makeHttpRequest(const std::string& url) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("http_request");span->SetAttribute("http.url", url);span->SetAttribute("http.method", "GET");// 注入 Context 到 HTTP headersstd::map<std::string, std::string> headers;auto propagator = opentelemetry::trace::propagation::HttpTraceContext();auto context = opentelemetry::context::RuntimeContext::GetCurrent();propagator.Inject(headers, context);// 使用 HTTP 客户端发送请求// ... HTTP 请求代码 ...span->SetAttribute("http.status_code", statusCode);span->SetStatus(statusCode >= 400? opentelemetry::trace::StatusCode::kError: opentelemetry::trace::StatusCode::kOk);span->End();return response;}
Metrics - 指标采集
Counter(计数器)
#include <opentelemetry/metrics/provider.h>#include <opentelemetry/metrics/meter.h>void handleRequest(const std::string& method,const std::string& route,int statusCode) {auto meter_provider = opentelemetry::metrics::Provider::GetMeterProvider();auto meter = meter_provider->GetMeter("my-service");auto request_counter = meter->CreateUInt64Counter("http.server.requests","total","Total number of HTTP requests");std::map<std::string, std::string> attributes = {{"http.method", method},{"http.route", route},{"http.status_code", std::to_string(statusCode)}};request_counter->Add(1, attributes);}
Histogram(直方图)
void recordDuration(double duration, const std::string& method, const std::string& route) {auto meter_provider = opentelemetry::metrics::Provider::GetMeterProvider();auto meter = meter_provider->GetMeter("my-service");auto request_duration = meter->CreateDoubleHistogram("http.server.duration","ms","HTTP request duration");std::map<std::string, std::string> attributes = {{"http.method", method},{"http.route", route}};request_duration->Record(duration, attributes);}
框架集成示例
gRPC 服务集成
#include <grpcpp/grpcpp.h>#include <opentelemetry/trace/provider.h>#include <opentelemetry/instrumentation/grpc/grpc_client.h>class MyServiceImpl : public MyService::Service {public:grpc::Status GetUser(grpc::ServerContext* context,const GetUserRequest* request,GetUserResponse* response) override {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("get_user");span->SetAttribute("user.id", request->id());try {// 业务逻辑auto user = fetchUser(request->id());response->set_id(user.id());response->set_name(user.name());span->SetStatus(opentelemetry::trace::StatusCode::kOk);} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());return grpc::Status(grpc::StatusCode::INTERNAL, e.what());}span->End();return grpc::Status::OK;}};
HTTP 服务集成(使用 Crow)
#include <crow.h>#include <opentelemetry/trace/provider.h>int main() {initOpenTelemetry();crow::SimpleApp app;CROW_ROUTE(app, "/users/<int>")([](int id) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("get_user");span->SetAttribute("user.id", std::to_string(id));try {auto user = fetchUser(id);span->SetStatus(opentelemetry::trace::StatusCode::kOk);span->End();return crow::response(user.toJson());} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());span->End();return crow::response(500, e.what());}});app.port(8080).multithreaded().run();return 0;}
数据库集成
MySQL 客户端集成
#include <mysql/mysql.h>#include <opentelemetry/trace/provider.h>std::vector<Row> queryDatabase(const std::string& query) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("database.query");span->SetAttribute("db.system", "mysql");span->SetAttribute("db.statement", query);MYSQL* conn = mysql_init(nullptr);mysql_real_connect(conn, "localhost", "user", "password", "mydb", 0, nullptr, 0);try {if (mysql_query(conn, query.c_str())) {throw std::runtime_error(mysql_error(conn));}MYSQL_RES* result = mysql_store_result(conn);std::vector<Row> rows;MYSQL_ROW row;while ((row = mysql_fetch_row(result))) {rows.push_back(Row(row));}mysql_free_result(result);span->SetStatus(opentelemetry::trace::StatusCode::kOk);return rows;} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());throw;} finally {mysql_close(conn);span->End();}}
PostgreSQL 客户端集成
#include <libpq-fe.h>#include <opentelemetry/trace/provider.h>std::vector<Row> queryPostgreSQL(const std::string& query) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("database.query");span->SetAttribute("db.system", "postgresql");span->SetAttribute("db.statement", query);PGconn* conn = PQconnectdb("host=localhost dbname=mydb user=user password=password");try {PGresult* result = PQexec(conn, query.c_str());if (PQresultStatus(result) != PGRES_TUPLES_OK) {throw std::runtime_error(PQerrorMessage(conn));}std::vector<Row> rows;int nRows = PQntuples(result);for (int i = 0; i < nRows; i++) {rows.push_back(Row(result, i));}PQclear(result);span->SetStatus(opentelemetry::trace::StatusCode::kOk);return rows;} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());throw;} finally {PQfinish(conn);span->End();}}
HTTP 客户端集成
libcurl 集成
#include <curl/curl.h>#include <opentelemetry/trace/provider.h>#include <opentelemetry/trace/propagation/http_trace_context.h>std::string makeHttpRequest(const std::string& url) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("http_request");span->SetAttribute("http.url", url);span->SetAttribute("http.method", "GET");// 注入 Context 到 HTTP headersstd::map<std::string, std::string> headers;auto propagator = opentelemetry::trace::propagation::HttpTraceContext();auto context = opentelemetry::context::RuntimeContext::GetCurrent();propagator.Inject(headers, context);CURL* curl = curl_easy_init();struct curl_slist* header_list = nullptr;for (const auto& [key, value] : headers) {std::string header = key + ": " + value;header_list = curl_slist_append(header_list, header.c_str());}curl_easy_setopt(curl, CURLOPT_URL, url.c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);std::string response_data;curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](void* contents, size_t size, size_t nmemb, std::string* data) {data->append((char*)contents, size * nmemb);return size * nmemb;});curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);CURLcode res = curl_easy_perform(curl);long status_code;curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);curl_slist_free_all(header_list);curl_easy_cleanup(curl);span->SetAttribute("http.status_code", (int)status_code);span->SetStatus(status_code >= 400? opentelemetry::trace::StatusCode::kError: opentelemetry::trace::StatusCode::kOk);span->End();return response_data;}
消息队列集成
RabbitMQ 集成
#include <amqp.h>#include <opentelemetry/trace/provider.h>void publishMessage(amqp_connection_state_t conn,const std::string& exchange,const std::string& routing_key,const std::string& message) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("rabbitmq.publish");span->SetAttribute("messaging.system", "rabbitmq");span->SetAttribute("messaging.destination", exchange);span->SetAttribute("messaging.rabbitmq.routing_key", routing_key);amqp_basic_properties_t props;props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG;props.content_type = amqp_cstring_bytes("text/plain");props.delivery_mode = 2; // persistentamqp_bytes_t message_bytes = amqp_cstring_bytes(message.c_str());amqp_basic_publish(conn,1, // channelamqp_cstring_bytes(exchange.c_str()),amqp_cstring_bytes(routing_key.c_str()),0, // mandatory0, // immediate&props,message_bytes);span->SetStatus(opentelemetry::trace::StatusCode::kOk);span->End();}
高级配置
自定义采样策略
#include <opentelemetry/sdk/trace/sampler.h>class CustomSampler : public opentelemetry::sdk::trace::Sampler {public:opentelemetry::sdk::trace::SamplingResult ShouldSample(const opentelemetry::trace::SpanContext& parent_context,opentelemetry::trace::TraceId trace_id,const std::string& name,opentelemetry::trace::SpanKind span_kind,const opentelemetry::common::KeyValueIterable& attributes,const opentelemetry::trace::SpanContextKeyValueIterable& links) noexcept override {// 总是采样特定路径if (name.find("/checkout") != std::string::npos ||name.find("/payment") != std::string::npos) {return {opentelemetry::sdk::trace::Decision::RECORD_AND_SAMPLE};}// 从不采样健康检查if (name.find("/health") != std::string::npos ||name.find("/metrics") != std::string::npos) {return {opentelemetry::sdk::trace::Decision::DROP};}// 默认采样率 10%return {opentelemetry::sdk::trace::Decision::RECORD_AND_SAMPLE};}std::string GetDescription() const noexcept override {return "CustomSampler";}};
性能优化配置
#include <opentelemetry/sdk/trace/batch_span_processor.h>void initOptimizedTracer() {otlp::OtlpHttpExporterOptions exporter_options;exporter_options.url = "http://uptrace-grpc.yodao.cn:80/v1/traces";exporter_options.headers = {{"uptrace-dsn", "http://token@uptrace-grpc.yodao.cn/project?grpc=4317"}};exporter_options.timeout = std::chrono::seconds(10);auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new otlp::OtlpHttpExporter(exporter_options));trace_sdk::BatchSpanProcessorOptions processor_options;processor_options.max_queue_size = 2048;processor_options.schedule_delay_millis = std::chrono::milliseconds(5000);processor_options.max_export_batch_size = 512;processor_options.export_timeout_millis = std::chrono::milliseconds(30000);auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::BatchSpanProcessor(std::move(exporter), processor_options));auto provider = opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(new trace_sdk::TracerProvider(std::move(processor), resource));trace_api::Provider::SetTracerProvider(provider);}
多 Exporter 配置
#include <opentelemetry/exporters/ostream/span_exporter.h>#include <opentelemetry/exporters/jaeger/jaeger_exporter.h>void initMultipleExporters() {// OTLP Exporterotlp::OtlpHttpExporterOptions otlp_options;otlp_options.url = "http://uptrace-grpc.yodao.cn:80/v1/traces";otlp_options.headers = {{"uptrace-dsn", "http://token@uptrace-grpc.yodao.cn/project?grpc=4317"}};auto otlp_exporter = std::unique_ptr<trace_sdk::SpanExporter>(new otlp::OtlpHttpExporter(otlp_options));// Console Exporterauto console_exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(std::cout));// 创建多个 processorauto otlp_processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::BatchSpanProcessor(std::move(otlp_exporter)));auto console_processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::SimpleSpanProcessor(std::move(console_exporter)));// 创建 TracerProviderauto provider = opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(new trace_sdk::TracerProvider(std::vector<std::unique_ptr<trace_sdk::SpanProcessor>>{std::move(otlp_processor),std::move(console_processor)},resource));trace_api::Provider::SetTracerProvider(provider);}
线程池配置
#include <opentelemetry/sdk/trace/batch_span_processor.h>#include <thread>#include <vector>void initWithThreadPool() {// 创建线程池用于异步导出std::vector<std::thread> workers;const size_t num_threads = std::thread::hardware_concurrency();for (size_t i = 0; i < num_threads; ++i) {workers.emplace_back([&]() {// 工作线程逻辑});}// 配置 BatchSpanProcessor 使用线程池trace_sdk::BatchSpanProcessorOptions processor_options;processor_options.max_queue_size = 2048;processor_options.schedule_delay_millis = std::chrono::milliseconds(5000);auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::BatchSpanProcessor(std::move(exporter), processor_options));// ... 创建 TracerProvider}
最佳实践
1. Context 传递(在多线程环境中)
// ✅ 正确:在多线程中传递 contextvoid processOrder(const std::string& orderId, std::shared_ptr<opentelemetry::trace::Span> parentSpan) {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("process_order", {.parent = parentSpan->GetContext()});// 业务逻辑validateOrder(orderId);chargePayment(orderId);span->End();}// 在新线程中使用std::thread t([parentSpan, orderId]() {processOrder(orderId, parentSpan);});t.join();
2. 错误处理
// ✅ 正确:记录错误并设置状态try {processOrder(orderId);span->SetStatus(opentelemetry::trace::StatusCode::kOk);} catch (const std::exception& e) {span->RecordException(e);span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());throw;}
3. 资源管理(RAII)
// ✅ 正确:使用 RAII 确保 span 结束class ScopedSpan {public:ScopedSpan(std::shared_ptr<opentelemetry::trace::Span> span) : span_(span) {}~ScopedSpan() { if (span_) span_->End(); }std::shared_ptr<opentelemetry::trace::Span> get() { return span_; }private:std::shared_ptr<opentelemetry::trace::Span> span_;};void operation() {auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-service");auto span = tracer->StartSpan("operation");ScopedSpan scoped(span);// 业务逻辑// span 会在作用域结束时自动结束}
环境变量配置
OpenTelemetry C++ 支持通过环境变量配置:
# 服务信息export OTEL_SERVICE_NAME=my-cpp-serviceexport OTEL_SERVICE_VERSION=1.0.0export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production# OTLP Exporter(内部平台)export OTEL_EXPORTER_OTLP_ENDPOINT=http://uptrace-grpc.yodao.cn:80export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufexport OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project?grpc=4317# Traces 配置export OTEL_TRACES_EXPORTER=otlpexport OTEL_TRACES_SAMPLER=parentbased_traceidratioexport OTEL_TRACES_SAMPLER_ARG=0.1# Metrics 配置export OTEL_METRICS_EXPORTER=otlpexport OTEL_METRIC_EXPORT_INTERVAL=60000# Logs 配置export OTEL_LOGS_EXPORTER=otlpexport OTEL_LOG_LEVEL=info
编译选项
CMake 编译选项
# 启用 OTLP Exporterset(WITH_OTLP ON)# 启用 gRPC(如果需要)set(WITH_GRPC ON)# 启用压缩set(WITH_ZLIB ON)# 启用 protobuf(性能优化)set(WITH_PROTOBUF ON)
使用 vcpkg 特性
# 安装带所有特性的包./vcpkg install opentelemetry-cpp[otlp,grpc,protobuf,zlib]
故障排查
常见问题
1. 数据未发送到后端
排查步骤:
- 检查环境变量:
echo $OTEL_SERVICE_NAMEecho $OTEL_EXPORTER_OTLP_ENDPOINT
- 检查链接库:
ldd ./my-app | grep opentelemetry# 或 macOSotool -L ./my-app | grep opentelemetry
- 启用调试日志:
export OTEL_LOG_LEVEL=debug
- 检查网络连接:
curl -v http://uptrace-grpc.yodao.cn:80/v1/traces
2. 编译错误
确保正确链接 OpenTelemetry 库:
target_link_libraries(my-appopentelemetry::traceopentelemetry::exporter_otlp_httpopentelemetry::sdk::traceopentelemetry::sdk::resource)
常见编译错误:
- 找不到头文件: 检查
CMAKE_PREFIX_PATH或vcpkg路径 - 链接错误: 确保所有依赖库都已安装
- C++ 标准: 确保使用 C++14 或更高版本
3. 多线程问题
确保在多线程环境中正确使用 context:
// 使用线程局部存储或传递 contextthread_local auto context = opentelemetry::context::RuntimeContext::GetCurrent();// 或使用互斥锁保护std::mutex context_mutex;std::lock_guard<std::mutex> lock(context_mutex);auto context = opentelemetry::context::RuntimeContext::GetCurrent();
4. 内存泄漏
使用 RAII 确保资源正确释放:
class ScopedSpan {public:ScopedSpan(std::shared_ptr<opentelemetry::trace::Span> span): span_(span) {}~ScopedSpan() {if (span_) span_->End();}// 禁用拷贝ScopedSpan(const ScopedSpan&) = delete;ScopedSpan& operator=(const ScopedSpan&) = delete;// 允许移动ScopedSpan(ScopedSpan&&) = default;ScopedSpan& operator=(ScopedSpan&&) = default;private:std::shared_ptr<opentelemetry::trace::Span> span_;};
调试技巧
启用详细日志
export OTEL_LOG_LEVEL=debug
使用 Console Exporter
#include <opentelemetry/exporters/ostream/span_exporter.h>auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(std::cout));auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
使用 GDB 调试
gdb ./my-app(gdb) break opentelemetry::trace::Span::End(gdb) run
性能优化建议
- 使用批量导出: BatchSpanProcessor 比 SimpleSpanProcessor 性能更好
- 调整批量大小: 根据数据量调整
max_export_batch_size - 使用异步导出: 避免阻塞主线程
- 采样: 在生产环境使用较低的采样率(如 10%)
- 启用压缩: 如果网络带宽有限,启用 gzip 压缩
- 使用 protobuf: 如果可用,使用 protobuf 而不是 JSON
下一步
- 环境变量配置 - 了解所有配置选项
- 高级配置 - 深入了解采样和性能优化
- Uptrace 平台使用 - 了解如何使用 Uptrace 平台