テレメトリー

ADK-Rust は、tracing エコシステムと OpenTelemetry を使用して構造化されたロギングと分散トレーシングを統合する adk-telemetry クレートを通じて、本番環境レベルの可観測性を提供します。

概要

テレメトリーシステムは以下を可能にします。

  • 構造化ロギング: コンテキスト情報を含む、リッチでクエリ可能なログ
  • 分散トレーシング: agent 階層とサービス境界を越えたリクエストの追跡
  • OpenTelemetry Integration: 可観測性バックエンド (Jaeger, Datadog, Honeycomb など) へのトレースのエクスポート
  • Automatic Context Propagation: Session、user、invocation ID がすべての操作を流れる
  • Pre-configured Spans: 一般的な ADK 操作のためのヘルパー関数

クイックスタート

基本的なコンソールロギング

開発および単純なデプロイメントの場合、コンソールロギングを初期化します。

use adk_telemetry::init_telemetry;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize telemetry with your service name
    init_telemetry("my-agent-service")?;
    
    // Your agent code here
    
    Ok(())
}

これにより、構造化ロギングが sensible defaults で標準出力に設定されます。

OpenTelemetry エクスポート

分散トレーシングを使用する本番環境デプロイメントの場合:

use adk_telemetry::init_with_otlp;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize with OTLP exporter
    init_with_otlp("my-agent-service", "http://localhost:4317")?;
    
    // Your agent code here
    
    // Flush traces before exit
    adk_telemetry::shutdown_telemetry();
    Ok(())
}

これにより、トレースとメトリクスが OpenTelemetry コレクターのエンドポイントにエクスポートされます。

ログレベル

RUST_LOG 環境変数を使用して、ロギングの冗長性を制御します。

レベル説明ユースケース
errorエラーのみ本番環境 (最小)
warn警告とエラー本番環境 (デフォルト)
info情報メッセージ開発、ステージング
debug詳細なデバッグ情報ローカル開発
trace非常に冗長なトレーシングディープデバッグ

ログレベルの設定

# Set global log level
export RUST_LOG=info

# Set per-module log levels
export RUST_LOG=adk_agent=debug,adk_model=info

# Combine global and module-specific levels
export RUST_LOG=warn,adk_agent=debug

RUST_LOG が設定されていない場合、テレメトリーシステムはデフォルトで info レベルになります。

ロギングマクロ

ロギングには標準の tracing マクロを使用します。

use adk_telemetry::{trace, debug, info, warn, error};

// Informational logging
info!("Agent started successfully");

// Structured logging with fields
info!(
    agent.name = "my_agent",
    session.id = "sess-123",
    "Processing user request"
);

// Debug logging
debug!(user_input = ?input, "Received input");

// Warning and error logging
warn!("Rate limit approaching");
error!(error = ?err, "Failed to call model");

構造化フィールド

ログメッセージにコンテキストフィールドを追加して、フィルタリングと分析を改善します。

use adk_telemetry::info;

info!(
    agent.name = "customer_support",
    user.id = "user-456",
    session.id = "sess-789",
    invocation.id = "inv-abc",
    "Agent execution started"
);

これらのフィールドは、可観測性バックエンドでクエリ可能になります。

計測

自動計測

関数にスパンを自動作成するには、#[instrument]属性を使用します。

use adk_telemetry::{instrument, info};

#[instrument]
async fn process_request(user_id: &str, message: &str) {
    info!("Processing request");
    // Function logic here
}

// "process_request"という名前のスパンを作成し、user_idとmessageをフィールドとして含めます

機密パラメータのスキップ

トレースから機密データを除外します。

use adk_telemetry::instrument;

#[instrument(skip(api_key))]
async fn call_external_api(api_key: &str, query: &str) {
    // api_keyはトレースに表示されません
}

カスタムスパン名

use adk_telemetry::instrument;

#[instrument(name = "external_api_call")]
async fn fetch_data(url: &str) {
    // スパンは"fetch_data"の代わりに"external_api_call"という名前になります
}

