OpenTelemetry C++ SDK¶
この記事では SDK によるインストルメンテーションを採用しています。アプリケーションコードで SDK を初期化し、Span を作成して Exporter を設定し、DataKit 経由でトレースを Guance に送信します。これはゼロコードインジェクションではありません。依存関係のインストールや環境変数の設定だけでは、すべてのフレームワーク呼び出しを自動的に収集することはできません。この記事では Trace のみを有効にします。Kubernetes には対応していません。
前提条件¶
- 以下のビルドコマンドは 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にアクセスできること。
1. OpenTelemetry コレクターを有効にする¶
DataKit ホストで設定ディレクトリに移動します。設定ファイルが存在しない場合のみサンプルをコピーし、既存のファイルがある場合は直接編集してください:
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 を再起動して確認します:
/v1/ping は HTTP サービスに到達できることだけを確認するもので、トレースが取り込まれたことを示すものではありません。詳細な説明は OpenTelemetry コレクター を参照してください。ワークスペースの認証は DataKit が担当するため、サンプルアプリケーションでワークスペースの Token を直接設定する必要はありません。
2. アプリケーションを 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.txt を作成します。WITH_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 をシャットダウンしないでください。
3. データ送信パラメータ¶
| パラメータまたは設定 | 説明 |
|---|---|
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_SAMPLER や OTEL_TRACES_SAMPLER_ARG には依存しません。
この例では Trace のエクスポートパイプラインを明示的に作成するため、OTEL_TRACES_EXPORTER がその選択やシャットダウンを行うわけではありません。Metric や Log の Provider は作成されないため、OTEL_METRICS_EXPORTER や OTEL_LOGS_EXPORTER を設定しても対応するシグナルは有効になりません。ログは別途 DataKit の ログファイル収集 を利用できます。
コンテキスト伝播と業務統合¶
WithActiveSpan の Scope は現在のコンテキストにのみ影響し、親コンテキストを他のスレッドやサービスに自動的に引き継ぐことはできません。スレッド切り替え時には Context を明示的に受け渡して復元する必要があります。HTTP/RPC では、HttpTraceContext と TextMapCarrier を組み合わせて traceparent と tracestate を抽出・注入し、サーバー側のエントリポイントにリモートの親を設定します。実際の業務コードでは、エラーパスで Span のステータスを設定し、すべての Span が確実に終了するようにする必要もあります。
確認とトラブルシューティング¶
- サンプルを実行した後、Guance のアプリケーションパフォーマンスモニタリング(APM)で
order-serviceを指定してトレースを検索し、checkoutとその子 Span であるdb.lookupが存在することを確認します。 - データがない場合は、コレクターが有効かどうか、アプリケーションの環境変数が反映されているか、HTTP パスに
/otel/v1/tracesが含まれているか、DataKit とアプリケーションのエクスポートエラーを確認します。 - Span が終了し、Provider がプロセス終了前にフラッシュを完了していることを確認します。強制終了した場合やルートトレースの比率を
0に設定した場合は、期待どおりのデータが表示されません。ネットワークの疎通が確認できても、それはエクスポートの成功を意味しません。