Telemetría
ADK-Rust proporciona observabilidad de nivel de producción mediante el crate adk-telemetry, que integra el registro estructurado y el rastreo distribuido utilizando el ecosistema tracing y OpenTelemetry.
Descripción general
El sistema de telemetría permite:
- Registro estructurado: Registros enriquecidos y consultables con información contextual
- Rastreo distribuido: Seguimiento de solicitudes a través de jerarquías de agentes y límites entre servicios
- Integración con OpenTelemetry: Exportación de trazas a backends de observabilidad (Jaeger, Datadog, Honeycomb, etc.)
- Propagación automática del contexto: Los identificadores de sesión, usuario e invocación fluyen a través de todas las operaciones
- Spans preconfigurados: Funciones auxiliares para operaciones comunes de ADK
Inicio rápido
Registro básico en consola
Para desarrollo y despliegues sencillos, inicializa 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 en stdout con valores predeterminados adecuados.
Exportación de OpenTelemetry
Para despliegues de producción con rastreo 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 recopilación de OpenTelemetry.
Capa componible (avanzado)
Si ya tienes un suscriptor tracing configurado, utiliza 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
Controla la verbosidad del registro mediante la variable de entorno RUST_LOG:
| Nivel | Descripción | Caso de uso |
|---|---|---|
error | Solo errores | Producción (mínimo) |
warn | Advertencias y errores | Producción (predeterminado) |
info | Mensajes informativos | Desarrollo, preparación |
debug | Información detallada de depuración | Desarrollo local |
trace | Trazado muy detallado | Depuración profunda |
Configuración de los 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 registro en consola toma info de forma predeterminada si no se establece RUST_LOG. La recopilación de spans del tiempo de ejecución configurada mediante init_with_adk_exporter es independiente de este filtro de consola: RUST_LOG=warn aún conserva los spans de agentes, modelos, herramientas y equipos portátiles para la interfaz de telemetría de sesiones del servidor.
Telemetría de sesiones en el proceso
Los servidores pueden exponer trazas sin infraestructura mediante la interfaz de usuario integrada del tiempo de ejecución:
let exporter = adk_telemetry::init_with_adk_exporter("my-agent")?;
let config = ServerConfig::new(agent_loader, session_service)
.with_span_exporter(exporter);
La respuesta de detalles del agente conserva el booleano services.telemetry existente y añade services.telemetryStatus:
disabled— no se ha configurado ningún exportador;configured— el exportador está listo, pero no ha conservado ningún span compatible;collecting— se ha conservado al menos un span compatible del tiempo de ejecución.
Llamar a init_with_adk_exporter más de una vez devuelve el mismo exportador registrado. Si primero se inicializó otro modo de telemetría global, devuelve un error práctico en lugar de un exportador desconectado que nunca puede recibir spans.
Macros de registro
Usa 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
Añade campos contextuales a los mensajes de registro para mejorar el filtrado y el 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 tu backend de observabilidad.
Instrumentación
Instrumentación automática
Usa el atributo #[instrument] para crear spans automáticamente para las 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
Excluye los datos sensibles de las trazas:
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 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 al modelo
use adk_telemetry::model_call_span;
let span = model_call_span("gemini-3.7-flash");
let _enter = span.enter();
// Model API call here
Span de ejecución de la herramienta
use adk_telemetry::tool_execute_span;
let span = tool_execute_span("weather_tool");
let _enter = span.enter();
// Tool execution code here
Span de devolución de llamada
use adk_telemetry::callback_span;
let span = callback_span("before_model");
let _enter = span.enter();
// Callback logic here
Adición de atributos de contexto
Añade el contexto del usuario y de la sesión al span actual:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
LLM Seguimiento del uso de tokens
Realiza un seguimiento del consumo de tokens en todos los proveedores de LLM mediante las convenciones semánticas de OpenTelemetry GenAI. llm_generate_span crea un span con campos gen_ai.usage.* predeclarados, y record_llm_usage los completa 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.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()
});
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 registrados del span siguen las convenciones de OpenTelemetry GenAI:
| Campo | Descripción |
|---|---|
gen_ai.usage.input_tokens | Recuento de tokens de indicaciones / entrada |
gen_ai.usage.output_tokens | Recuento de tokens de finalización / salida |
gen_ai.usage.total_tokens | Recuento total de tokens |
gen_ai.usage.cache_read_tokens | Tokens leídos de la caché de indicaciones |
gen_ai.usage.cache_creation_tokens | Tokens utilizados para crear la caché |
gen_ai.usage.thinking_tokens | Tokens de razonamiento de cadena de pensamiento |
gen_ai.usage.audio_input_tokens | Recuento de tokens de entrada de audio |
gen_ai.usage.audio_output_tokens | Recuento de tokens de salida de audio |
Los campos opcionales solo se registran cuando el proveedor los informa (no son None).
Creación manual de spans
Para la 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 los spans
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
Endpoint de OTLP
El exportador de OTLP envía las trazas a un endpoint del 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")?;
Ejecución de un colector local
Para el desarrollo, ejecute Jaeger con compatibilidad con 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 configuradas, las trazas aparecen en su backend de observabilidad y muestran:
- Jerarquía de ejecución del agente
- Latencias de las llamadas al modelo
- Duración de la ejecución de las herramientas
- Propagación de errores
- Flujo del contexto (ID de usuario, ID de sesión, etc.)
Integración con ADK
Los componentes de ADK-Rust emiten telemetría automáticamente cuando se inicializa el sistema de telemetría:
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(())
}
Las operaciones del agente, el modelo y las herramientas emitirán automáticamente registros y trazas estructurados.
Ejemplo de demostración de telemetría
Valide localmente la selección de funcionalidades de telemetría:
cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp
Consulte el repositorio adk-playground para ver una demostración completa de telemetría con llamadas reales a Gemini.
Telemetría personalizada en herramientas
Añada telemetría a las 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ñ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:
// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend
Spans asíncronos
Utilice siempre #[instrument] en funciones asíncronas para garantizar un 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 registro en producción
Utilice 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
- Compruebe que la variable de entorno
RUST_LOGesté definida - Asegúrese de que se llame a
init_telemetry()antes de cualquier registro - Inicialice el modo de telemetría global previsto antes de construir el servidor
Trazas no exportadas
- Verifica que se pueda acceder al endpoint de OTLP
- Comprueba que el collector esté ejecutándose y aceptando conexiones
- Llama a
shutdown_telemetry()antes de salir de la aplicación para vaciar los spans pendientes - Comprueba si hay problemas de red o del firewall
Para la interfaz de usuario del runtime integrado, comprueba services.telemetryStatus. configured
significa que el exportador está conectado, pero la sesión seleccionada aún no ha producido un
span de runtime retenido; collecting confirma que el collector ha recibido datos.
Falta de contexto en los spans
- Usa
#[instrument]en las funciones asíncronas - Asegúrate de que los spans se activen con
let _enter = span.enter() - Mantén la protección
_enterdentro del ámbito durante toda la operación
Prácticas recomendadas
- Inicializa pronto: llama a
init_telemetry()al inicio demain() - Usa campos estructurados: añade contexto con pares clave-valor, no mediante interpolación de cadenas
- Instrumenta las funciones asíncronas: usa siempre
#[instrument]en las funciones asíncronas - Vacía los datos al salir: llama a
shutdown_telemetry()antes de terminar la aplicación - Niveles de registro adecuados: usa
infopara eventos importantes ydebugpara los detalles - Evita los datos confidenciales: omite los parámetros confidenciales con
#[instrument(skip(...))] - Nombres coherentes: usa nombres de campo coherentes (por ejemplo,
user.id,session.id)
Relacionado
- Telemetría en Google Cloud - Exportación directa a Cloud Trace y JSON de Cloud Logging
- Callbacks - Añade telemetría a los callbacks
- Herramientas - Instrumenta herramientas personalizadas
- Implementación - Configuración de telemetría para producción
Anterior: ← Eventos | Siguiente: Launcher →