コンテンツにスキップ

OpenTelemetry C++ SDK

この記事では SDK によるインストルメンテーションを採用しています。アプリケーションコードで SDK を初期化し、Span を作成して Exporter を設定し、DataKit 経由でトレースを Guance に送信します。これはゼロコードインジェクションではありません。依存関係のインストールや環境変数の設定だけでは、すべてのフレームワーク呼び出しを自動的に収集することはできません。この記事では Trace のみを有効にします。Kubernetes には対応していません。

C++ + OpenTelemetry SDK -> OTLP/HTTP -> DataKit -> Guance

前提条件

  • 以下のビルドコマンドは 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 ホストで設定ディレクトリに移動します。設定ファイルが存在しない場合のみサンプルをコピーし、既存のファイルがある場合は直接編集してください:

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 を直接設定する必要はありません。

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_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. サンプルを実行した後、Guance のアプリケーションパフォーマンスモニタリング(APM)で order-service を指定してトレースを検索し、checkout とその子 Span である db.lookup が存在することを確認します。
  2. データがない場合は、コレクターが有効かどうか、アプリケーションの環境変数が反映されているか、HTTP パスに /otel/v1/traces が含まれているか、DataKit とアプリケーションのエクスポートエラーを確認します。
  3. Span が終了し、Provider がプロセス終了前にフラッシュを完了していることを確認します。強制終了した場合やルートトレースの比率を 0 に設定した場合は、期待どおりのデータが表示されません。ネットワークの疎通が確認できても、それはエクスポートの成功を意味しません。

参考ドキュメント

フィードバック

このページは役に立ちましたか?