Telemetría

ADK-Rust proporciona observabilidad de grado de producción a través del crate adk-telemetry, que integra registro estructurado y trazado distribuido utilizando el ecosistema tracing y OpenTelemetry.

Resumen

El sistema de telemetría permite:

  • Registro Estructurado: Registros ricos y consultables con información contextual
  • Trazado Distribuido: Rastrea solicitudes a través de jerarquías de agentes y límites de servicios
  • Integración de OpenTelemetry: Exporta trazas a backends de observabilidad (Jaeger, Datadog, Honeycomb, etc.)
  • Propagación Automática de Contexto: Los ID de sesión, usuario e invocación fluyen a través de todas las operaciones
  • Spans Preconfigurados: Funciones de ayuda para operaciones comunes de ADK

Inicio Rápido

Registro Básico en Consola

Para desarrollo y despliegues sencillos, inicialice el registro en consola:

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

Esto configura el registro estructurado a stdout con valores predeterminados sensatos.

Exportación de OpenTelemetry

Para despliegues en producción con trazado distribuido:

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

Esto exporta trazas y métricas a un endpoint de colector de OpenTelemetry.

Capa Componible (Avanzado)

Si ya tiene un suscriptor de tracing configurado, use build_otlp_layer para obtener una capa componible en lugar de inicializar un suscriptor 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();

Niveles de Registro

Controle la verbosidad del registro usando la variable de entorno RUST_LOG:

NivelDescripciónCaso de Uso
errorSolo erroresProducción (mínimo)
warnAdvertencias y erroresProducción (predeterminado)
infoMensajes informativosDesarrollo, staging
debugInformación detallada de depuraciónDesarrollo local
traceRastreo muy detalladoDepuración profunda

Configuración de Niveles de Registro

# 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

El sistema de telemetría por defecto utiliza el nivel info si RUST_LOG no está configurado.

Macros de Registro

Utilice las macros estándar de tracing para el registro:

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

Campos Estructurados

Agregue campos contextuales a los mensajes de registro para un mejor filtrado y análisis:

use adk_telemetry::info;

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

Estos campos se pueden consultar en su backend de observabilidad.

Instrumentación

Instrumentación Automática

Utilice el atributo #[instrument] para crear automáticamente spans para funciones:

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

Omitir Parámetros Sensibles

Excluir datos sensibles de los rastreos:

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
}

Nombres de Span Personalizados

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 Preconfigurados

ADK-Telemetry proporciona funciones de ayuda para operaciones comunes:

Span de Ejecución del Agente

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 de Llamada a Modelo

use adk_telemetry::model_call_span;

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

// Model API call here

Span de Ejecución de Herramienta

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

Añadiendo Atributos de Contexto

Añada el contexto de usuario y sesión al span actual:

use adk_telemetry::add_context_attributes;

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

Seguimiento de Uso de Tokens LLM

Rastrea el consumo de tokens en todos los proveedores de LLM con las convenciones semánticas de OpenTelemetry GenAI. llm_generate_span crea un span con campos gen_ai.usage.* preestablecidos, y record_llm_usage los rellena después de que llega la respuesta:

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

Todos los proveedores de modelos ADK (Gemini, OpenAI, Anthropic, Ollama, Bedrock, DeepSeek, Groq, Azure AI, y todos los proveedores compatibles con OpenAI) registran automáticamente el uso de tokens en cada llamada a generate_content. No se necesita instrumentación manual — el seguimiento está integrado en la capa del proveedor.

Los campos de span registrados siguen las convenciones de OpenTelemetry GenAI:

CampoDescripción
gen_ai.usage.input_tokensRecuento de tokens de prompt / entrada
gen_ai.usage.output_tokensRecuento de tokens de finalización / salida
gen_ai.usage.total_tokensRecuento total de tokens
gen_ai.usage.cache_read_tokensTokens leídos de la caché de prompt
gen_ai.usage.cache_creation_tokensTokens utilizados para crear la caché
gen_ai.usage.thinking_tokensTokens de razonamiento de cadena de pensamiento
gen_ai.usage.audio_input_tokensRecuento de tokens de entrada de audio
gen_ai.usage.audio_output_tokensRecuento de tokens de salida de audio

