Telemetrie
ADK-Rust bietet produktionsreife Beobachtbarkeit durch das adk-telemetry Crate, welches strukturiertes Logging und verteiltes Tracing unter Verwendung des tracing Ökosystems und OpenTelemetry integriert.
Übersicht
Das Telemetriesystem ermöglicht:
- Strukturiertes Logging: Umfangreiche, abfragbare Logs mit kontextuellen Informationen
- Verteiltes Tracing: Verfolgen von Anfragen über Agent-Hierarchien und Dienstgrenzen hinweg
- OpenTelemetry Integration: Exportieren von Traces an Observability-Backends (Jaeger, Datadog, Honeycomb, etc.)
- Automatische Kontext-Weitergabe: Session, user und invocation IDs fließen durch alle Operationen
- Vorkonfigurierte Spans: Hilfsfunktionen für gängige ADK-Operationen
Schnellstart
Grundlegendes Konsolen-Logging
Für Entwicklung und einfache Bereitstellungen initialisieren Sie das Konsolen-Logging:
use adk_telemetry::init_telemetry;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialisieren Sie die Telemetrie mit Ihrem Dienstnamen
init_telemetry("my-agent-service")?;
// Ihr Agent-Code hier
Ok(())
}
Dies konfiguriert strukturiertes Logging nach stdout mit vernünftigen Standardeinstellungen.
OpenTelemetry-Export
Für Produktionsbereitstellungen mit verteiltes Tracing:
use adk_telemetry::init_with_otlp;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Mit OTLP-Exporter initialisieren
init_with_otlp("my-agent-service", "http://localhost:4317")?;
// Ihr Agent-Code hier
// Traces vor dem Beenden leeren
adk_telemetry::shutdown_telemetry();
Ok(())
}
Dies exportiert Traces und Metriken an einen OpenTelemetry-Collector-Endpunkt.
Log-Level
Steuern Sie die Ausführlichkeit des Loggings mithilfe der Umgebungsvariable RUST_LOG:
| Level | Beschreibung | Anwendungsfall |
|---|---|---|
error | Nur Fehler | Produktion (minimal) |
warn | Warnungen und Fehler | Produktion (Standard) |
info | Informative Nachrichten | Entwicklung, Staging |
debug | Detaillierte Debugging-Informationen | Lokale Entwicklung |
trace | Sehr ausführliches Tracing | Tiefes Debugging |
Log-Level einstellen
# Globales Log-Level setzen
export RUST_LOG=info
# Log-Level pro Modul setzen
export RUST_LOG=adk_agent=debug,adk_model=info
# Globale und modulspezifische Level kombinieren
export RUST_LOG=warn,adk_agent=debug
Das Telemetriesystem verwendet standardmäßig das info-Level, wenn RUST_LOG nicht gesetzt ist.
Logging-Makros
Verwenden Sie die Standard tracing Makros für das Logging:
use adk_telemetry::{trace, debug, info, warn, error};
// Informatives Logging
info!("Agent erfolgreich gestartet");
// Strukturiertes Logging mit Feldern
info!(
agent.name = "my_agent",
session.id = "sess-123",
"Benutzeranfrage wird verarbeitet"
);
// Debug-Logging
debug!(user_input = ?input, "Eingabe erhalten");
// Warn- und Fehler-Logging
warn!("Ratenbegrenzung nähert sich");
error!(error = ?err, "Fehler beim Aufruf des Modells");
Strukturierte Felder
Fügen Sie kontextuelle Felder zu Log-Nachrichten hinzu, um eine bessere Filterung und Analyse zu ermöglichen:
use adk_telemetry::info;
info!(
agent.name = "customer_support",
user.id = "user-456",
session.id = "sess-789",
invocation.id = "inv-abc",
"Agent-Ausführung gestartet"
);
Diese Felder werden in Ihrem Observability-Backend abfragbar.
Instrumentierung
Automatische Instrumentierung
Verwenden Sie das Attribut #[instrument], um Spans automatisch für Funktionen zu erstellen:
use adk_telemetry::{instrument, info};
#[instrument]
async fn process_request(user_id: &str, message: &str) {
info!("Processing request");
// Function logic here
}
// Erstellt einen Span namens "process_request" mit user_id und message als Felder
Sensible Parameter überspringen
Schließen Sie sensible Daten von Traces aus:
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
}
Benutzerdefinierte Span-Namen
use adk_telemetry::instrument;
#[instrument(name = "external_api_call")]
async fn fetch_data(url: &str) {
// Span wird "external_api_call" anstelle von "fetch_data" genannt
}
Vorkonfigurierte Spans
adk-telemetry bietet Hilfsfunktionen für gängige Operationen:
Agent-Ausführungs-Span
use adk_telemetry::agent_run_span;
let span = agent_run_span("my_agent", "inv-123");
let _enter = span.enter();
// Agent-Ausführungscode hier
// Alle Logs innerhalb dieses Scopes erben den Span-Kontext
Modellaufruf-Span
use adk_telemetry::model_call_span;
let span = model_call_span("gemini-2.0-flash");
let _enter = span.enter();
// Modell-API-Aufruf hier
Tool-Ausführungs-Span
use adk_telemetry::tool_execute_span;
let span = tool_execute_span("weather_tool");
let _enter = span.enter();
// Tool-Ausführungscode hier
Callback-Span
use adk_telemetry::callback_span;
let span = callback_span("before_model");
let _enter = span.enter();
// Callback-Logik hier
Kontextattribute hinzufügen
Fügen Sie dem aktuellen Span Benutzer- und Session-Kontext hinzu:
use adk_telemetry::add_context_attributes;
add_context_attributes("user-456", "sess-789");
Manuelle Span-Erstellung
Für eine benutzerdefinierte Instrumentierung erstellen Sie Spans manuell:
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");
// Operationscode hier
Span-Attribute
Attribute dynamisch hinzufügen:
use adk_telemetry::Span;
let span = Span::current();
span.record("result.count", 42);
span.record("result.status", "success");
OpenTelemetry-Konfiguration
OTLP-Endpunkt
Der OTLP-Exporter sendet Traces an einen Collector-Endpunkt:
use adk_telemetry::init_with_otlp;
// Lokaler Jaeger (Standard-OTLP-Port)
init_with_otlp("my-service", "http://localhost:4317")?;
// Cloud-Provider-Endpunkt
init_with_otlp("my-service", "https://otlp.example.com:4317")?;
Lokalen Collector ausführen
Für die Entwicklung führen Sie Jaeger mit OTLP-Unterstützung aus:
docker run -d --name jaeger \
-p 4317:4317 \
-p 16686:16686 \
jaegertracing/all-in-one:latest
# Traces unter http://localhost:16686 anzeigen
Trace-Visualisierung
Nach der Konfiguration erscheinen Traces in Ihrem Observability-Backend und zeigen:
- Agent-Ausführungshierarchie
- Latenzen von Modellaufrufen
- Timing der Tool-Ausführung
- Fehlerfortpflanzung
- Kontextfluss (Benutzer-ID, Session-ID usw.)
Integration mit ADK
adk-rust Komponenten senden automatisch Telemetriedaten, wenn das Telemetriesystem initialisiert wird:
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(())
}
Die Agent-, Model- und Tool-Operationen senden automatisch strukturierte Logs und Traces.
Benutzerdefinierte Telemetrie in Tools
Fügen Sie benutzerdefinierten Tools Telemetrie hinzu:
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,
);
Benutzerdefinierte Telemetrie in Callbacks
Fügen Sie Callbacks Observability hinzu:
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()?;
Überlegungen zur Performance
Sampling
Bei Systemen mit hohem Durchsatz sollten Sie Trace-Sampling in Betracht ziehen:
// Note: Sampling configuration depends on your OpenTelemetry setup
// Configure sampling in your OTLP collector or backend
Async Spans
Verwenden Sie immer #[instrument] für async-Funktionen, um den richtigen Span-Kontext sicherzustellen:
use adk_telemetry::instrument;
// ✅ Korrekt - der Span-Kontext bleibt über await-Punkte hinweg erhalten
#[instrument]
async fn async_operation() {
tokio::time::sleep(Duration::from_secs(1)).await;
}
// ❌ Falsch - ein manueller Span kann den Kontext verlieren
async fn manual_span_operation() {
let span = tracing::info_span!("operation");
let _enter = span.enter();
tokio::time::sleep(Duration::from_secs(1)).await;
// Der Kontext kann nach await verloren gehen
}
Log Level in Produktion
Verwenden Sie in der Produktion die Stufen info oder warn, um den Overhead zu reduzieren:
export RUST_LOG=warn,my_app=info
Fehlerbehebung
Keine Protokolle werden angezeigt
- Überprüfen Sie, ob die Umgebungsvariable
RUST_LOGgesetzt ist - Stellen Sie sicher, dass
init_telemetry()vor jeder Protokollierung aufgerufen wird - Überprüfen Sie, ob die telemetry nur einmal initialisiert wird (verwendet intern
Once)
Traces werden nicht exportiert
- Überprüfen Sie, ob der OTLP-Endpunkt erreichbar ist
- Überprüfen Sie, ob der collector läuft und Verbindungen akzeptiert
- Rufen Sie
shutdown_telemetry()vor dem Beenden der Anwendung auf, um ausstehende spans zu leeren - Suchen Sie nach Netzwerk-/Firewall-Problemen
Fehlender Kontext in Spans
- Verwenden Sie
#[instrument]für async Funktionen - Stellen Sie sicher, dass spans mit
let _enter = span.enter()betreten werden - Halten Sie den
_enter-Guard für die Dauer des Vorgangs im Geltungsbereich
Bewährte Methoden
- Frühzeitig initialisieren: Rufen Sie
init_telemetry()zu Beginn vonmain()auf - Strukturierte Felder verwenden: Fügen Sie Kontext mit Schlüssel-Wert-Paaren hinzu, nicht mit Zeichenketten-Interpolation
- Async-Funktionen instrumentieren: Verwenden Sie immer
#[instrument]für async Funktionen - Beim Beenden leeren: Rufen Sie
shutdown_telemetry()vor dem Beenden der Anwendung auf - Angemessene Protokollebenen: Verwenden Sie
infofür wichtige Ereignisse,debugfür Details - Vermeiden Sie sensible Daten: Überspringen Sie sensible Parameter mit
#[instrument(skip(...))] - Konsistente Benennung: Verwenden Sie konsistente Feldnamen (z. B.
user.id,session.id)
Verwandt
- Callbacks - Fügen Sie Callbacks telemetry hinzu
- Tools - Instrumentieren Sie benutzerdefinierte Tools
- Bereitstellung - telemetry-Einrichtung für die Produktion
Zurück: ← Events | Weiter: Launcher →