القياس عن بعد

ADK-Rust يوفر قابلية ملاحظة على مستوى الإنتاج من خلال adk-telemetry crate، الذي يدمج التسجيل المنظم والتتبع الموزع باستخدام tracing النظام البيئي و OpenTelemetry.

نظرة عامة

يتيح نظام القياس عن بعد ما يلي:

  • التسجيل المنظم: سجلات غنية وقابلة للاستعلام بمعلومات سياقية
  • التتبع الموزع: تتبع الطلبات عبر التسلسلات الهرمية للوكلاء وحدود الخدمة
  • تكامل OpenTelemetry: تصدير التتبعات إلى الواجهات الخلفية للمراقبة (Jaeger, Datadog, Honeycomb، إلخ.)
  • نشر السياق التلقائي: تتدفق معرفات الجلسة والمستخدم والاستدعاء عبر جميع العمليات
  • النطاقات المكونة مسبقًا: وظائف مساعدة لعمليات 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.

وحدات الماكرو للتسجيل

استخدم وحدات الماكرو 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 دوال مساعدة للعمليات الشائعة:

نطاق تنفيذ 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

نطاق استدعاء النموذج

use adk_telemetry::model_call_span;

let span = model_call_span("gemini-2.5-flash");
let _enter = span.enter();

// Model API call here

نطاق تنفيذ Tool

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-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. لا يلزم أي أجهزة يدوية — يتم تضمين التتبع في طبقة الموفر.

تتبع حقول النطاق المسجلة اصطلاحات 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).

إنشاء Span يدويًا

لأغراض القياس المخصص، قم بإنشاء spans يدويًا:

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")?;

تشغيل مجمّع محلي

للتطوير، قم بتشغيل Jaeger مع دعم OTLP:

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
  • انتشار الأخطاء
  • تدفق السياق (معرف المستخدم، معرف الجلسة، إلخ.)

التكامل مع 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 المضمّن للاطلاع على أمثلة قياس عن بعد كاملة مع استدعاءات حقيقية للنماذج.

القياس عن بعد المخصص في Tools

أضف القياس عن بعد إلى الأدوات المخصصة:

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,
);

القياس عن بعد المخصص في Callbacks

أضف قابلية المراقبة إلى 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()?;

اعتبارات الأداء

أخذ العينات

بالنسبة للأنظمة عالية الإنتاجية، ضع في اعتبارك أخذ عينات التتبع:

// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend

Async Spans

استخدم دائمًا #[instrument] على الدوال async لضمان سياق 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

استكشاف الأخطاء وإصلاحها

عدم ظهور السجلات

  1. تحقق من تعيين متغير البيئة RUST_LOG
  2. تأكد من استدعاء init_telemetry() قبل أي تسجيل
  3. تحقق من تهيئة القياس عن بعد مرة واحدة فقط (يستخدم Once داخليًا)

عدم تصدير التتبعات

  1. تحقق من إمكانية الوصول إلى نقطة نهاية OTLP
  2. تحقق من أن المجمّع يعمل ويقبل الاتصالات
  3. استدعِ shutdown_telemetry() قبل خروج التطبيق لتفريغ spans المعلقة
  4. تحقق من وجود مشكلات في الشبكة/جدار الحماية

سياق مفقود في Spans

  1. استخدم #[instrument] على الدوال async
  2. تأكد من إدخال spans باستخدام let _enter = span.enter()
  3. حافظ على حارس _enter ضمن النطاق طوال مدة العملية

أفضل الممارسات

  1. التهيئة المبكرة: استدعِ init_telemetry() في بداية main()
  2. استخدام الحقول المنظمة: أضف السياق باستخدام أزواج المفتاح-القيمة، وليس استيفاء السلسلة
  3. قياس الدوال Async: استخدم دائمًا #[instrument] على الدوال async
  4. التفريغ عند الخروج: استدعِ shutdown_telemetry() قبل إنهاء التطبيق
  5. مستويات السجل المناسبة: استخدم info للأحداث الهامة، و debug للتفاصيل
  6. تجنب البيانات الحساسة: تخطى المعلمات الحساسة باستخدام #[instrument(skip(...))]
  7. تسمية متسقة: استخدم أسماء حقول متسقة (على سبيل المثال، user.id، session.id)
  • Callbacks - أضف القياس عن بعد إلى Callbacks
  • Tools - قم بقياس الأدوات المخصصة
  • Deployment - إعداد القياس عن بعد للإنتاج

السابق: ← الأحداث | التالي: Launcher →

القياس عن بعد - وثائق ADK-Rust | ADK-Rust