Télémétrie

ADK-Rust fournit une observabilité de qualité production via le crate adk-telemetry, qui intÚgre la journalisation structurée et le traçage distribué en utilisant l'écosystÚme tracing et OpenTelemetry.

Aperçu

Le systÚme de télémétrie permet :

  • Journalisation StructurĂ©e : Des journaux riches et interrogeables avec des informations contextuelles
  • Traçage DistribuĂ© : Suivre les requĂȘtes Ă  travers les hiĂ©rarchies d'agents et les limites de service
  • IntĂ©gration OpenTelemetry : Exporter les traces vers des backends d'observabilitĂ© (Jaeger, Datadog, Honeycomb, etc.)
  • Propagation Automatique du Contexte : Les ID de session, d'utilisateur et d'invocation circulent Ă  travers toutes les opĂ©rations
  • Spans PrĂ©configurĂ©s : Fonctions d'aide pour les opĂ©rations ADK courantes

Démarrage Rapide

Journalisation Console Basique

Pour le développement et les déploiements simples, initialisez la journalisation console :

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(())
}

Ceci configure la journalisation structurée vers stdout avec des valeurs par défaut raisonnables.

Exportation OpenTelemetry

Pour les déploiements de production avec traçage distribué :

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(())
}

Ceci exporte les traces et les métriques vers un point de terminaison de collecteur OpenTelemetry.

Couche Composable (Avancé)

Si vous avez déjà un abonné tracing configuré, utilisez build_otlp_layer pour obtenir une couche composable au lieu d'initialiser un abonné global :

use adk_telemetry::build_otlp_layer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

let otlp_layer = build_otlp_layer("my-agent", "http://localhost:4317")?;

tracing_subscriber::registry()
    .with(otlp_layer)
    .with(tracing_subscriber::fmt::layer())
    .init();

Niveaux de Journalisation

ContrÎlez la verbosité de la journalisation en utilisant la variable d'environnement RUST_LOG :

NiveauDescriptionCas d'Utilisation
errorSeulement les erreursProduction (minimale)
warnAvertissements et erreursProduction (par défaut)
infoMessages d'informationDéveloppement, staging
debugInformations de débogage détailléesDéveloppement local
traceTraçage trÚs verbeuxDébogage approfondi

Définition des Niveaux de Log

# 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

Le systÚme de télémétrie utilise par défaut le niveau info si RUST_LOG n'est pas défini.

Macros de Log

Utilisez les macros standards tracing pour le logging :

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");

Champs Structurés

Ajoutez des champs contextuels aux messages de log pour un meilleur filtrage et une meilleure analyse :

use adk_telemetry::info;

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

Ces champs deviennent interrogeables dans votre backend d'observabilité.

Instrumentation

Instrumentation Automatique

Utilisez l'attribut #[instrument] pour créer automatiquement des spans pour les fonctions :

use adk_telemetry::{instrument, info};

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

// Creates a span named "process_request" with user_id and message as fields

Ignorer les ParamĂštres Sensibles

Excluez les données sensibles des traces :

use adk_telemetry::instrument;

#[instrument(skip(api_key))]
async fn call_external_api(api_key: &str, query: &str) {
    // api_key won't appear in traces
}

Noms de Span Personnalisés

use adk_telemetry::instrument;

#[instrument(name = "external_api_call")]
async fn fetch_data(url: &str) {
    // Span will be named "external_api_call" instead of "fetch_data"
}

Spans Préconfigurés

ADK-Telemetry fournit des fonctions d'assistance pour les opérations courantes :

Span d'Exécution d'Agent

use adk_telemetry::agent_run_span;

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

// Agent execution code here
// All logs within this scope inherit the span context

Span d'Appel de ModĂšle

use adk_telemetry::model_call_span;

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

// Model API call here

Span d'Exécution d'Outil

use adk_telemetry::tool_execute_span;

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

// Tool execution code here

Span de Callback

use adk_telemetry::callback_span;

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

// Callback logic here

Ajout d'Attributs de Contexte

Ajoutez le contexte utilisateur et de session au span actuel :

use adk_telemetry::add_context_attributes;

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

Suivi de l'Utilisation des Tokens LLM

Suivez la consommation de jetons de tous les fournisseurs LLM avec les conventions sémantiques OpenTelemetry GenAI. Le llm_generate_span crée une portée (span) avec des champs gen_ai.usage.* prédéclarés, et record_llm_usage les remplit aprÚs l'arrivée de la réponse :

use adk_telemetry::{llm_generate_span, record_llm_usage, LlmUsage};

let span = llm_generate_span("openai", "gpt-5-mini", true);
let _enter = span.enter();

// After receiving the LLM response with usage metadata:
record_llm_usage(&LlmUsage {
    input_tokens: 100,
    output_tokens: 50,
    total_tokens: 150,
    cache_read_tokens: Some(80),
    ..Default::default()
});

Tous les fournisseurs de modĂšles ADK (Gemini, OpenAI, Anthropic, Ollama, Bedrock, DeepSeek, Groq, Azure AI et tous les fournisseurs compatibles OpenAI) enregistrent automatiquement l'utilisation des jetons Ă  chaque appel de generate_content. Aucune instrumentation manuelle n'est nĂ©cessaire — le suivi est intĂ©grĂ© Ă  la couche du fournisseur.

