#![cfg(feature = "otel")]
use axum::{Router, body::Bytes, extract::State, routing::post};
use faucet_core::observability::otel::{
OtelConfig, OtelProtocol, OtelSignal, build_meter_provider, build_trace_provider,
};
use opentelemetry::trace::{Tracer, TracerProvider as _};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
#[derive(Clone, Default)]
struct Hits {
traces: Arc<AtomicUsize>,
metrics: Arc<AtomicUsize>,
}
async fn traces_handler(State(h): State<Hits>, body: Bytes) {
if !body.is_empty() {
h.traces.fetch_add(1, Ordering::SeqCst);
}
}
async fn metrics_handler(State(h): State<Hits>, body: Bytes) {
if !body.is_empty() {
h.metrics.fetch_add(1, Ordering::SeqCst);
}
}
#[tokio::test(flavor = "multi_thread")]
async fn spans_and_metrics_reach_collector() {
let hits = Hits::default();
let app = Router::new()
.route("/v1/traces", post(traces_handler))
.route("/v1/metrics", post(metrics_handler))
.with_state(hits.clone());
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
let cfg = OtelConfig {
endpoint: format!("http://{addr}"),
protocol: OtelProtocol::Http,
export: vec![OtelSignal::Traces, OtelSignal::Metrics],
metric_interval_secs: 1,
..Default::default()
};
let tp = build_trace_provider(&cfg).unwrap();
let tracer = tp.tracer("test");
tracer.in_span("unit-span", |_cx| {});
let _ = tp.force_flush();
let (mp, recorder) = build_meter_provider(&cfg).unwrap();
metrics::with_local_recorder(&recorder, || {
metrics::counter!("faucet_test_counter").increment(1);
});
let _ = mp.force_flush();
for _ in 0..50 {
if hits.traces.load(Ordering::SeqCst) > 0 && hits.metrics.load(Ordering::SeqCst) > 0 {
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
assert!(
hits.traces.load(Ordering::SeqCst) > 0,
"no trace export received"
);
assert!(
hits.metrics.load(Ordering::SeqCst) > 0,
"no metric export received"
);
let _ = tp.shutdown();
let _ = mp.shutdown();
}