Los campos opcionales solo se registran cuando el proveedor los reporta (distinto de None).

Creación Manual de Spans

Para instrumentación personalizada, cree spans manualmente:

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

Atributos de Span

Añada atributos dinámicamente:

use adk_telemetry::Span;

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

Configuración de OpenTelemetry

Punto Final OTLP

El exportador OTLP envía trazas a un punto final de colector:

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

Ejecutar un recopilador local

Para desarrollo, ejecute Jaeger con soporte OTLP:

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

# View traces at http://localhost:16686

Visualización de trazas

Una vez configurado, las trazas aparecen en su backend de observabilidad mostrando:

  • Jerarquía de ejecución de Agent
  • Latencias de llamadas a Model
  • Tiempos de ejecución de Tool
  • Propagación de errores
  • Flujo de contexto (ID de usuario, ID de session, etc.)

Integración con ADK

ADK-Rust componentes emiten telemetría automáticamente cuando el sistema de telemetría es inicializado:

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

Las operaciones de Agent, model y Tool emitirán automáticamente registros estructurados y trazas.

Ejemplo de demostración de telemetría

Valide la selección de características de telemetría localmente:

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

Abra el ADK-Rust Playground integrado para ver ejemplos completos de telemetría con llamadas reales a modelos.

Telemetría personalizada en Tools

Agregue telemetría a herramientas personalizadas:

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

Telemetría Personalizada en Callbacks

Añadir observabilidad a los 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()?;

Consideraciones de Rendimiento

Muestreo

Para sistemas de alto rendimiento, considere el muestreo de trazas:

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

Spans Asíncronos

Siempre use #[instrument] en funciones async para asegurar el contexto de span adecuado:

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
}

Nivel de Log en Producción

Use el nivel info o warn en producción para reducir la sobrecarga:

export RUST_LOG=warn,my_app=info

Resolución de Problemas

No Aparecen Logs

  1. Verifique que la variable de entorno RUST_LOG esté configurada
  2. Asegúrese de que init_telemetry() se llame antes de cualquier registro
  3. Verifique que la telemetría se inicialice solo una vez (utiliza Once internamente)

Trazas No Exportadas

  1. Verificar que el endpoint OTLP sea accesible
  2. Verificar que el collector esté funcionando y aceptando conexiones
  3. Llamar a shutdown_telemetry() antes de la salida de la aplicación para vaciar los spans pendientes
  4. Buscar problemas de red/firewall

Contexto Faltante en Spans

  1. Usar #[instrument] en funciones async
  2. Asegurarse de que los spans se ingresen con let _enter = span.enter()
  3. Mantener el guard _enter en el ámbito durante la duración de la operación

Mejores Prácticas

  1. Inicializar Temprano: Llamar a init_telemetry() al inicio de main()
  2. Usar Campos Estructurados: Añadir contexto con pares clave-valor, no interpolación de cadenas
  3. Instrumentar Funciones Asíncronas: Usar siempre #[instrument] en funciones asíncronas
  4. Vaciar al Salir: Llamar a shutdown_telemetry() antes de la terminación de la aplicación
  5. Niveles de Registro Apropiados: Usar info para eventos importantes, debug para detalles
  6. Evitar Datos Sensibles: Omitir parámetros sensibles con #[instrument(skip(...))]
  7. Nomenclatura Consistente: Usar nombres de campo consistentes (por ejemplo, user.id, session.id)
  • Callbacks - Añadir telemetría a las callbacks
  • Herramientas - Instrumentar herramientas personalizadas
  • Despliegue - Configuración de telemetría en producción

Anterior: ← Eventos | Siguiente: Lanzador →