use opentelemetry::trace::{TraceContextExt, TracerProvider as _};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_stdout as stdout;
use std::sync::{Arc, Mutex, OnceLock};
use tracing::{debug, info, span, warn, Subscriber};
use tracing::{dispatcher::WeakDispatch, level_filters::LevelFilter, Dispatch};
use tracing_opentelemetry::{get_otel_context, layer};
use tracing_subscriber::layer::Context;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
#[derive(Clone, Default)]
struct SpanAnalysisLayer {
analysis_results: Arc<Mutex<Vec<SpanAnalysis>>>,
dispatch: Arc<OnceLock<WeakDispatch>>,
}
#[derive(Debug, Clone)]
struct SpanAnalysis {
span_name: String,
trace_id: String,
span_id: String,
is_sampled: bool,
}
impl SpanAnalysisLayer {
fn get_analysis_results(&self) -> Vec<SpanAnalysis> {
self.analysis_results.lock().unwrap().clone()
}
fn analyze_span_context(&self, span_name: &str, otel_context: &opentelemetry::Context) {
let span = otel_context.span();
let span_context = span.span_context();
if span_context.is_valid() {
let analysis = SpanAnalysis {
span_name: span_name.to_string(),
trace_id: format!("{:032x}", span_context.trace_id()),
span_id: format!("{:016x}", span_context.span_id()),
is_sampled: span_context.is_sampled(),
};
println!(
"🔍 Analyzing span '{}': trace_id={}, span_id={}, sampled={}",
analysis.span_name,
analysis.trace_id,
analysis.span_id,
span_context.trace_flags().is_sampled()
);
if let Ok(mut results) = self.analysis_results.lock() {
results.push(analysis);
}
}
}
}
impl<S> Layer<S> for SpanAnalysisLayer
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
fn on_register_dispatch(&self, subscriber: &Dispatch) {
let _ = self.dispatch.set(subscriber.downgrade());
}
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
id: &tracing::span::Id,
ctx: Context<'_, S>,
) {
let Some(weak_dispatch) = self.dispatch.get() else {
return;
};
if let Some(span_ref) = ctx.span(id) {
let mut extensions = span_ref.extensions_mut();
if let Some(dispatch) = weak_dispatch.upgrade() {
if let Some(otel_context) = get_otel_context(&mut extensions, &dispatch) {
self.analyze_span_context(attrs.metadata().name(), &otel_context);
} else {
println!(
"⚠️ Could not extract OpenTelemetry context for span '{}'",
attrs.metadata().name()
);
}
}
}
}
fn on_enter(&self, id: &tracing::span::Id, ctx: Context<'_, S>) {
if let Some(weak_dispatch) = self.dispatch.get() {
if let Some(span_ref) = ctx.span(id) {
let mut extensions = span_ref.extensions_mut();
if let Some(dispatch) = weak_dispatch.upgrade() {
if let Some(otel_context) = get_otel_context(&mut extensions, &dispatch) {
let span = otel_context.span();
let span_context = span.span_context();
if span_context.is_valid() {
println!(
"📍 Entering span with trace_id: {:032x}, span_id: {:016x}",
span_context.trace_id(),
span_context.span_id()
);
}
}
}
}
}
}
}
fn setup_tracing() -> (impl Subscriber, SdkTracerProvider, SpanAnalysisLayer) {
let provider = SdkTracerProvider::builder()
.with_simple_exporter(stdout::SpanExporter::default())
.build();
let tracer = provider.tracer("span_ref_ext_example");
let analysis_layer = SpanAnalysisLayer::default();
let subscriber = tracing_subscriber::registry()
.with(layer().with_tracer(tracer).with_filter(LevelFilter::DEBUG))
.with(analysis_layer.clone())
.with(
tracing_subscriber::fmt::layer()
.with_target(false)
.with_filter(LevelFilter::INFO),
);
(subscriber, provider, analysis_layer)
}
fn simulate_application_work() {
let root_span = span!(tracing::Level::INFO, "application_main", version = "1.0.0");
let _root_guard = root_span.enter();
info!("Starting application");
{
let auth_span = span!(tracing::Level::DEBUG, "authenticate_user", user_id = 12345);
let _auth_guard = auth_span.enter();
debug!("Validating user credentials");
std::thread::sleep(std::time::Duration::from_millis(10));
info!("User authenticated successfully");
}
{
let db_span = span!(
tracing::Level::DEBUG,
"database_query",
query = "SELECT * FROM users",
table = "users"
);
let _db_guard = db_span.enter();
debug!("Executing database query");
{
let conn_span = span!(tracing::Level::DEBUG, "acquire_connection", pool_size = 10);
let _conn_guard = conn_span.enter();
debug!("Acquiring database connection from pool");
std::thread::sleep(std::time::Duration::from_millis(5));
}
std::thread::sleep(std::time::Duration::from_millis(20));
info!("Database query completed");
}
{
let process_span = span!(
tracing::Level::DEBUG,
"process_data",
records_count = 150,
batch_size = 50
);
let _process_guard = process_span.enter();
debug!("Processing user data");
for batch in 1..=3 {
let batch_span = span!(
tracing::Level::DEBUG,
"process_batch",
batch_number = batch,
batch_size = 50
);
let _batch_guard = batch_span.enter();
debug!("Processing batch {}", batch);
std::thread::sleep(std::time::Duration::from_millis(8));
}
info!("Data processing completed");
}
warn!("Application work completed");
}
fn main() {
println!(
"🚀 OpenTelemetryContext Example: Extracting OpenTelemetry Contexts from Separate Layer"
);
println!("{}", "=".repeat(80));
let (subscriber, provider, analysis_layer) = setup_tracing();
tracing::subscriber::with_default(subscriber, || {
simulate_application_work();
});
drop(provider);
println!("\n📊 Span Analysis Results:");
println!("{}", "-".repeat(80));
let results = analysis_layer.get_analysis_results();
for (i, analysis) in results.iter().enumerate() {
println!(
"{}. Span: '{}'\n Trace ID: {}\n Span ID: {}\n Sampled: {}\n",
i + 1,
analysis.span_name,
analysis.trace_id,
analysis.span_id,
analysis.is_sampled
);
}
println!(
"✅ Example completed! Total spans analyzed: {}",
results.len()
);
}