Télémétrie

ADK-Rust fournit une observabilitĂ© de niveau production via la crate adk-telemetry, qui intĂšgre la journalisation structurĂ©e et la traçabilitĂ© distribuĂ©e Ă  l’aide de l’écosystĂšme tracing et de OpenTelemetry.

Vue d’ensemble

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

  • Journalisation structurĂ©e : journaux riches et interrogeables contenant des informations contextuelles
  • TraçabilitĂ© distribuĂ©e : suivi des requĂȘtes Ă  travers les hiĂ©rarchies d’agents et les frontiĂšres entre services
  • IntĂ©gration OpenTelemetry : exportation des traces vers des backends d’observabilitĂ© (Jaeger, Datadog, Honeycomb, etc.)
  • Propagation automatique du contexte : les identifiants de session, d’utilisateur et d’invocation sont transmis Ă  travers toutes les opĂ©rations
  • Spans prĂ©configurĂ©s : fonctions d’aide pour les opĂ©rations courantes de ADK

Démarrage rapide

Journalisation console de base

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

Cela configure la journalisation structurée vers stdout avec des valeurs par défaut adaptées.

Exportation OpenTelemetry

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

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

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

Couche composable (avancé)

Si vous disposez dĂ©jĂ  d’un subscriber tracing configurĂ©, utilisez build_otlp_layer pour obtenir une couche composable au lieu d’initialiser un subscriber 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 le niveau de verbositĂ© de la journalisation Ă  l’aide de la variable d’environnement RUST_LOG :

NiveauDescriptionCas d’utilisation
errorErreurs uniquementProduction (minimal)
warnAvertissements et erreursProduction (par défaut)
infoMessages d’informationDĂ©veloppement, prĂ©production
debugInformations détaillées de débogageDéveloppement local
traceTraces trÚs verbeusesDébogage approfondi

Configuration des niveaux de journalisation

# 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

La journalisation dans la console utilise info par défaut si RUST_LOG n'est pas défini. La collecte des spans d'exécution configurée par init_with_adk_exporter est indépendante de ce filtre de console : RUST_LOG=warn conserve toujours les spans des agents, des modÚles, des outils et des équipes portables pour l'interface de télémétrie des sessions du serveur.

Télémétrie des sessions au sein du processus

Les serveurs peuvent exposer des traces sans infrastructure via l'interface utilisateur d'exécution intégrée :

let exporter = adk_telemetry::init_with_adk_exporter("my-agent")?;
let config = ServerConfig::new(agent_loader, session_service)
    .with_span_exporter(exporter);

La réponse détaillée de l'agent conserve le booléen services.telemetry existant et ajoute services.telemetryStatus :

  • disabled — aucun exportateur n'est configurĂ© ;
  • configured — l'exportateur est prĂȘt, mais n'a conservĂ© aucun span pris en charge ;
  • collecting — au moins un span d'exĂ©cution pris en charge a Ă©tĂ© conservĂ©.

Appeler init_with_adk_exporter plusieurs fois renvoie le mĂȘme exportateur enregistrĂ©. Si un autre mode de tĂ©lĂ©mĂ©trie global a Ă©tĂ© initialisĂ© auparavant, une erreur exploitable est renvoyĂ©e au lieu d'un exportateur dĂ©connectĂ© qui ne pourra jamais recevoir de spans.

Macros de journalisation

Utilisez les macros tracing standard pour la journalisation :

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 journalisation pour améliorer le filtrage et l'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 peuvent ĂȘtre interrogĂ©s 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 spans 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 utilitaires pour les opérations courantes :

Span d'exécution de l'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 du modĂšle

use adk_telemetry::model_call_span;

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

// Model API call here

Span d'exécution de l'outil

use adk_telemetry::tool_execute_span;

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

// Tool execution code here

Span de rappel

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 de l'utilisateur et de la session au span actuel :

use adk_telemetry::add_context_attributes;

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