事前設定されたスパン

ADK-Telemetryは、一般的な操作のためのヘルパー関数を提供します。

Agent実行スパン

use adk_telemetry::agent_run_span;

let span = agent_run_span("my_agent", "inv-123");
let _enter = span.enter();

// ここにAgentの実行コード
// このスコープ内のすべてのログはスパンコンテキストを継承します

モデル呼び出しスパン

use adk_telemetry::model_call_span;

let span = model_call_span("gemini-2.0-flash");
let _enter = span.enter();

// ここにモデルAPI呼び出し

ツール実行スパン

use adk_telemetry::tool_execute_span;

let span = tool_execute_span("weather_tool");
let _enter = span.enter();

// ここにツールの実行コード

コールバックスパン

use adk_telemetry::callback_span;

let span = callback_span("before_model");
let _enter = span.enter();

// ここにコールバックロジック

コンテキスト属性の追加

現在のスパンにユーザーおよびセッションのコンテキストを追加します。

use adk_telemetry::add_context_attributes;

add_context_attributes("user-456", "sess-789");

手動スパン作成

カスタム計測の場合、スパンを手動で作成します。

use adk_telemetry::{info, Span};

let span = tracing::info_span!(
    "custom_operation",
    operation.type = "data_processing",
    operation.id = "op-123"
);

let _enter = span.enter();
info!("Performing custom operation");
// ここに操作コード

スパン属性

属性を動的に追加します。

use adk_telemetry::Span;

let span = Span::current();
span.record("result.count", 42);
span.record("result.status", "success");

OpenTelemetry設定

OTLPエンドポイント

OTLPエクスポーターは、コレクターエンドポイントにトレースを送信します。

use adk_telemetry::init_with_otlp;

// Local Jaeger (default OTLP port)
init_with_otlp("my-service", "http://localhost:4317")?;

// Cloud provider endpoint
init_with_otlp("my-service", "https://otlp.example.com:4317")?;

ローカルコレクターの実行

開発のために、OTLPサポート付きでJaegerを実行します。

docker run -d --name jaeger \
  -p 4317:4317 \
  -p 16686:16686 \
  jaegertracing/all-in-one:latest

# http://localhost:16686でトレースを表示

トレースの可視化

設定が完了すると、トレースはオブザーバビリティバックエンドに次のように表示されます。

  • Agentの実行階層
  • モデル呼び出しのレイテンシ
  • ツールの実行タイミング
  • エラー伝播
  • コンテキストフロー (ユーザーID、セッションIDなど)

ADKとの統合

テレメトリーシステムが初期化されると、adk-rustコンポーネントは自動的にテレメトリーを出力します。

use adk_rust::prelude::*;
use adk_telemetry::init_telemetry;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    // Initialize telemetry first
    init_telemetry("my-agent-app")?;
    
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
    
    let agent = LlmAgentBuilder::new("support_agent")
        .model(model)
        .instruction("You are a helpful support agent.")
        .build()?;
    
    // Use Launcher for simple execution
    Launcher::new(Arc::new(agent)).run().await?;
    
    Ok(())
}

Agent、model、およびToolの操作は、構造化されたログとトレースを自動的に出力します。

Toolにおけるカスタムテレメトリー

カスタムToolにテレメトリーを追加します。

use adk_rust::prelude::*;
use adk_telemetry::{info, instrument, tool_execute_span};
use serde_json::{json, Value};

#[instrument(skip(ctx))]
async fn weather_tool_impl(
    ctx: Arc<dyn ToolContext>,
    args: Value,
) -> Result<Value> {
    let span = tool_execute_span("weather_tool");
    let _enter = span.enter();
    
    let location = args["location"].as_str().unwrap_or("unknown");
    info!(location = location, "Fetching weather data");
    
    // Tool logic here
    let result = json!({
        "temperature": 72,
        "condition": "sunny"
    });
    
    info!(location = location, "Weather data retrieved");
    Ok(result)
}

