Claude Media
Anthropic SDKでOpenTelemetryトレースを有効にする手順と注意点

Anthropic SDKでOpenTelemetryトレースを有効にする手順と注意点

Anthropic SDKのOpenTelemetry tracingは、tracer providerを登録するだけで動きます。content modeの切り替え、stream、trace contextの扱いまでコード付きで説明します。

Anthropic SDK(Claude SDK)のOpenTelemetry tracingは、アプリケーション側でtracer providerを登録すれば有効になります。SDKの呼び出しコードは変わりません。リクエストごと、そしてtool runnerが実行するツールごとにspanが出力され、自分のトレースの中に入れ子で並びます。

この機能はベータ版です。SDKのオプションやspanのスキーマは、バージョンアップで壊れる形に変わる場合があります。依存関係でSDKのバージョンを固定し、採用する時期を自分で選ぶ運用が前提になります。

この記事は、クライアントSDKのtracingを扱います。Claude Code経由で動くAgent SDKのテレメトリは仕組みが別で、Agent SDKのOpenTelemetry実装ガイドにまとめています。

SDKのspanは誰が送るのか

SDKが出すspanは、自分のアプリケーションの中で作られます。送信するのはアプリ側のOpenTelemetry exporterです。Anthropic側のサービスに送られるわけではありません。

対応範囲は、Messages APIとClaude Managed Agentsです。提供状況はClaude API、Claude Platform on AWS、Amazon Bedrock、Google Cloud、Microsoft Foundryのいずれもベータと表示されています。

Claude APIの側にも、同じリクエストのspanを出す仕組みがあります。SDKのspanはクライアント側、APIのspanはサーバー側で、両方を同じトレースに並べると1つのリクエストの往復が追えます。API側の設定は別ページの手順になるため、ここではSDK側に絞ります。

最小構成はtracer providerの登録だけ

tracer providerは、spanを作ってexporterに渡すOpenTelemetryのオブジェクトです。SDKはアプリが登録したものを使います。最初のトレース対象の呼び出しより前に登録してください。

1. SDKとOpenTelemetry SDKを入れる

Pythonの場合は次の2つです。コンソール出力用のexporterはOpenTelemetry SDKに含まれます。

pip install --upgrade anthropic
pip install opentelemetry-sdk

TypeScriptでは npm install @anthropic-ai/sdk@latest と npm install @opentelemetry/sdk-trace-node を使います。C#、Go、Java、PHP、Rubyにも同じ構成が用意されています。Javaは anthropic-java を2.73.0以上にします。

2. コンソールにspanを出す

最初はコンソールexporterで、spanが出ることだけを確かめます。

from anthropic import Anthropic
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    ConsoleSpanExporter,
    SimpleSpanProcessor,
)
 
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)
 
client = Anthropic()
message = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
)

コンソールには anthropic.messages.create というspanが1つ出ます。ここまでの追加コードは、tracer providerの登録部分だけです。

TypeScriptでは、NodeTracerProvider を作って provider.register() を呼びます。register() はcontext managerも入れるので、SDKのspanが自分のspanの下に入れ子になります。

C#は少し毛色が違い、AddSource("com.anthropic.sdk.csharp") でSDKのspanの発生源をproviderに加えます。クラウドプラットフォーム用のクライアントも含めたいときは com.anthropic.sdk.csharp* と書きます。

3. 出力先をOTLP exporterに差し替える

コンソール出力を、観測基盤に向けたOTLP exporterへ置き換えます。言語ごとの設定は、OpenTelemetry側のexporterの手順に従います。SDKに専用の設定は要らず、tracer providerのexporterを差し替えるだけです。

promptと応答をspanに載せるcontent modeの切り替え

既定のcontent modeは metadata_only です。識別子、所要時間、ステータス、トークン数、そしてツール名など自分で決めたテキストが入り、プロンプトと応答は入りません。

content に切り替えると、プロンプトと応答もspanに載ります。失敗や遅延の原因を会話の中身から調べたいとき、実トラフィックから評価データを作りたいときに効きます。

くらべる

content modeで変わること

既定

metadata_only

識別子、タイミング、ステータス、トークン数、エージェントやツールの名前など。プロンプトと応答は載りません。

明示的に切り替え

content

metadata_onlyの内容に加えて、モデルへのプロンプトと応答が載ります。観測基盤側の保存期間とアクセス権限が、組織の要件を満たすか確認が要ります。

切り替えはクライアントの生成時に行います。

client = Anthropic(
    open_telemetry={
        "tracer_provider": provider,
        "traces": {"content_mode": "content"},
    }
)