Les champs de portée (span) enregistrés suivent les conventions OpenTelemetry GenAI :

ChampDescription
gen_ai.usage.input_tokensNombre de jetons d'invite / d'entrée
gen_ai.usage.output_tokensNombre de jetons de complétion / de sortie
gen_ai.usage.total_tokensNombre total de jetons
gen_ai.usage.cache_read_tokensJetons lus du cache d'invite
gen_ai.usage.cache_creation_tokensJetons utilisés pour créer le cache
gen_ai.usage.thinking_tokensJetons de raisonnement de chaßne de pensée
gen_ai.usage.audio_input_tokensNombre de jetons d'entrée audio
gen_ai.usage.audio_output_tokensNombre de jetons de sortie audio

Les champs optionnels ne sont enregistrés que lorsque le fournisseur les signale (non-None).

Création manuelle de portées (spans)

Pour une instrumentation personnalisée, créez des portées (spans) manuellement :

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");
// Operation code here

Attributs de portée (span)

Ajoutez des attributs dynamiquement :

use adk_telemetry::Span;

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

Configuration OpenTelemetry

Point de terminaison OTLP

L'exportateur OTLP envoie les traces Ă  un point de terminaison de collecteur :

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")?;

Exécution d'un collecteur local

Pour le développement, exécutez Jaeger avec la prise en charge OTLP :

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

# View traces at http://localhost:16686

Visualisation des traces

Une fois configurées, les traces apparaissent dans votre backend d'observabilité, montrant :

  • HiĂ©rarchie d'exĂ©cution de l'Agent
  • Latences d'appel du modĂšle
  • ChronomĂ©trage de l'exĂ©cution de l'outil
  • Propagation des erreurs
  • Flux de contexte (ID utilisateur, ID de session, etc.)

Intégration avec ADK

Les composants ADK-Rust émettent automatiquement des données de télémétrie lorsque le systÚme de télémétrie est initialisé :

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(())
}

Les opérations d'Agent, de modÚle et d'outil émettront automatiquement des logs et des traces structurés.

Exemple de démonstration de télémétrie

Valider la sélection des fonctionnalités de télémétrie localement :

cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp

Ouvrez l'ADK-Rust Playground intégré pour des exemples complets de télémétrie avec de vrais appels de modÚles.

Télémétrie personnalisée dans les Tools

Ajouter la télémétrie aux outils personnalisés :

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,
);

Télémétrie personnalisée dans les Callbacks

Ajouter l'observabilité aux callbacks :

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()?;

Considérations de performance

Échantillonnage

Pour les systÚmes à haut débit, envisagez l'échantillonnage des traces :

// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend

Spans asynchrones

Utilisez toujours #[instrument] sur les fonctions async pour assurer un contexte de span approprié :

use adk_telemetry::instrument;

// ✅ Correct - span context preserved across await points
#[instrument]
async fn async_operation() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

// ❌ Incorrect - manual span may lose context
async fn manual_span_operation() {
    let span = tracing::info_span!("operation");
    let _enter = span.enter();
    tokio::time::sleep(Duration::from_secs(1)).await;
    // Context may be lost after await
}

Niveau de log en production

Utilisez le niveau info ou warn en production pour réduire la surcharge :

export RUST_LOG=warn,my_app=info

Dépannage

Aucun log n'apparaĂźt

  1. Vérifiez que la variable d'environnement RUST_LOG est définie
  2. Assurez-vous que init_telemetry() est appelé avant toute journalisation
  3. Vérifiez que la télémétrie est initialisée une seule fois (utilise Once en interne)

Traces non exportées

  1. Vérifiez que le point de terminaison OTLP est accessible
  2. Vérifiez que le collecteur est en cours d'exécution et accepte les connexions
  3. Appelez shutdown_telemetry() avant la sortie de l'application pour vider les spans en attente
  4. Vérifiez les problÚmes de réseau/pare-feu

Contexte manquant dans les Spans

  1. Utiliser #[instrument] sur les fonctions async
  2. S'assurer que les spans sont entrés avec let _enter = span.enter()
  3. Garder le _enter guard dans la portée pour la durée de l'opération

Bonnes Pratiques

  1. Initialiser TÎt : Appeler init_telemetry() au début de main()
  2. Utiliser des Champs Structurés : Ajouter du contexte avec des paires clé-valeur, pas d'interpolation de chaßne
  3. Instrumenter les Fonctions Async : Toujours utiliser #[instrument] sur les fonctions async
  4. Vider Ă  la Sortie : Appeler shutdown_telemetry() avant la terminaison de l'application
  5. Niveaux de Log Appropriés : Utiliser info pour les événements importants, debug pour les détails
  6. Éviter les DonnĂ©es Sensibles : Ignorer les paramĂštres sensibles avec #[instrument(skip(...))]
  7. Nommage Cohérent : Utiliser des noms de champs cohérents (par exemple, user.id, session.id)
  • Callbacks - Ajouter de la tĂ©lĂ©mĂ©trie aux rappels
  • Tools - Instrumenter les outils personnalisĂ©s
  • Deployment - Configuration de la tĂ©lĂ©mĂ©trie de production

PrĂ©cĂ©dent : ← Events | Suivant : Launcher →

Télémétrie - Documentation ADK-Rust | ADK-Rust