LLM Suivi de l’utilisation des jetons

Suivez la consommation de jetons sur tous les fournisseurs LLM avec les conventions sémantiques OpenTelemetry GenAI. Le llm_generate_span crée une étendue avec des champs gen_ai.usage.* prédéclarés, et record_llm_usage les renseigne aprÚs réception de la réponse :

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

let span = llm_generate_span("openai", "gpt-5.6-terra", 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 avec OpenAI) enregistrent automatiquement l’utilisation des jetons lors de chaque appel generate_content. Aucune instrumentation manuelle n’est nĂ©cessaire — le suivi est intĂ©grĂ© Ă  la couche des fournisseurs.

Les champs d’étendue enregistrĂ©s suivent les conventions OpenTelemetry GenAI :

ChampDescription
gen_ai.usage.input_tokensNombre de jetons de l’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 depuis le cache d’invite
gen_ai.usage.cache_creation_tokensJetons utilisés pour créer le cache
gen_ai.usage.thinking_tokensJetons de raisonnement de la chaßne de pensée
gen_ai.usage.audio_input_tokensNombre de tokens d’entrĂ©e audio
gen_ai.usage.audio_output_tokensNombre de tokens de sortie audio

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

Création manuelle de spans

Pour une instrumentation personnalisée, créez des 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 des spans

Ajoutez des attributs dynamiquement :

use adk_telemetry::Span;

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

Configuration de 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 d’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Ă© et affichent :

  • HiĂ©rarchie d’exĂ©cution de l’agent
  • Latences des appels au modĂšle
  • Temps d’exĂ©cution des outils
  • Propagation des erreurs
  • Flux du 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-3.7-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 de l’agent, du modĂšle et des outils Ă©mettent automatiquement des journaux et des traces structurĂ©s.

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

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

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

Consultez le dépÎt adk-playground pour une démonstration complÚte de la télémétrie avec de véritables appels Gemini.

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

Ajoutez de 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

Ajoutez de 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 relatives aux performances

É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 asynchrones afin de garantir 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 journalisation en production

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

export RUST_LOG=warn,my_app=info

Résolution des problÚmes

Aucun journal 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. Initialisez le mode global de télémétrie souhaité avant de construire le serveur

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 fin de l’application afin de vider les spans en attente
  4. Vérifiez les problÚmes de réseau ou de pare-feu

Pour l’interface utilisateur du runtime intĂ©grĂ©, vĂ©rifiez services.telemetryStatus. configured signifie que l’exportateur est attachĂ©, mais que la session sĂ©lectionnĂ©e n’a pas produit de span de runtime conservĂ© ; collecting confirme que le collecteur a reçu les donnĂ©es.

Contexte manquant dans les spans

  1. Utilisez #[instrument] sur les fonctions asynchrones
  2. Assurez-vous que les spans sont activés avec let _enter = span.enter()
  3. Conservez la protection _enter dans la portĂ©e pendant toute la durĂ©e de l’opĂ©ration

Bonnes pratiques

  1. Initialiser tÎt : appelez init_telemetry() au début de main()
  2. Utiliser des champs structurĂ©s : ajoutez le contexte avec des paires clĂ©-valeur, plutĂŽt qu’avec une interpolation de chaĂźnes
  3. Instrumenter les fonctions asynchrones : utilisez toujours #[instrument] sur les fonctions asynchrones
  4. Vider les donnĂ©es Ă  la fin : appelez shutdown_telemetry() avant l’arrĂȘt de l’application
  5. Niveaux de journalisation appropriés : utilisez info pour les événements importants et debug pour les détails
  6. Éviter les donnĂ©es sensibles : ignorez les paramĂštres sensibles avec #[instrument(skip(...))]
  7. Nommage cohérent : utilisez des noms de champs cohérents (par exemple, user.id, session.id)

PrĂ©cĂ©dent : ← ÉvĂ©nements | Suivant : Lanceur →

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