القياس عن بُعد
يوفّر ADK-Rust إمكانية رصد بمستوى الإنتاج من خلال حزمة adk-telemetry، التي تدمج التسجيل المنظّم والتتبّع الموزّع باستخدام منظومة tracing وOpenTelemetry.
نظرة عامة
يتيح نظام القياس عن بُعد ما يلي:
- التسجيل المنظّم: سجلات غنية وقابلة للاستعلام تتضمن معلومات سياقية
- التتبّع الموزّع: تتبّع الطلبات عبر تسلسلات agent وحدود الخدمات
- تكامل OpenTelemetry: تصدير آثار التتبّع إلى أنظمة خلفية للرصد (Jaeger وDatadog وHoneycomb وغيرها)
- نشر السياق تلقائيًا: تنتقل معرّفات الجلسة والمستخدم والاستدعاء عبر جميع العمليات
- 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 مهيّأ، فاستخدم build_otlp_layer للحصول على طبقة قابلة للتركيب بدلًا من تهيئة مشترك عام:
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
يكون تسجيل وحدة التحكم افتراضيًا على info إذا لم يتم تعيين RUST_LOG. ويكون جمع نطاقات وقت التشغيل الذي تم تكوينه بواسطة init_with_adk_exporter مستقلًا عن عامل تصفية وحدة التحكم هذا: إذ لا يزال RUST_LOG=warn يحتفظ بنطاقات الوكيل والنموذج والأداة والفريق المحمول لواجهة القياس عن بُعد لجلسة الخادم.
القياس عن بُعد للجلسات داخل العملية
يمكن للخوادم عرض آثار لا تتطلب بنية تحتية من خلال واجهة المستخدم المضمنة لوقت التشغيل:
let exporter = adk_telemetry::init_with_adk_exporter("my-agent")?;
let config = ServerConfig::new(agent_loader, session_service)
.with_span_exporter(exporter);
تحافظ استجابة تفاصيل الوكيل على القيمة المنطقية الحالية services.telemetry وتضيف services.telemetryStatus:
disabled— لم يتم تكوين أي مُصدِّر؛configured— المُصدِّر جاهز، لكنه لم يحتفظ بأي نطاق مدعوم؛collecting— تم الاحتفاظ بنطاق واحد مدعوم على الأقل من نطاقات وقت التشغيل.
يؤدي استدعاء init_with_adk_exporter أكثر من مرة إلى إرجاع المُصدِّر المسجل نفسه. وإذا تمت تهيئة وضع قياس عن بُعد عام آخر أولًا، فسيُرجع خطأً قابلًا للتنفيذ بدلًا من مُصدِّر غير متصل لا يمكنه تلقي النطاقات مطلقًا.
وحدات ماكرو التسجيل
استخدم وحدات ماكرو 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] لإنشاء نطاقات تلقائيًا للدوال:
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
}
أسماء النطاقات المخصصة
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"
}
النطاقات المُهيَّأة مسبقًا
يوفر ADK-Telemetry دوالًا مساعدة للعمليات الشائعة:
نطاق تنفيذ الوكيل
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
نطاق استدعاء النموذج
use adk_telemetry::model_call_span;
let span = model_call_span("gemini-3.7-flash");
let _enter = span.enter();
// Model API call here
نطاق تنفيذ الأداة
use adk_telemetry::tool_execute_span;
let span = tool_execute_span("weather_tool");
let _enter = span.enter();
// Tool execution code here
نطاق الاستدعاء اللاحق
use adk_telemetry::callback_span;
let span = callback_span("before_model");
let _enter = span.enter();
// Callback logic here
إضافة سمات السياق
أضف سياق المستخدم والجلسة إلى النطاق الحالي:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
LLM تتبّع استخدام الرموز
تتبّع استهلاك الرموز عبر جميع مزوّدي LLM باستخدام الاصطلاحات الدلالية لـ OpenTelemetry GenAI. ينشئ llm_generate_span امتدادًا يحتوي على حقول gen_ai.usage.* مُعلنة مسبقًا، ويملؤها record_llm_usage بعد وصول الاستجابة:
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()
});
يسجّل جميع مزوّدي نماذج ADK (Gemini وOpenAI وAnthropic وOllama وBedrock وDeepSeek وGroq وAzure AI وجميع المزوّدين المتوافقين مع OpenAI) استخدام الرموز تلقائيًا في كل استدعاء لـ generate_content. لا حاجة إلى إجراء رصد يدوي — إذ إن التتبّع مضمّن في طبقة المزوّد.
تتبع حقول الامتداد المسجّلة اصطلاحات OpenTelemetry GenAI:
| الحقل | الوصف |
|---|---|
gen_ai.usage.input_tokens | عدد رموز المطالبة / الإدخال |
gen_ai.usage.output_tokens | عدد رموز الإكمال / الإخراج |
gen_ai.usage.total_tokens | إجمالي عدد الرموز |
gen_ai.usage.cache_read_tokens | الرموز المقروءة من ذاكرة التخزين المؤقت للمطالبة |
gen_ai.usage.cache_creation_tokens | الرموز المستخدمة لإنشاء ذاكرة التخزين المؤقت |
gen_ai.usage.thinking_tokens | رموز استدلال سلسلة الأفكار |
gen_ai.usage.audio_input_tokens | عدد رموز إدخال الصوت |
gen_ai.usage.audio_output_tokens | عدد رموز إخراج الصوت |
لا تُسجَّل الحقول الاختيارية إلا عندما يبلّغ عنها المزوّد (وليست None).
إنشاء الامتدادات يدويًا
لأغراض القياس المخصّص، أنشئ الامتدادات يدويًا:
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
سمات الامتداد
أضف السمات ديناميكيًا:
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")?;
تشغيل مُجمّع محلي
لأغراض التطوير، شغّل Jaeger مع دعم OTLP:
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
# View traces at http://localhost:16686
تصوّر التتبعات
بعد التكوين، تظهر التتبعات في الواجهة الخلفية لقابلية المراقبة، موضحةً ما يلي:
- التسلسل الهرمي لتنفيذ الوكيل
- أزمنة استدعاء النموذج
- توقيت تنفيذ الأداة
- انتشار الأخطاء
- تدفق السياق (معرّف المستخدم، ومعرّف الجلسة، وغير ذلك)
التكامل مع 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-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(())
}
ستُصدر عمليات الوكيل والنموذج والأداة تلقائيًا سجلات وتتّبعات منظّمة.
مثال توضيحي للقياس عن بُعد
تحقّق محليًا من اختيار ميزة القياس عن بُعد:
cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp
راجع مستودع adk-playground للاطلاع على عرض توضيحي كامل للقياس عن بُعد مع استدعاءات Gemini الفعلية.
القياس عن بُعد المخصّص في الأدوات
أضف القياس عن بُعد إلى الأدوات المخصّصة:
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
الامتدادات غير المتزامنة
استخدم دائمًا #[instrument] في الدوال غير المتزامنة لضمان سياق امتداد مناسب:
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()قبل أي تسجيل - هيّئ وضع القياس عن بُعد العام المقصود قبل إنشاء الخادم
التتبعات غير المُصدَّرة
- تحقّق من إمكانية الوصول إلى نقطة نهاية OTLP
- تحقّق من أن المُجمِّع قيد التشغيل ويقبل الاتصالات
- استدعِ
shutdown_telemetry()قبل خروج التطبيق لتفريغ الامتدادات المعلّقة - تحقّق من وجود مشكلات في الشبكة/جدار الحماية
بالنسبة إلى واجهة مستخدم وقت التشغيل المضمّنة، تحقّق من services.telemetryStatus. يشير configured
إلى أن المُصدِّر مرفق، لكن الجلسة المحددة لم تُنتج امتدادًا محتفظًا به لوقت التشغيل؛
ويؤكد collecting أن المُجمِّع قد استلم البيانات.
السياق المفقود في الامتدادات
- استخدم
#[instrument]مع الدوال غير المتزامنة - تأكد من الدخول إلى الامتدادات باستخدام
let _enter = span.enter() - أبقِ حارس
_enterضمن النطاق طوال مدة العملية
أفضل الممارسات
- التهيئة مبكرًا: استدعِ
init_telemetry()في بدايةmain() - استخدم الحقول المهيكلة: أضف السياق باستخدام أزواج مفتاح-قيمة، وليس باستيفاء السلاسل النصية
- سجّل الدوال غير المتزامنة: استخدم دائمًا
#[instrument]مع الدوال غير المتزامنة - التفريغ عند الخروج: استدعِ
shutdown_telemetry()قبل إنهاء التطبيق - مستويات السجل المناسبة: استخدم
infoللأحداث المهمة، وdebugللتفاصيل - تجنّب البيانات الحساسة: تخطَّ المعلمات الحساسة باستخدام
#[instrument(skip(...))] - التسمية المتسقة: استخدم أسماء حقول متسقة (مثل
user.idوsession.id)
ذو صلة
- القياس عن بُعد إلى Google Cloud - تصدير مباشر إلى Cloud Trace وCloud Logging JSON
- عمليات الاستدعاء - أضف القياس عن بُعد إلى عمليات الاستدعاء
- الأدوات - أضف القياس إلى الأدوات المخصصة
- النشر - إعداد القياس عن بُعد للإنتاج