Telemetria
ADK-Rust fornece observabilidade de nível de produção através da crate adk-telemetry, que integra log estruturado e rastreamento distribuído usando o ecossistema tracing e OpenTelemetry.
Visão Geral
O sistema de telemetria permite:
- Log Estruturado: Logs ricos e consultáveis com informações contextuais
- Rastreamento Distribuído: Rastreie solicitações através de hierarquias de agentes e limites de serviço
- Integração OpenTelemetry: Exporte rastreamentos para backends de observabilidade (Jaeger, Datadog, Honeycomb, etc.)
- Propagação Automática de Contexto: IDs de sessão, usuário e invocação fluem por todas as operações
- Spans Pré-configurados: Funções auxiliares para operações comuns do ADK
Início Rápido
Log Básico no Console
Para desenvolvimento e implantações simples, inicialize o log do 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(())
}
Isso configura o log estruturado para stdout com padrões sensatos.
Exportação OpenTelemetry
Para implantações em produção com rastreamento distribuído:
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(())
}
Isso exporta rastreamentos e métricas para um endpoint de coletor OpenTelemetry.
Camada Componível (Avançado)
Se você já tem um tracing assinante configurado, use build_otlp_layer para obter uma camada componível em vez de inicializar um assinante 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();
Níveis de Log
Controle a verbosidade do logging usando a variável de ambiente RUST_LOG:
| Nível | Descrição | Caso de Uso |
|---|---|---|
error | Apenas erros | Produção (mínima) |
warn | Avisos e erros | Produção (padrão) |
info | Mensagens informativas | Desenvolvimento, homologação |
debug | Informações detalhadas de depuração | Desenvolvimento local |
trace | Rastreamento muito detalhado | Depuração profunda |
Configurando Níveis 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
O sistema de telemetria usa o nível info por padrão se RUST_LOG não estiver definido.
Macros de Log
Use as macros tracing padrão para log:
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 Estruturados
Adicione campos contextuais às mensagens de log para melhor filtragem e análise:
use adk_telemetry::info;
info!(
agent.name = "customer_support",
user.id = "user-456",
session.id = "sess-789",
invocation.id = "inv-abc",
"Agent execution started"
);
Esses campos se tornam consultáveis no seu backend de observabilidade.
Instrumentação
Instrumentação Automática
Use o atributo #[instrument] para criar spans automaticamente para funções:
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
Ignorar Parâmetros Sensíveis
Exclua dados sensíveis dos rastreamentos:
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
}
Nomes 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 Pré-configurados
ADK-Telemetry fornece funções auxiliares para operações comuns:
Span de Execução 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
// All logs within this scope inherit the span context
Span de Chamada de 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 Execução 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
Adicionando Atributos de Contexto
Adicionar contexto de usuário e sessão ao span atual:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
Rastreamento de Uso de Tokens LLM
Rastreie o consumo de tokens em todos os provedores LLM com as convenções semânticas OpenTelemetry GenAI. O llm_generate_span cria um span com campos gen_ai.usage.* pré-declarados, e o record_llm_usage os preenche após a chegada da resposta:
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 os provedores de modelo ADK (Gemini, OpenAI, Anthropic, Ollama, Bedrock, DeepSeek, Groq, Azure AI, e todos os provedores compatíveis com OpenAI) registram automaticamente o uso de tokens em cada chamada generate_content. Nenhuma instrumentação manual é necessária — o rastreamento é integrado à camada do provedor.
Os campos de span registrados seguem as convenções OpenTelemetry GenAI:
| Campo | Descrição |
|---|---|
gen_ai.usage.input_tokens | Contagem de tokens de prompt / entrada |
gen_ai.usage.output_tokens | Conclusão / contagem de tokens de saída |
gen_ai.usage.total_tokens | Contagem total de tokens |
gen_ai.usage.cache_read_tokens | Tokens lidos do cache de prompt |
gen_ai.usage.cache_creation_tokens | Tokens usados para criar cache |
gen_ai.usage.thinking_tokens | Tokens de raciocínio em cadeia de pensamento |
gen_ai.usage.audio_input_tokens | Contagem de tokens de entrada de áudio |
gen_ai.usage.audio_output_tokens | Contagem de tokens de saída de áudio |
Campos opcionais são registrados apenas quando o provedor os reporta (não-None).
Criação Manual de Spans
Para instrumentação personalizada, crie 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
Adicione atributos dinamicamente:
use adk_telemetry::Span;
let span = Span::current();
span.record("result.count", 42);
span.record("result.status", "success");
Configuração do OpenTelemetry
Endpoint OTLP
O exportador OTLP envia traces para um endpoint de coletor:
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")?;
Executando um Coletor Local
Para desenvolvimento, execute o Jaeger com suporte OTLP:
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
# View traces at http://localhost:16686
Visualização de Traces
Uma vez configurado, os traces aparecem no seu backend de observabilidade mostrando:
- Hierarquia de execução do Agent
- Latências de chamadas do Model
- Tempo de execução do Tool
- Propagação de erros
- Fluxo de contexto (user ID, session ID, etc.)
Integração com ADK
ADK-Rust componentes emitem telemetria automaticamente quando o sistema de telemetria é 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(())
}
As operações de agent, model e tool emitirão automaticamente logs e traces estruturados.
Exemplo de Demonstração de Telemetria
Valide a seleção de recursos de telemetria localmente:
cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp
Abra o ADK-Rust Playground incorporado para ver exemplos completos de telemetria com chamadas reais de modelos.
Telemetria Personalizada em Tools
Adicione telemetria a tools 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,
);
Telemetria Personalizada em Callbacks
Adicione observabilidade a 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()?;
Considerações de Desempenho
Amostragem
Para sistemas de alto throughput, considere a amostragem de traces:
// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend
Spans Async
Sempre use #[instrument] em funções async para garantir o contexto de span adequado:
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
}
Nível de Log em Produção
Use o nível info ou warn em produção para reduzir a sobrecarga:
export RUST_LOG=warn,my_app=info
Solução de Problemas
Nenhum Log Aparecendo
- Verifique se a variável de ambiente
RUST_LOGestá definida - Garanta que
init_telemetry()seja chamada antes de qualquer registro (logging) - Verifique se a telemetria é inicializada apenas uma vez (usa
Onceinternamente)
Traços Não Exportados
- Verifique se o endpoint OTLP está acessível
- Verifique se o coletor está em execução e aceitando conexões
- Chame
shutdown_telemetry()antes do encerramento da aplicação para descarregar (flush) spans pendentes - Verifique a existência de problemas de rede/firewall
Contexto Ausente em Spans
- Use
#[instrument]em funções async - Garanta que os spans sejam inseridos com
let _enter = span.enter() - Mantenha o guard
_enterno escopo pela duração da operação
Melhores Práticas
- Inicialize Cedo: Chame
init_telemetry()no início demain() - Use Campos Estruturados: Adicione contexto com pares chave-valor, não interpolação de string
- Instrumente Funções Async: Sempre use
#[instrument]em async functions - Descarregue na Saída: Chame
shutdown_telemetry()antes do encerramento da aplicação - Níveis de Log Apropriados: Use
infopara eventos importantes,debugpara detalhes - Evite Dados Sensíveis: Pule parâmetros sensíveis com
#[instrument(skip(...))] - Nomenclatura Consistente: Use nomes de campo consistentes (ex:
user.id,session.id)
Relacionado
- Callbacks - Adicione telemetria a callbacks
- Tools - Instrumente ferramentas personalizadas
- Implantação - Configuração de telemetria de produção
Anterior: ← Eventos | Próximo: Lançador →