環境変数で済ませる場合は、ANTHROPIC_OPEN_TELEMETRY_TRACES_CONTENT_MODE=content を設定します。TypeScriptではキー名が openTelemetry / contentMode になり、Goでは option.WithOpenTelemetryTracesContentMode() を使うなど、言語ごとに綴りが変わります。

contentに切り替える前に押さえる4点

  • SDKのspanは保護の対象外: HIPAA、FERPA、K-12のデータ取り扱い制限がある組織では、API側の送信先はcontent modeに関係なくメタデータだけを出力します。SDKのspanはこの制限の影響を受けません。SDKで content にすれば、アプリが出力する限り中身が載ります
  • 上限は1 spanあたり0.75 MB: 超えるとSDKが内容を切り詰め、anthropic.content.truncated を true にします。リクエストのspanはシステムプロンプトと古いメッセージから先に落とし、ツールのspanは結果の末尾から落とします。画像とドキュメントは種類だけが残ります
  • 上限は変えられる: traces.max_content_bytes(環境変数は ANTHROPIC_OPEN_TELEMETRY_TRACES_MAX_CONTENT_BYTES)で調整します。1Mトークンのコンテキストを丸ごと残すには約4 MBが必要です。逆に、観測基盤やプロキシが大きなリクエストを弾く場合は下げます
  • conversation_idはモードに関係なく出る: 複数の呼び出しを1つの会話にまとめる conversation_id は gen_ai.conversation.id に書かれ、metadata_only でも送信されます。個人情報やシークレットは入れないでください

ゼロデータ保持(ZDR)の契約でも、Messages APIのAPI側スパンでFull contentは使えます。ただしManaged AgentsはZDRの対象外です。

設定の優先関係と、クライアント単位の切り替え

SDKのオプションは、コードでも環境変数でも指定できます。ここに落とし穴があります。クライアントに open_telemetry(TypeScriptなどでは openTelemetry)を渡すと、環境変数は一括して無視されます。ネストしたキーも同じで、書かなかったキーは環境変数が設定されていても既定値に戻ります。

たとえば、環境変数で content を指定していても、コードで open_telemetry={"tracer_provider": provider} だけを渡せば、content modeは metadata_only に戻ります。上のコード例が tracer_provider と traces を一緒に渡しているのはこのためです。

主なオプションを並べます。

オプション役割既定
traces.content_mode役割プロンプトと応答を載せるか既定metadata_only
traces.max_content_bytes役割spanごとの内容の上限既定0.75 MB
conversation_id役割複数呼び出しを会話にまとめる既定なし
tracer_provider役割spanを渡すprovider既定登録済みのもの
propagation役割trace contextとbaggageのヘッダー送信既定オン
open_telemetry役割クライアント単位でspanを止める既定オン

クライアント単位で止めたいときは Anthropic(open_telemetry=False) です。別のライブラリがすでにSDKを計装していて、spanが二重になる場合に使います。

conversation_id はPythonでは with_options() で渡します。with_options() はオプションをまるごと置き換えるため、client.open_telemetry を展開してから足します。

message = client.with_options(
    open_telemetry={**client.open_telemetry, "conversation_id": "conv_42"}
).messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
)

TypeScriptは1回の呼び出しのリクエストオプションに { openTelemetry: { conversationId: "conv_42" } } を渡す形です。

Javaには追加のルールがあります。クライアントが環境変数を読むのは fromEnv() で組み立てたときだけです。システムプロパティ anthropic.openTelemetry などは環境変数より優先されます。コードで上書きするときは OpenTelemetryOptions.fromEnv() から始めると、読み込んだ設定を引き継げます。

streamのspanはいつ終わるか

ストリームの anthropic.messages.create spanは、コードがストリームを最後まで読み終えるか、閉じた時点で終わります。途中で読むのをやめるなら、必ず閉じてください。閉じ忘れると、spanが終わらないままになります。

中身(content mode)を見たいときは、読み方も関係します。content でspanに応答が載るのは、messages.stream() で読んだ場合に限られます。Goは Messages.NewStreaming()、Javaは messages().createStreaming()、C#は Messages.CreateStreaming()、PHPは messages->createStream() が対応するメソッドです。

with client.messages.stream(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="")

もう1つ、ストリームを読んでいる間に自分で開始したspanは、SDKのspanの子にはなりません。兄弟になります。呼び出しと読み取りのループを1つのトレースにまとめたければ、両方を包む自分のspanを開始してください。

自分のトレースにつなぐ: 入れ子、propagator、baggage

