Telemetría

ADK-Rust proporciona observabilidad de nivel de producción a través de la crate adk-telemetry, que integra el registro estructurado (structured logging) y el rastreo distribuido (distributed tracing) utilizando el ecosistema tracing y OpenTelemetry.

Resumen

El sistema de telemetría permite:

  • Registro Estructurado (Structured Logging): Registros ricos y consultables con información contextual
  • Rastreo Distribuido (Distributed Tracing): Rastrea solicitudes a través de jerarquías de Agent y límites de servicio
  • Integración con OpenTelemetry: Exporta trazas a backends de observabilidad (Jaeger, Datadog, Honeycomb, etc.)
  • Propagación Automática de Contexto: Los ID de Session, 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 (structured logging) a stdout con valores predeterminados sensatos.

Exportación OpenTelemetry

Para despliegues en producción con rastreo distribuido (distributed tracing):

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 punto final del recopilador de OpenTelemetry.

Niveles de Registro

Controle la verbosidad del registro utilizando 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 de depuración detalladaDesarrollo local
traceRastreo muy verbosoDepuració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 utiliza el nivel info por defecto 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 una 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 vuelven consultables en su backend de observabilidad.

Instrumentación

Instrumentación Automática

Usa 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
}

// Crea un span llamado "process_request" con user_id y message como campos

Omitir Parámetros Sensibles

Excluye 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 auxiliares para operaciones comunes:

Span de Ejecución de Agent

use adk_telemetry::agent_run_span;

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

// Agent execution code here
// Todos los logs dentro de este ámbito heredan el contexto del span

Span de Llamada a Model

use adk_telemetry::model_call_span;

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

// Model API call here

Span de Ejecución de Tool

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ñade contexto de usuario y sesión al span actual:

use adk_telemetry::add_context_attributes;

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

Creación Manual de Spans

Para instrumentación personalizada, crea 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ñade 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 de Conexión OTLP

El exportador OTLP envía rastreos a un punto de conexión de colector:

use adk_telemetry::init_with_otlp;

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

// Punto de conexión del proveedor de la nube
init_with_otlp("my-service", "https://otlp.example.com:4317")?;

Ejecutando un Colector Local

Para desarrollo, ejecuta 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 Rastreo

Una vez configurado, los rastreos aparecen en tu 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 sesión, etc.)

Integración con ADK

Los componentes de ADK-Rust emiten telemetría automáticamente cuando el sistema de telemetría se inicializa:

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 del Agent, model y Tool emitirán automáticamente registros y trazas estructurados.

Telemetría Personalizada en Tools

Añada telemetría a los tools personalizados:

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ñada 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:

// Nota: La configuración de muestreo depende de su configuración de OpenTelemetry
// Configure el muestreo en su colector OTLP o backend

Spans Asíncronos

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

use adk_telemetry::instrument;

// ✅ Correcto - el contexto del span se conserva en los puntos de await
#[instrument]
async fn async_operation() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

// ❌ Incorrecto - el span manual puede perder el contexto
async fn manual_span_operation() {
    let span = tracing::info_span!("operation");
    let _enter = span.enter();
    tokio::time::sleep(Duration::from_secs(1)).await;
    // El contexto puede perderse después de 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

Solución de problemas

No Aparecen Registros

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

Trazas No Exportadas

  1. Verifique que el endpoint OTLP sea alcanzable
  2. Verifique que el collector esté ejecutándose y aceptando conexiones
  3. Llame a shutdown_telemetry() antes de la salida de la aplicación para vaciar las trazas pendientes
  4. Verifique si hay problemas de red/firewall

Contexto Faltante en las Trazas

  1. Use #[instrument] en funciones async
  2. Asegúrese de que las trazas se inicien con let _enter = span.enter()
  3. Mantenga el guard _enter dentro del ámbito durante la duración de la operación

Mejores Prácticas

  1. Inicializar Temprano: Llame a init_telemetry() al inicio de main()
  2. Usar Campos Estructurados: Agregue contexto con pares clave-valor, no interpolación de cadenas
  3. Instrumentar Funciones Async: Siempre use #[instrument] en funciones async
  4. Vaciar al Salir: Llame a shutdown_telemetry() antes de la terminación de la aplicación
  5. Niveles de Registro Apropiados: Use info para eventos importantes, debug para detalles
  6. Evitar Datos Sensibles: Omita parámetros sensibles con #[instrument(skip(...))]
  7. Nomenclatura Consistente: Use nombres de campo consistentes (ej., user.id, session.id)

Relacionado

  • Callbacks - Agregue telemetría a los callbacks
  • Tools - Instrumente herramientas personalizadas
  • Deployment - Configuración de telemetría en producción

Anterior: ← Events | Siguiente: Launcher →