本文介绍通过OpenTelemetry C++ SDK将C++应用的Trace数据接入到日志服务的操作步骤。
前提条件
已创建Trace实例。更多信息,请参见创建Trace实例。
已准备相关的开发环境,该环境需要支持编译及运行Opentelemetry C++ SDK。
如果您使用的是CMake编译器,则其版本需为3.1及以上。
如果您使用的是GCC或G++编译器,则其版本需要为4.8及以上。
如果您使用的是MSVC,则其版本需要为vs2015及以上(建议为vs2019)。
支持的C++版本如下所示。
ISO/IEC 14882:2011 (C++11, C++0x)
ISO/IEC 14882:2014 (C++14, C++1y)
ISO/IEC 14882:2017 (C++17, C++1z)
ISO/IEC 14882:2020 (C++20)
更多依赖及版本信息,请参见opentelemetry-cpp。
步骤一:SDK集成
您可以通过源码或包管理器集成SDK。更多信息,请参见Install opentelemetry-cpp。
使用源码集成(作为独立的CMake项目编译)
前提条件
支持在Windows、macOS、Linux平台进行编译。
已安装支持C++11及以上版本的C++编译器。
已安装Git。
已安装CMake。
已安装GoogleTest。
已安装Google Benchmark。
操作步骤
获取opentelemetry-cpp源代码。
$ cd <your-path> $ git clone --recurse-submodules https://github.com/open-telemetry/opentelemetry-cpp
创建CMake build配置。
$ cd opentelemetry-cpp $ mkdir build && cd build && cmake ..
构建CMake targets。
$ cmake --build . --target all
安装API的头文件。
$ cmake --install . --prefix /<install-root>/
使用源码集成(集成到目标CMake项目中)
前置条件
支持在Windows、macOS、Linux平台进行编译。
已安装支持C++11及以上版本的C++编译器。
已安装Git。
已安装CMake。
已安装GoogleTest。
已安装Google Benchmark。
代码示例
# CMakeLists.txt
find_package(opentelemetry-cpp CONFIG REQUIRED)
...
target_include_directories(foo PRIVATE ${OPENTELEMETRY_CPP_INCLUDE_DIRS})
target_link_libraries(foo PRIVATE ${OPENTELEMETRY_CPP_LIBRARIES})
使用包管理器集成
使用包管理器集成的方式请参见using package managers。
步骤二:初始化SDK
初始化Opentelemetry C++ SDK后,您才能正常使用SDK。请按照如下操作,初始化SDK。
// 导入以下头文件。
#include "opentelemetry/exporters/otlp/otlp_grpc_exporter_factory.h"
#include "opentelemetry/sdk/trace/simple_processor_factory.h"
#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
#include "opentelemetry/trace/provider.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
#include "opentelemetry/sdk/version/version.h"
#include "opentelemetry/trace/provider.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"
namespace trace = opentelemetry::trace;
namespace trace_sdk = opentelemetry::sdk::trace;
namespace otlp = opentelemetry::exporter::otlp;
namespace resource = opentelemetry::sdk::resource;
namespace
{
// 初始化Exporter。Exporter用于导出Trace到日志服务Logstore。
opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts;
void InitTracer()
{
opts.endpoint = "https://${endpoint}";
opts.use_ssl_credentials = true;
opts.ssl_credentials_cacert_path = "<your root pem file path>"; // 需手动指定root pem文件路径。一般包含在grpc依赖库路径中。
// Setup credentials info
opts.metadata.insert(std::pair<std::string, std::string>("x-sls-otel-project", "${project}"));
opts.metadata.insert(std::pair<std::string, std::string>("x-sls-otel-instance-id", "${instanceId}"));
opts.metadata.insert(std::pair<std::string, std::string>("x-sls-otel-ak-id", "${access-key-id}"));
opts.metadata.insert(std::pair<std::string, std::string>("x-sls-otel-ak-secret", "${access-key-secret}"));
// Create OTLP exporter instance
auto exporter = otlp::OtlpGrpcExporterFactory::Create(opts);
auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter));
// 初始化tracer provider。tracer provider用于暴露主要的API,对Span进行预处理。
// 自定义TraceId、SpanId生成规则,自定义采样器等。您可以根据实际需要进行配置。
resource::ResourceAttributes attributes = {
{resource::SemanticConventions::kServiceName, "${service}"}, //一般为子模块名称。
{resource::SemanticConventions::kServiceNamespace, "${service.namespace}"}, //一般为模块或App名称。
{resource::SemanticConventions::kServiceVersion, "${version}"}, //一般为模块或App版本号。
{resource::SemanticConventions::kHostName, "${host}"},
{resource::SemanticConventions::kDeploymentEnvironment, "${environment}"}
};
auto resource = opentelemetry::sdk::resource::Resource::Create(attributes);
std::shared_ptr<opentelemetry::trace::TracerProvider> provider =
trace_sdk::TracerProviderFactory::Create(std::move(processor), std::move(resource));
// Set the global trace provider
trace::Provider::SetTracerProvider(provider);
}
void CleanupTracer()
{
// We call ForceFlush to prevent to cancel running exportings, It's optional.
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> provider =
trace::Provider::GetTracerProvider();
if (provider)
{
static_cast<trace_sdk::TracerProvider*>(provider.get())->ForceFlush();
}
std::shared_ptr<opentelemetry::trace::TracerProvider> none;
trace::Provider::SetTracerProvider(none);
}
} // namespace
变量 | 说明 | 示例 |
| 日志服务Project的接入地址,格式为
| test-project.cn-hangzhou.log.aliyuncs.com:10010 |
| 日志服务Project名称。 | test-project |
| Trace服务实例ID。更多信息,请参见创建Trace实例。 | test-traces |
| 阿里云账号AccessKey ID。 | 无 |
| 阿里云账号AccessKey Secret。 重要 建议您使用只具备日志服务Project写入权限的RAM用户的AccessKey。 | 无 |
| 服务归属的命名空间。 | order |
| 服务名。根据您的实际场景配置。 | payment |
| 服务版本号。建议按照va.b.c格式定义。 | v0.1.2 |
| 主机名。 | localhost |
| 部署环境。例如测试环境、生产环境。根据您的实际场景配置。 | pre |
步骤三:使用SDK
创建Tracer
建议根据不同的业务场景创建Tracer。创建Tracer时需要传入library name,利于按照library区分不同的Trace数据。
namespace trace = opentelemetry::trace;
namespace nostd = opentelemetry::nostd;
namespace
{
nostd::shared_ptr<trace::Tracer> get_tracer()
{
auto provider = trace::Provider::GetTracerProvider();
return provider->GetTracer("<your library name>", OPENTELEMETRY_SDK_VERSION);
}
} // namespace
创建基本Span
Span代表事务中的操作,每个Span都封装了操作名称、起止时间戳、属性信息、事件信息和Context信息等。
auto span = get_tracer()->StartSpan("basic_f1");
// do your stuff
// ...
span->End();
创建嵌套Span
当您希望为嵌套操作关联Span时,OpenTelemetry支持在进程内和跨远程进程进行跟踪。例如针对methodA调用methodB ,您可以通过以下方式创建嵌套Span。
void method_a()
{
auto span = get_tracer()->StartSpan("operation A");
auto scope = get_tracer()->WithActiveSpan(span);
method_b();
span->End();
}
void method_b()
{
auto span_child = get_tracer()->StartSpan("operation B");
// do your stuff
// ...
span_child->End();
}
OpenTemetry API还提供了一种自动化的方式来传播parentSpan,示例如下:
void method_a()
{
auto scoped_span = trace::Scope(get_tracer()->StartSpan("operation A"));
method_b();
}
void method_b()
{
auto scoped_span = trace::Scope(get_tracer()->StartSpan("operation B"));
}
创建带属性的Span
您可以通过属性在Span上提供特定操作的上下文信息。例如执行结果、关联的其他业务信息等。
auto span = get_tracer()->StartSpan("<my operation>");
span->SetAttribute("age", 12);
span->SetAttribute("sex", "man");
// do your stuff
// ...
span->SetAttribute("height", 154.5);
span->End();
创建带事件的Span
您可以通过携带多个事件的方式对Span进行注释。
auto span = get_tracer()->StartSpan("<my operation>");
// do your stuff
// ...
span->AddEvent("message: success");
span->End();
给Span添加状态
Span包含trace::StatusCode::kUnset
、trace::StatusCode::kOk
、trace::StatusCode::kError
三个状态,分别表示默认状态、成功状态、操作包含错误。
auto span = get_tracer()->StartSpan("<my operation>");
// do your stuff
// ...
span->SetStatus(trace::StatusCode::kError);
span->End();
传播上下文信息
OpenTelemetry提供了一种基于文本的方法,传播上下文信息。
#include "opentelemetry/trace/context.h"
#include "opentelemetry/context/propagation/global_propagator.h"
#include "opentelemetry/context/propagation/text_map_propagator.h"
#include "opentelemetry/trace/propagation/http_trace_context.h"
#include "opentelemetry/nostd/shared_ptr.h"
// 以HttpTraceContext为例,演示context propagation的基本用法。
// 在SDK初始化时,完成global propagator配置。
opentelemetry::context::propagation::GlobalTextMapPropagator::SetGlobalPropagator(
opentelemetry::nostd::shared_ptr<opentelemetry::context::propagation::TextMapPropagator>(
new opentelemetry::trace::propagation::HttpTraceContext()));
// 在client侧发起网络请求时,注入Header信息。
opentelemetry::context::propagation::TextMapCarrier carrier; // 实际使用时,需要替换为目标TextMapCarrier。
auto propagator = opentelemetry::context::propagation::GlobalTextMapPropagator::GetGlobalPropagator();
// 注入Header信息。
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
propagator->Inject(carrier, current_ctx);
// 在server侧,提取Header信息。
auto current_ctx = opentelemetry::context::RuntimeContext::GetCurrent();
auto new_context = propagator->Extract(carrier, current_ctx);
auto remote_span = opentelemetry::trace::GetSpan(new_context);
目前,OpenTelemetry SDK支持按照W3C Trace Context标准传播上下文信息。更多信息,请参见W3CTraceContextPropagator类。
更多OpenTelemetry SDK使用信息,请参考官方文档。