遥测
ADK-Rust 通过 adk-telemetry crate 提供生产级可观测性,该 crate 使用 tracing 生态系统和 OpenTelemetry 集成了结构化日志和分布式追踪。
概述
遥测系统支持:
- 结构化日志:具有上下文信息的丰富、可查询日志
- 分布式追踪:跨 agent 层次结构和服务边界追踪请求
- OpenTelemetry 集成:将追踪导出到可观测性后端(Jaeger、Datadog、Honeycomb 等)
- 自动上下文传播:Session、用户和调用 ID 流经所有操作
- 预配置 Spans:用于常见 ADK 操作的辅助函数
快速开始
基本控制台日志记录
对于开发和简单部署,初始化控制台日志记录:
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(())
}
这会将结构化日志记录配置到 stdout,并使用合理的默认值。
OpenTelemetry 导出
对于具有分布式追踪的生产部署:
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(())
}
这会将追踪和指标导出到 OpenTelemetry 收集器端点。
可组合层(高级)
如果您已经配置了 tracing subscriber,请使用 build_otlp_layer 获取一个可组合层,而不是初始化一个全局 subscriber:
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();
日志级别
使用 RUST_LOG 环境变量控制日志记录详细程度:
| 级别 | 描述 | 用例 |
|---|---|---|
error | 仅错误 | 生产环境 (最小) |
warn | 警告和错误 | 生产环境 (默认) |
info | 信息性消息 | 开发、预发布 |
debug | 详细调试信息 | 本地开发 |
trace | 非常详细的追踪 | 深度调试 |
设置日志级别
# 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
如果未设置 RUST_LOG,遥测系统默认为 info 级别。
日志宏
使用标准的 tracing 宏进行日志记录:
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");
结构化字段
向日志消息添加上下文字段,以便更好地进行过滤和分析:
use adk_telemetry::info;
info!(
agent.name = "customer_support",
user.id = "user-456",
session.id = "sess-789",
invocation.id = "inv-abc",
"Agent execution started"
);
这些字段在您的可观测性后端中变得可查询。
检测
自动检测
使用 #[instrument] 属性自动为函数创建 span:
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
跳过敏感参数
从跟踪中排除敏感数据:
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
}
自定义 Span 名称
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"
}
预配置 Span
ADK-Telemetry 为常见操作提供了辅助函数:
Agent 执行 Span
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
use adk_telemetry::model_call_span;
let span = model_call_span("gemini-2.5-flash");
let _enter = span.enter();
// Model API call here
工具执行 Span
use adk_telemetry::tool_execute_span;
let span = tool_execute_span("weather_tool");
let _enter = span.enter();
// Tool execution code here
回调 Span
use adk_telemetry::callback_span;
let span = callback_span("before_model");
let _enter = span.enter();
// Callback logic here
添加上下文属性
将用户和会话上下文添加到当前 span:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
LLM 令牌使用跟踪
使用 OpenTelemetry GenAI 语义约定跟踪所有 LLM 提供商的令牌消耗。llm_generate_span 创建一个带有预声明 gen_ai.usage.* 字段的 span,并在响应到达后由 record_llm_usage 填充这些字段:
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()
});
所有 ADK 模型提供商(Gemini、OpenAI、Anthropic、Ollama、Bedrock、DeepSeek、Groq、Azure AI 以及所有 OpenAI 兼容提供商)都会在每次 generate_content 调用时自动记录令牌使用情况。无需手动检测——跟踪功能已内置到提供商层中。
记录的 span 字段遵循 OpenTelemetry GenAI 约定:
| 字段 | 描述 |
|---|---|
gen_ai.usage.input_tokens | 提示词/输入 token 计数 |
gen_ai.usage.output_tokens | 补全/输出 token 计数 |
gen_ai.usage.total_tokens | 总 token 计数 |
gen_ai.usage.cache_read_tokens | 从提示词缓存中读取的 token |
gen_ai.usage.cache_creation_tokens | 用于创建缓存的 token |
gen_ai.usage.thinking_tokens | 思维链推理 token |
gen_ai.usage.audio_input_tokens | 音频输入 token 计数 |
gen_ai.usage.audio_output_tokens | 音频输出 token 计数 |
仅当提供者报告时(非 None)才记录可选字段。
手动创建 Span
对于自定义检测,请手动创建 span:
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
Span 属性
动态添加属性:
use adk_telemetry::Span;
let span = Span::current();
span.record("result.count", 42);
span.record("result.status", "success");
OpenTelemetry 配置
OTLP 端点
OTLP 导出器将跟踪发送到收集器端点:
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")?;
运行本地收集器
对于开发,请运行支持 OTLP 的 Jaeger:
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
# View traces at http://localhost:16686
跟踪可视化
配置完成后,跟踪将显示在您的可观测性后端中,显示:
- Agent 执行层次结构
- Model 调用延迟
- Tool 执行时间
- 错误传播
- 上下文流(用户 ID、session ID 等)
与 ADK 集成
ADK-Rust 组件在遥测系统初始化时自动发出遥测数据:
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(())
}
agent、model 和 tool 操作将自动发出结构化日志和跟踪。
遥测演示示例
在本地验证遥测功能选择:
cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp
在本站内嵌的 ADK-Rust Playground 中查看使用真实模型调用的完整遥测示例。
Tool 中的自定义遥测
将遥测添加到自定义 tool:
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,
);
回调中的自定义遥测
将可观测性添加到回调:
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()?;
性能考量
采样
对于高吞吐量系统,请考虑跟踪采样:
// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend
异步 Span
始终在 async 函数上使用 #[instrument] 以确保正确的 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
}
生产环境中的日志级别
在生产环境中使用 info 或 warn 级别以减少开销:
export RUST_LOG=warn,my_app=info
故障排除
未出现日志
- 检查
RUST_LOG环境变量是否已设置 - 确保在任何日志记录之前调用
init_telemetry() - 验证遥测是否仅初始化一次(内部使用
Once)
未导出跟踪
- 验证 OTLP 端点是否可达
- 检查收集器是否正在运行并接受连接
- 在应用程序退出前调用
shutdown_telemetry()以刷新待处理的 span - 检查网络/防火墙问题
Span 中缺少上下文
- 在 async 函数上使用
#[instrument] - 确保使用
let _enter = span.enter()进入 span - 在操作期间保持
_enter守卫在作用域内
最佳实践
- 尽早初始化:在
main()开始时调用init_telemetry() - 使用结构化字段:使用键值对添加上下文,而不是字符串插值
- 检测异步函数:始终在 async 函数上使用
#[instrument] - 退出时刷新:在应用程序终止前调用
shutdown_telemetry() - 适当的日志级别:对重要事件使用
info,对详细信息使用debug - 避免敏感数据:使用
#[instrument(skip(...))]跳过敏感参数 - 一致的命名:使用一致的字段名称(例如,
user.id,session.id)
相关
- Callbacks - 将遥测添加到回调
- Tools - 检测自定义 tool
- Deployment - 生产遥测设置
上一页:← Events | 下一页:Launcher →