Skip to content

OpenTelemetry

トレース・メトリクス・ログを自分のコレクター/バックエンドへ送るオプトインの OTLP エクスポート。

Updated View as Markdown

shunt は トレース・メトリクス・ログ を OTLP/HTTP で自分の OpenTelemetry Collector(または OTLP 互換バックエンド)へエクスポートできます。オプトインで、デフォルトはオフ です — [otel] セクションがなければ、何もマシンの外に出ません — そして Sentry とは独立して動作するため、どちらか一方でも両方でも有効にできます。

このページは、外へ出ていく shunt 自身の テレメトリーについてのものです。ゲートウェイの独立したテレメトリー受信は逆方向 — 管理された Claude Code クライアント が shunt へ送る OTLP ペイロード — を扱い、[server.gateway.telemetry] の宛先へ verbatim に中継します。両者は独立して設定され、異なるコレクターを指すこともできます。

有効化

キー 1 つで有効になります。コレクターの OTLP/HTTP レシーバーを指定してください:

[otel]
endpoint = "http://localhost:4318"   # OTLP/HTTP のベース URL。shunt が /v1/{traces,metrics,logs} を付加

それ以外はすべて妥当なデフォルトを持ちます:

[otel]
endpoint = "http://localhost:4318"
service_name = "shunt"     # (デフォルト) service.name リソース属性
environment = "prod"       # 任意: deployment.environment.name
sample_ratio = 1.0         # (デフォルト) ヘッドベースのトレースサンプリング、0.0–1.0
traces = true              # (デフォルト) リクエストスパンをエクスポート
metrics = true             # (デフォルト) 使用量メトリクスをエクスポート
logs = true                # (デフォルト) ログイベントをエクスポート(stderr ログには影響なし)
include_session_id = false # (デフォルト) クライアントのセッション id をスパンから除外

[otel.headers]             # 任意: リクエストごとのヘッダー(例: ホスト型コレクターのトークン)
authorization = "Bearer <token>"

endpoint = ""(例: SHUNT_OTEL__ENDPOINT="")にすると、セクションを削除せずにエクスポートを再び無効化できます。無効なエンドポイント、http(s) 以外の URL、範囲外の sample_ratio は 起動エラー になるため、タイプミスですべてのエクスポートが黙って失われることはありません。

3 つのシグナル

シグナル エクスポートされるもの 備考
トレース リクエストごとの proxy_request スパン sample_ratio によるヘッドベースサンプリング。低カーディナリティで、リクエスト/レスポンス本文は含まない。
メトリクス 以下に示す低カーディナリティの系列 [sentry] metrics = true のときに shunt が Sentry へ送るものと同じ系列。
ログ shunt の tracing ログイベントを OTLP にブリッジ stderr ログには影響しない。

各シグナルは traces / metrics / logs で個別にオン/オフできます。

メトリクス系列