SDKのspanを自分のspanの下に置くには、自分のspanの内側でSDKを呼びます。

tracer = trace.get_tracer("my-app")
 
with tracer.start_as_current_span("answer_question"):
    message = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello, Claude"}],
    )

リクエストごとに、SDKはアプリのpropagatorを使ってtrace contextをAPIへ traceparent などのヘッダーで送ります。そのpropagatorがbaggageも書く設定なら、baggageも一緒に送られます。baggageを送りたくない場合は、W3C trace contextのpropagatorだけを登録します。

言語によっては、propagatorが自動では登録されません。Go、Java、PHPの公式例は、W3C trace context propagatorを明示的に登録しています。登録しないと、リクエストに traceparent が付きません。

二重送信を止めるpropagation

HTTPクライアント側の計装がすでにヘッダーを付けている場合は、SDK側の送信を止めます。

client = Anthropic(open_telemetry={"propagation": False})

TypeScriptで @opentelemetry/instrumentation-undici(@opentelemetry/auto-instrumentations-node に含まれます)を使っている場合は、propagation: false が必要です。そのパッケージの不具合で traceparent が2本付き、trace contextが無効になるためです。

C#だけは仕組みが違います。.NETの DistributedContextPropagator でヘッダーを書くため、DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator() を最初のクライアント生成より前に設定します。この設定はプロセス内の全 HttpClient に効く点が、他言語のクライアント単位の設定と異なります。

MCPツールのspanと、サーバー側への引き継ぎ

SDKがMCPツールを呼んでいる間は、SDKのツールspanがカレントspanです。MCPクライアントライブラリによっては、trace contextを _meta に載せてMCPサーバーへ送ります。サーバーがそれを読めば、サーバー側のspanも同じトレースのツールspanの下に入ります。

自動でやってくれるのは、Pythonの mcp パッケージ2.0以降と、C#の ModelContextProtocol パッケージです。TypeScript、Go、Java、Ruby、PHPのMCP SDKは、そのままでは引き継ぎません。

Managed Agentsのself-hosted sandboxを追う

Managed Agentsのself-hosted sandboxでは、ツールを動かすのは自分のサンドボックスです。ツールのspanはAPIではなくSDKから出ます。

Python、TypeScript、Goの環境ワーカーが、実行するツールごとと送るリクエストごとにspanを出し、作業項目ごとに anthropic.environment.work spanの下にまとめます。手順は2つです。

  1. ワーカーを動かすプロセスで、ここまでの手順どおりにSDKのtracingを設定する
  2. サンドボックスから観測基盤、またはOpenTelemetry Collectorへのネットワーク通信を許可する

ワーカーがツール結果を送るとき、そのリクエストのAPI spanは、対応するSDK spanの下に入れ子になります。

spanが出ないときの切り分け

症状確認すること
SDKのspanが出ない確認することtracer providerを最初の呼び出しより前に登録したか。SDKを入れ直したか。ANTHROPIC_OPEN_TELEMETRY が false でないか
Javaでprovider宛に出ない、またはAPIにtrace contextが届かない確認すること他の依存が opentelemetry-api を1.57.0未満に固定していないか。1.57.0以上へ上げる
SDKのspanが自分のspanの下に入らない確認すること自分のspanの内側でSDKを呼んでいるか。Goでは、ツールが作るspanを、tool runnerが渡す ctx から開始する
リクエストに traceparent が付かない確認することW3C trace context propagatorを登録したか
streamのspanが終わらない、応答が載らない確認することストリームを閉じたか。応答の記録は messages.stream() で読んだ場合のみ
同じリクエストが2回見える確認することSDKのspanとAPIのspanは同名。span kindか service.name で見分ける。別のライブラリがSDKを計装しているなら、片方を止める

最後の「2回見える」は故障ではありません。クライアント側とサーバー側の2つのspanが、同じ名前で並んでいるだけです。

まとめ

SDKのtracingは、tracer providerの登録で始まり、機微な情報の扱いはcontent modeで決まります。ベータ版なので、SDKのバージョン固定が先です。

運用で最初に決めるのは content にするかどうかです。API側の送信先にはHIPAAなどの制限による保護がかかっても、SDKのspanにはかかりません。内容を載せるなら、観測基盤の保存期間とアクセス権限をそれに合わせて見直すところまでが1セットになります。

共通処理をHTTPレベルで差し込みたいときは、Claude SDKのミドルウェアの仕組みと役割が重なる場面があります。SDKに未対応の項目を足す方法はextra_bodyとmodel_extraで扱っています。

この記事を共有:XはてブLinkedIn