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(推荐)

# 安装 vcpkg
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh # Linux/macOS
# 或
.\bootstrap-vcpkg.bat # Windows
# 安装 OpenTelemetry C++
./vcpkg install opentelemetry-cpp[otlp]

使用 Conan

# 安装 Conan
pip 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.git
cd opentelemetry-cpp
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_OTLP=ON
make -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-service
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production
# OTLP Exporter(内部平台)
export OTEL_EXPORTER_OTLP_ENDPOINT=http://uptrace-grpc.yodao.cn:80
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project?grpc=4317
# Traces 配置
export OTEL_TRACES_EXPORTER=otlp
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
# Metrics 配置
export OTEL_METRICS_EXPORTER=otlp

4. 编译和运行

# 使用 CMake
mkdir build && cd build
cmake ..
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)
# 查找 OpenTelemetry
find_package(opentelemetry-cpp REQUIRED)
# 添加可执行文件
add_executable(my-app main.cpp)
# 链接库
target_link_libraries(my-app
opentelemetry::trace
opentelemetry::exporter_otlp_http
opentelemetry::sdk::trace
opentelemetry::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 Exporter
otlp::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 Processor
auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
new trace_sdk::SimpleSpanProcessor(std::move(exporter))
);
// 创建 Resource
auto resource = opentelemetry::sdk::resource::Resource::Create({
{"service.name", service_name},
{"service.version", "1.0.0"}
});
// 创建 TracerProvider
auto provider = opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(
new trace_sdk::TracerProvider(std::move(processor), resource)
);
// 注册全局 TracerProvider
trace_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 headers
std::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 headers
std::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; // persistent
amqp_bytes_t message_bytes = amqp_cstring_bytes(message.c_str());
amqp_basic_publish(
conn,
1, // channel
amqp_cstring_bytes(exchange.c_str()),
amqp_cstring_bytes(routing_key.c_str()),
0, // mandatory
0, // 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 Exporter
otlp::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 Exporter
auto console_exporter = std::unique_ptr<trace_sdk::SpanExporter>(
new opentelemetry::exporter::trace::OStreamSpanExporter(std::cout)
);
// 创建多个 processor
auto 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))
);
// 创建 TracerProvider
auto 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 传递(在多线程环境中)

// ✅ 正确:在多线程中传递 context
void 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-service
export OTEL_SERVICE_VERSION=1.0.0
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
# OTLP Exporter(内部平台)
export OTEL_EXPORTER_OTLP_ENDPOINT=http://uptrace-grpc.yodao.cn:80
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project?grpc=4317
# Traces 配置
export OTEL_TRACES_EXPORTER=otlp
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
# Metrics 配置
export OTEL_METRICS_EXPORTER=otlp
export OTEL_METRIC_EXPORT_INTERVAL=60000
# Logs 配置
export OTEL_LOGS_EXPORTER=otlp
export OTEL_LOG_LEVEL=info

编译选项

CMake 编译选项

# 启用 OTLP Exporter
set(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. 数据未发送到后端

排查步骤:

  1. 检查环境变量:
echo $OTEL_SERVICE_NAME
echo $OTEL_EXPORTER_OTLP_ENDPOINT
  1. 检查链接库:
ldd ./my-app | grep opentelemetry
# 或 macOS
otool -L ./my-app | grep opentelemetry
  1. 启用调试日志:
export OTEL_LOG_LEVEL=debug
  1. 检查网络连接:
curl -v http://uptrace-grpc.yodao.cn:80/v1/traces

2. 编译错误

确保正确链接 OpenTelemetry 库:

target_link_libraries(my-app
opentelemetry::trace
opentelemetry::exporter_otlp_http
opentelemetry::sdk::trace
opentelemetry::sdk::resource
)

常见编译错误:

  • 找不到头文件: 检查 CMAKE_PREFIX_PATH 或 vcpkg 路径
  • 链接错误: 确保所有依赖库都已安装
  • C++ 标准: 确保使用 C++14 或更高版本

3. 多线程问题

确保在多线程环境中正确使用 context:

// 使用线程局部存储或传递 context
thread_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

性能优化建议

  1. 使用批量导出: BatchSpanProcessor 比 SimpleSpanProcessor 性能更好
  2. 调整批量大小: 根据数据量调整 max_export_batch_size
  3. 使用异步导出: 避免阻塞主线程
  4. 采样: 在生产环境使用较低的采样率(如 10%)
  5. 启用压缩: 如果网络带宽有限,启用 gzip 压缩
  6. 使用 protobuf: 如果可用,使用 protobuf 而不是 JSON

下一步

参考资源

在Gitlab上编辑本文档