系列 種類 属性 意味
shunt.requests カウンター provider, model, http.response.status_code, caller, client_kind, managed プロキシされた推論リクエスト。caller はインバウンドのトラフィックなら client、[models.router] エントリが呼び出し元に代わって行ったジャッジ呼び出しなら router。
shunt.latency ヒストグラム(ms) provider, model, http.response.status_code, caller, client_kind, managed ストリームではヘッダーまで、それ以外では応答全体のレイテンシ。コミット型ストリーミング応答はヘッダーがアップストリーム応答より先にコミットされるため、ディスパッチから試行分類までを測定します。
shunt.ttft ヒストグラム(ms) provider, model, client_kind, managed リクエスト開始から最初の完全な非キープアライブ SSE フレームまでの時間。
shunt.stream_outcome カウンター provider, model, outcome, client_kind, managed SSE の最終結果を 1 件記録: completed, error_event, upstream_cut, client_disconnect。
shunt.tokens カウンター provider, model, kind, client_kind, managed 最後に報告されたストリーミング token usage (input, output, cache_read, cache_creation)。非ストリーミングは記録しない。
shunt.codex_continuation カウンター provider, outcome Codex WebSocket continuation の hit または fallback。
shunt.codex_ws_overflow カウンター provider, outcome 並行 turn が専用 overflow 接続を開いた場合は opened、接続上限で拒否された場合は refused(issue #248)。
shunt.codex_client_events カウンター event サニタイズ済みイベント名ごとの Codex CLI analytics イベント。payload とプロパティは破棄される。
shunt.gateway_telemetry_ingest カウンター signal, outcome インバウンドのゲートウェイ OTLP ペイロード(metrics/logs/traces)を受信結果別に集計: relayed、どの宛先も opt-in していなければ discarded、飛行中の中継上限で飽和していれば shed、不正なベアラーや上限超過ボディは rejected。
shunt.upstream_retries カウンター provider, reason 回数制限付きの一時的な upstream retry。
shunt.failover カウンター provider, state 順序付きアップストリームのフェイルオーバー遷移: attempted, advanced, exhausted。
shunt.router.decisions カウンター model, algorithm, target, source すべての [models.router] type のルーティング決定と、[models.subagents] オーバーレイの決定(algorithm = "subagents")。決定を下した [[models]] エントリの id(マッチした形なので [1m] ヒントは除去済み)、設定された type、決定が解決したモデル id、選んだ理由ごとに数える。count_tokens のプローブは除外。
shunt.stage_router.decisions カウンター model, tier, source ステージルーターのティア決定。ルーターの model id(マッチした形なので [1m] ヒントは除去済み)、選ばれたティア(capable/efficient)、選んだ理由ごとに数える。count_tokens のプローブは除外。
shunt.router.judge_calls カウンター model, algorithm, outcome ジャッジを設定した [models.router] エントリが行ったジャッジ呼び出し。ルーターの model id、設定された type、呼び出しがどう決着したか(decided、または timeout、oversized、upstream_error、invalid_reply、translation_failed、budget_exhausted のいずれか)ごとに数える。budget_exhausted はセッションが max_judge_calls を使い切ったため呼び出しが拒否されたことを表す。アップストリームが途中で打ち切った応答は invalid_reply ではなく upstream_error として数える。ステージルーターではこれらの結果はいずれもそのターンを picker の既定値にルーティングし、llm_classifier や composite のエントリ、classifier 形式の [models.subagents] オーバーレイではアルゴリズム自身のフォールバックがターンを閉じる。呼び出しなしで保持された割り当てから応答したターンは retained として記録される。escalation や advisor のエントリでは、完成したターンのあとの失敗は留め置いたそのターンを代わりに返し(fail_open = false の advisor はリクエストを失敗させ、失敗したレビューはやはり upstream_error や timeout といったその結果として数え、max_judge_calls で拒否されたレビューは budget_exhausted として数える)、advisor のレビューは algorithm = "advisor" として数える。留め置いたターン自体はジャッジ呼び出しではない。
shunt.stage_router.flips カウンター model, from, to セッションを固定済みのティアから動かした決定。昇格は容易に、降格は難しくという設計なので、2 つの方向を分けて数える。
shunt.requests_shed カウンター — max_concurrent_requests の上限で拒否されたインバウンドリクエスト。拒否自体は debug! でのみログ出力されるため、ゲートウェイの飽和を可視化するのはこのカウンターです。
shunt.pool.quota_utilization ゲージ provider, window 5h, 7d, 7d_oi ごとに、有効で観測済みかつ期限切れでない quota 値の最小 utilization。
shunt.pool.rotations カウンター provider, reason アカウントからの切り替えと pool exhaustion の回数。
shunt.pool.reprobes カウンター provider 陳腐化した近接クォータの Codex/ChatGPT アカウントで、最初の HTTP 送信時にコミットされた再プローブ。WebSocket 有効プロバイダーは inbound HTTP のみを数える。
shunt.store.write_failures カウンター subsystem オプションの [store] への書き込み失敗を subsystem ごとに集計: pool(write-behind フラッシュ)、spend、gateway(トークンエンドポイント)。失敗したプールのフラッシュは次の 15 秒周期で再試行されるが、このプロセスがストアの hold を失うとプールのフラッシュは停止する。失敗した支出の書き込みは管理者の呼び出し元に、失敗したトークンエンドポイントの書き込みはクライアントに報告され、バックグラウンドのゲートウェイのフラッシュ失敗は呼び出し元がないためログにのみ記録される。
shunt.upstream.status ゲージ provider 観測された [server.status] Statuspage severity: 0 none、1 minor、2 major、3 critical。現在 unknown(信号なし)の provider は 0 として報告されるのではなく、この系列から完全に除外される。

リクエスト単位の 5 系列には属性が 2 つ追加されます。client_kind はリクエスト自身のクライアントヘッダー(x-app、originator、user-agent)から読み取った claude-code、codex、other のいずれかで、生のヘッダー値がエクスポートされることはありません。managed は、ゲートウェイが発行した JWT で認証されたリクエストのときだけ true になります。その資格情報こそが、クライアントにプッシュされた OTEL_* 環境を受け取る資格を与えるものです。両者を合わせると、shunt がサーバー側で導出した系列と、ゲートウェイモードのクライアント自身が出力する claude_code.* を区別できるため、ダッシュボードは claude_code.* があるところではそれを、それ以外では shunt.*{managed="false"} を採用し、二重計上せずに集計できます。このフィルターは、管理下のクライアントの claude_code.* メトリクスをバックエンドが実際に受け取っていることが前提です。metrics を有効にした [server.gateway.telemetry].forward_to の宛先経由でリレーされる場合も、クライアントが直接エクスポートする場合も含みます。受け取っていない場合は、managed の値を問わず shunt.* 全体を読み取ります。ジャッジ呼び出し(caller="router")は元のターンの client_kind を引き継ぎますが、managed は常に "false" です。shunt 自身のトラフィックであり、どのクライアントのエクスポーターも報告しないためです。インバウンド Codex エンドポイントは [server.auth] の資格情報しか受け付けないため、そのリクエストも常に managed="false" です。同じ 2 つの値はリクエストスパンにも shunt.client_kind / shunt.managed として記録されます。インバウンド Codex WebSocket のターンにはリクエストスパンがないため、その属性はメトリクスにだけ現れます。

プライバシー

shunt は メトリクスとトレース においてリクエスト/レスポンス本文・ヘッダー・認証情報を一切エクスポートしません。

  • メトリクスとトレース は低カーディナリティで本文を含みません。OTLP トレースエクスポートでは、リクエストスパンのクライアント セッション id は、include_session_id = true(デフォルトはオフ)のときのみ、しかもトレースエクスポートが有効な間だけコレクターへ送られます。同じルールは Sentry のトレースエクスポート([sentry] traces_sample_rate / include_session_id)にも適用されます。スパンのエクスポートが一切有効でない場合、id は従来どおりローカルのリクエストスパンにだけ残ります。
  • ログ は shunt 自身の診断イベントをそのまま反映するため、stderr ログと同様に、リクエスト由来のフィールド(アップストリームのエラー本文、認証済みクライアント id)を含みうる点に注意してください。厳密に本文なしでエクスポートしたい場合は logs = false にして、メトリクス/トレースだけを残します。

エクスポートされるリソースは service.*、telemetry.sdk.*、および environment が設定されている場合は deployment.environment.name を広告します — host や process detector は動かないため、マシンのホスト名は付与されません — これに標準の OTEL_RESOURCE_ATTRIBUTES で設定した値が加わります。

標準の OTEL_ 環境変数

  • endpoint と service_name はこの設定から取得され、OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAME より 優先されます。
  • 標準の OTEL_EXPORTER_OTLP_HEADERS と OTEL_RESOURCE_ATTRIBUTES は、[otel.headers] と組み込みのリソース属性の上に マージされます。

すべてのキーは [otel] 設定リファレンス を参照してください。

Navigation

Type to search…

↑↓ navigate↵ selectEsc close