agentforge_observability/
otlp.rs1use agentforge_core::Trace;
2use async_trait::async_trait;
3use opentelemetry::{
4 global,
5 trace::{Span, SpanKind, Tracer},
6 Context, KeyValue,
7};
8
9use crate::{ExporterError, TraceExporter};
10
11pub struct OtlpExporter {
18 endpoint: String,
19 service_name: String,
20}
21
22impl OtlpExporter {
23 pub fn new(endpoint: String, service_name: String) -> Self {
24 Self {
25 endpoint,
26 service_name,
27 }
28 }
29
30 pub fn from_env() -> Self {
31 Self::new(
32 std::env::var("AGENTFORGE_OTEL_ENDPOINT")
33 .unwrap_or_else(|_| "http://localhost:4318".to_string()),
34 std::env::var("AGENTFORGE_OTEL_SERVICE_NAME")
35 .unwrap_or_else(|_| "agentforge".to_string()),
36 )
37 }
38}
39
40#[async_trait]
41impl TraceExporter for OtlpExporter {
42 async fn export(&self, trace: &Trace) -> Result<(), ExporterError> {
43 let tracer = global::tracer(self.service_name.clone());
44 let cx = Context::new();
45
46 let mut span = tracer
47 .span_builder(format!("agentforge.trace.{}", trace.id))
48 .with_kind(SpanKind::Internal)
49 .start_with_context(&tracer, &cx);
50
51 span.set_attribute(KeyValue::new("trace.id", trace.id.to_string()));
52 span.set_attribute(KeyValue::new("run.id", trace.run_id.to_string()));
53 span.set_attribute(KeyValue::new("scenario.id", trace.scenario_id.to_string()));
54 span.set_attribute(KeyValue::new("status", trace.status.to_string()));
55 span.set_attribute(KeyValue::new("latency_ms", trace.latency_ms as i64));
56 span.set_attribute(KeyValue::new("llm_calls", trace.llm_calls as i64));
57 span.set_attribute(KeyValue::new("input_tokens", trace.input_tokens as i64));
58 span.set_attribute(KeyValue::new("output_tokens", trace.output_tokens as i64));
59
60 if let Some(score) = trace.aggregate_score {
61 span.set_attribute(KeyValue::new("aggregate_score", score.to_string()));
62 }
63
64 let step_count = trace.steps.len();
66 span.set_attribute(KeyValue::new("step_count", step_count as i64));
67
68 span.end();
69
70 tracing::debug!(
71 trace_id = %trace.id,
72 endpoint = %self.endpoint,
73 "Exported trace to OTLP"
74 );
75
76 Ok(())
77 }
78}