Telemetria
ADK-Rust fornece observabilidade de nível de produção por meio do crate adk-telemetry, que integra registro estruturado e rastreamento distribuído usando o ecossistema tracing e OpenTelemetry.
Visão geral
O sistema de telemetria oferece:
- Registro estruturado: Logs avançados e consultáveis com informações contextuais
- Rastreamento distribuído: Acompanhe solicitações entre hierarquias de agentes e limites de serviços
- Integração com 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 de ADK
Início rápido
Registro básico no console
Para desenvolvimento e implantações simples, inicialize o registro no 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 registro estruturado para stdout com padrões adequados.
Exportação de OpenTelemetry
Para implantações de 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 coletor OpenTelemetry.
Camada componível (avançado)
Se você já tiver um subscriber tracing configurado, use build_otlp_layer para obter uma camada componível em vez de inicializar um 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();
Níveis de log
Controle a verbosidade do registro usando a variável de ambiente RUST_LOG:
| Nível | Descrição | Caso de uso |
|---|---|---|
error | Apenas erros | Produção (mínimo) |
warn | Avisos e erros | Produção (padrão) |
info | Mensagens informativas | Desenvolvimento, preparação |
debug | Informações detalhadas de depuração | Desenvolvimento local |
trace | Rastreamento muito detalhado | Depuração profunda |
Configuração dos 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 registro em console usa info por padrão se RUST_LOG não estiver definido. A coleta de spans em runtime configurada por init_with_adk_exporter é independente deste filtro do console: RUST_LOG=warn ainda retém spans de agente, modelo, ferramenta e equipe portátil para a interface de telemetria de sessões do servidor.
Telemetria de sessões no processo
Os servidores podem expor traces sem infraestrutura por meio da UI de runtime incorporada:
let exporter = adk_telemetry::init_with_adk_exporter("my-agent")?;
let config = ServerConfig::new(agent_loader, session_service)
.with_span_exporter(exporter);
A resposta de detalhes do agente preserva o booleano services.telemetry existente e adiciona services.telemetryStatus:
disabled— nenhum exportador está configurado;configured— o exportador está pronto, mas ainda não reteve um span compatível;collecting— pelo menos um span de runtime compatível foi retido.
Chamar init_with_adk_exporter mais de uma vez retorna o mesmo exportador registrado. Se outro modo global de telemetria tiver sido inicializado primeiro, ele retorna um erro acionável em vez de um exportador desconectado que nunca poderá receber spans.
Macros de log
Use as macros padrão tracing para 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 estruturados
Adicione campos contextuais às mensagens de log para melhorar a filtragem e a 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 podem ser consultados 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 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
}
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 do 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 chamada do 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 execução da ferramenta
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
Adicione o contexto do usuário e da sessão ao span atual:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
LLM Rastreamento do uso de tokens
Acompanhe 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 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.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 os provedores de modelos 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. Não é necessária instrumentação manual — o rastreamento está integrado à camada do provedor.
Os campos registrados no span seguem as convenções OpenTelemetry GenAI:
| Campo | Descrição |
|---|---|
gen_ai.usage.input_tokens | Contagem de tokens do prompt / entrada |
gen_ai.usage.output_tokens | Contagem de tokens da conclusão / 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 o cache |
gen_ai.usage.thinking_tokens | Tokens de raciocínio do chain-of-thought |
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 |
Os campos opcionais só são registrados quando o provedor os informa (diferentes de 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 dos spans
Adicione atributos dinamicamente:
use adk_telemetry::Span;
let span = Span::current();
span.record("result.count", 42);
span.record("result.status", "success");
Configuração de 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")?;
Execução de um coletor local
Para desenvolvimento, execute o Jaeger com suporte a 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
Depois de configurados, os traces aparecem no seu backend de observabilidade, exibindo:
- Hierarquia de execução do agente
- Latências das chamadas ao modelo
- Duração da execução das ferramentas
- Propagação de erros
- Fluxo de contexto (ID do usuário, ID da sessão etc.)
Integração com ADK
Os componentes de ADK-Rust 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-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(())
}
As operações do agente, do modelo e das ferramentas emitirão automaticamente logs e traces estruturados.
Exemplo de demonstração de telemetria
Valide localmente a seleção do recurso de telemetria:
cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp
Consulte o repositório adk-playground para ver uma demonstração completa de telemetria com chamadas reais ao Gemini.
Telemetria personalizada em ferramentas
Adicione telemetria a ferramentas 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 aos 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 volume, considere a amostragem de traces:
// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend
Spans assíncronos
Sempre use #[instrument] em funções assíncronas para garantir o contexto adequado do span:
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 aparece
- Verifique se a variável de ambiente
RUST_LOGestá definida - Certifique-se de que
init_telemetry()seja chamado antes de qualquer registro de log - Inicialize o modo global de telemetria pretendido antes de construir o servidor
Rastreamentos 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 da saída da aplicação para liberar os spans pendentes - Verifique se há problemas de rede/firewall
Para a interface do usuário do runtime incorporado, verifique services.telemetryStatus. configured
significa que o exporter está anexado, mas a sessão selecionada ainda não produziu um
span de runtime retido; collecting confirma que o coletor recebeu dados.
Contexto ausente nos spans
- Use
#[instrument]em funções assíncronas - Certifique-se de que os spans sejam inseridos com
let _enter = span.enter() - Mantenha a proteção
_enterno escopo durante toda a operação
Práticas recomendadas
- Inicialize cedo: chame
init_telemetry()no início demain() - Use campos estruturados: adicione contexto com pares de chave-valor, não com interpolação de strings
- Instrumente funções assíncronas: sempre use
#[instrument]em funções assíncronas - Libere os dados na saída: chame
shutdown_telemetry()antes do encerramento da aplicação - Níveis de log apropriados: use
infopara eventos importantes edebugpara detalhes - Evite dados confidenciais: ignore parâmetros confidenciais com
#[instrument(skip(...))] - Nomenclatura consistente: use nomes de campos consistentes (por exemplo,
user.id,session.id)
Relacionados
- Telemetria para o Google Cloud - Exportação direta para o Cloud Trace e Cloud Logging JSON
- Callbacks - Adicione telemetria aos callbacks
- Ferramentas - Instrumente ferramentas personalizadas
- Implantação - Configuração da telemetria de produção
Anterior: ← Eventos | Próximo: Launcher →