let weather_tool = FunctionTool::new(
    "get_weather",
    "Get current weather for a location",
    json!({
        "type": "object",
        "properties": {
            "location": {"type": "string"}
        },
        "required": ["location"]
    }),
    weather_tool_impl,
);

Callbackにおけるカスタムテレメトリー

Callbackに可観測性を追加します。

use adk_rust::prelude::*;
use adk_telemetry::{info, callback_span};
use std::sync::Arc;

let agent = LlmAgentBuilder::new("observed_agent")
    .model(model)
    .before_callback(Box::new(|ctx| {
        Box::pin(async move {
            let span = callback_span("before_agent");
            let _enter = span.enter();
            
            info!(
                agent.name = ctx.agent_name(),
                user.id = ctx.user_id(),
                session.id = ctx.session_id(),
                "Agent execution starting"
            );
            
            Ok(None)
        })
    }))
    .after_callback(Box::new(|ctx| {
        Box::pin(async move {
            let span = callback_span("after_agent");
            let _enter = span.enter();
            
            info!(
                agent.name = ctx.agent_name(),
                "Agent execution completed"
            );
            
            Ok(None)
        })
    }))
    .build()?;

パフォーマンスに関する考慮事項

サンプリング

高スループットシステムでは、トレースのサンプリングを検討してください。

// 注: サンプリング設定はOpenTelemetryのセットアップに依存します
// OTLPコレクターまたはバックエンドでサンプリングを設定してください

Asyncスパン

適切なスパンコンテキストを確保するために、async関数では常に#[instrument]を使用してください。

use adk_telemetry::instrument;

// ✅ 正しい - awaitポイントをまたいでスパンコンテキストが保持されます
#[instrument]
async fn async_operation() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

// ❌ 不正確 - 手動スパンはコンテキストを失う可能性があります
async fn manual_span_operation() {
    let span = tracing::info_span!("operation");
    let _enter = span.enter();
    tokio::time::sleep(Duration::from_secs(1)).await;
    // await後にコンテキストが失われる可能性があります
}

本番環境でのログレベル

オーバーヘッドを削減するために、本番環境ではinfoまたはwarnレベルを使用してください。

export RUST_LOG=warn,my_app=info

トラブルシューティング

ログが表示されない

  1. RUST_LOG 環境変数が設定されているか確認してください
  2. ロギングの前に init_telemetry() が呼び出されていることを確認してください
  3. テレメトリが一度だけ初期化されていることを確認してください (Once を内部的に使用しています)

トレースがエクスポートされない

  1. OTLP エンドポイントに到達可能か確認してください
  2. コレクタが実行中で接続を受け入れているか確認してください
  3. アプリケーション終了前に shutdown_telemetry() を呼び出して、保留中のスパンをフラッシュしてください
  4. ネットワーク/ファイアウォールの問題を確認してください

スパン内のコンテキストが欠落している

  1. async 関数で #[instrument] を使用してください
  2. スパンが let _enter = span.enter() で入力されていることを確認してください
  3. 操作の期間中、_enter ガードをスコープ内に保持してください

ベストプラクティス

  1. 早期初期化: main() の開始時に init_telemetry() を呼び出してください
  2. 構造化フィールドの使用: 文字列補間ではなく、キーと値のペアでコンテキストを追加してください
  3. async 関数の計測: async 関数では常に #[instrument] を使用してください
  4. 終了時のフラッシュ: アプリケーション終了前に shutdown_telemetry() を呼び出してください
  5. 適切なログレベル: 重要なイベントには info を、詳細には debug を使用してください
  6. 機密データの回避: #[instrument(skip(...))] を使用して機密パラメーターをスキップしてください
  7. 一貫した命名: 一貫したフィールド名 (例: user.idsession.id) を使用してください

関連

  • Callbacks - コールバックにテレメトリを追加する
  • Tools - カスタムツールを計測する
  • Deployment - 本番環境のテレメトリ設定

前へ: ← Events | 次へ: Launcher →