टेलीमेट्री

ADK-Rust, adk-telemetry क्रेट के माध्यम से उत्पादन-स्तरीय ऑब्ज़र्वेबिलिटी प्रदान करता है, जो 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

यदि RUST_LOG सेट नहीं है, तो कंसोल लॉगिंग का डिफ़ॉल्ट info होता है। init_with_adk_exporter द्वारा कॉन्फ़िगर किया गया रनटाइम स्पैन संग्रह इस कंसोल फ़िल्टर से स्वतंत्र है: RUST_LOG=warn सर्वर के सत्र टेलीमेट्री इंटरफ़ेस के लिए एजेंट, मॉडल, टूल और पोर्टेबल टीम स्पैन अब भी बनाए रखता है।

प्रक्रिया-अंतर्गत सत्र टेलीमेट्री

सर्वर एम्बेडेड रनटाइम UI के माध्यम से शून्य-इन्फ्रास्ट्रक्चर ट्रेस उजागर कर सकते हैं:

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-टेलीमेट्री सामान्य ऑपरेशनों के लिए सहायक फ़ंक्शन प्रदान करती है:

एजेंट निष्पादन स्पैन

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ऑडियो आउटपुट टोकन संख्या

वैकल्पिक फ़ील्ड केवल तभी रिकॉर्ड किए जाते हैं जब प्रदाता उन्हें रिपोर्ट करता है (non-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 एक्सपोर्टर traces को कलेक्टर एंडपॉइंट पर भेजता है:

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

Trace विज़ुअलाइज़ेशन

कॉन्फ़िगर किए जाने के बाद, traces आपके observability backend में निम्नलिखित दिखाते हुए दिखाई देते हैं:

  • Agent निष्पादन पदानुक्रम
  • Model कॉल विलंबताएँ
  • Tool निष्पादन समय
  • त्रुटि प्रसार
  • Context प्रवाह (user ID, session ID, आदि)

ADK के साथ एकीकरण

ADK-Rust घटक telemetry सिस्टम के आरंभ होने पर स्वचालित रूप से telemetry उत्सर्जित करते हैं:

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(())
}

Agent, model और tool ऑपरेशन स्वचालित रूप से संरचित logs और traces उत्सर्जित करेंगे।

Telemetry डेमो उदाहरण

स्थानीय रूप से telemetry सुविधा चयन को मान्य करें:

cargo check -p adk-telemetry --no-default-features
cargo check -p adk-telemetry --no-default-features --features otlp

वास्तविक Gemini कॉल के साथ पूर्ण telemetry डेमो के लिए adk-playground repo देखें।

Tools में कस्टम Telemetry

कस्टम tools में telemetry जोड़ें:

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 में कस्टम Telemetry

Callbacks में observability जोड़ें:

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

प्रदर्शन संबंधी विचार

Sampling

उच्च-throughput सिस्टम के लिए, trace sampling पर विचार करें:

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

Async Spans

उचित span context सुनिश्चित करने के लिए async functions पर हमेशा #[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
}

Production में Log Level

ओवरहेड कम करने के लिए production में info या warn level का उपयोग करें:

export RUST_LOG=warn,my_app=info

समस्या निवारण

कोई Logs दिखाई नहीं दे रहे

  1. जाँचें कि RUST_LOG environment variable सेट है
  2. सुनिश्चित करें कि किसी भी logging से पहले init_telemetry() कॉल किया गया है
  3. server बनाने से पहले इच्छित global telemetry mode को initialize करें

ट्रेसेज़ निर्यात नहीं किए गए

  1. सत्यापित करें कि OTLP एंडपॉइंट तक पहुँचा जा सकता है
  2. जाँचें कि कलेक्टर चल रहा है और कनेक्शन स्वीकार कर रहा है
  3. लंबित स्पैन को फ्लश करने के लिए एप्लिकेशन से बाहर निकलने से पहले shutdown_telemetry() को कॉल करें
  4. नेटवर्क/फ़ायरवॉल संबंधी समस्याओं की जाँच करें

एम्बेडेड रनटाइम UI के लिए, services.telemetryStatus देखें। configured का अर्थ है कि एक्सपोर्टर संलग्न है, लेकिन चयनित सेशन ने कोई रिटेन्ड रनटाइम स्पैन उत्पन्न नहीं किया है; collecting पुष्टि करता है कि कलेक्टर ने डेटा प्राप्त कर लिया है।

स्पैन में संदर्भ अनुपलब्ध

  1. async फ़ंक्शन पर #[instrument] का उपयोग करें
  2. सुनिश्चित करें कि स्पैन let _enter = span.enter() के साथ एंटर किए गए हैं
  3. ऑपरेशन की अवधि के दौरान _enter गार्ड को स्कोप में रखें

सर्वोत्तम अभ्यास

  1. जल्दी इनिशियलाइज़ करें: main() की शुरुआत में init_telemetry() को कॉल करें
  2. संरचित फ़ील्ड का उपयोग करें: स्ट्रिंग इंटरपोलेशन के बजाय की-वैल्यू पेयर के साथ संदर्भ जोड़ें
  3. Async फ़ंक्शन को इंस्ट्रुमेंट करें: async फ़ंक्शन पर हमेशा #[instrument] का उपयोग करें
  4. बाहर निकलते समय फ्लश करें: एप्लिकेशन समाप्त होने से पहले shutdown_telemetry() को कॉल करें
  5. उपयुक्त लॉग स्तर: महत्वपूर्ण घटनाओं के लिए info और विवरण के लिए debug का उपयोग करें
  6. संवेदनशील डेटा से बचें: #[instrument(skip(...))] के साथ संवेदनशील पैरामीटर को छोड़ दें
  7. सुसंगत नामकरण: सुसंगत फ़ील्ड नामों का उपयोग करें (उदाहरण के लिए, user.id, session.id)

पिछला: ← इवेंट | अगला: लॉन्चर →

टेलीमेट्री - ADK-Rust दस्तावेज़ीकरण | ADK-Rust