opentelemetry_stdout/trace/
exporter.rs

1use chrono::{DateTime, Utc};
2use core::fmt;
3use opentelemetry_sdk::error::{OTelSdkError, OTelSdkResult};
4use opentelemetry_sdk::trace::SpanData;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use opentelemetry_sdk::resource::Resource;
8
9/// An OpenTelemetry exporter that writes Spans to stdout on export.
10pub struct SpanExporter {
11    resource: Resource,
12    is_shutdown: AtomicBool,
13    resource_emitted: AtomicBool,
14}
15
16impl fmt::Debug for SpanExporter {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        f.write_str("SpanExporter")
19    }
20}
21
22impl Default for SpanExporter {
23    fn default() -> Self {
24        SpanExporter {
25            resource: Resource::builder().build(),
26            is_shutdown: AtomicBool::new(false),
27            resource_emitted: AtomicBool::new(false),
28        }
29    }
30}
31
32impl opentelemetry_sdk::trace::SpanExporter for SpanExporter {
33    /// Write Spans to stdout
34    async fn export(&self, batch: Vec<SpanData>) -> OTelSdkResult {
35        if self.is_shutdown.load(Ordering::SeqCst) {
36            Err(OTelSdkError::AlreadyShutdown)
37        } else {
38            println!("Spans");
39            if self
40                .resource_emitted
41                .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
42                .is_err()
43            {
44                print_spans(batch);
45            } else {
46                println!("Resource");
47                if let Some(schema_url) = self.resource.schema_url() {
48                    println!("\tResource SchemaUrl: {schema_url:?}");
49                }
50
51                self.resource.iter().for_each(|(k, v)| {
52                    println!("\t ->  {k}={v:?}");
53                });
54
55                print_spans(batch);
56            }
57
58            Ok(())
59        }
60    }
61
62    fn shutdown(&mut self) -> OTelSdkResult {
63        self.is_shutdown.store(true, Ordering::SeqCst);
64        Ok(())
65    }
66
67    fn set_resource(&mut self, res: &opentelemetry_sdk::Resource) {
68        self.resource = res.clone();
69    }
70}
71
72fn print_spans(batch: Vec<SpanData>) {
73    for (i, span) in batch.into_iter().enumerate() {
74        println!("Span #{i}");
75        println!("\tInstrumentation Scope");
76        println!(
77            "\t\tName         : {:?}",
78            &span.instrumentation_scope.name()
79        );
80        if let Some(version) = &span.instrumentation_scope.version() {
81            println!("\t\tVersion  : {version:?}");
82        }
83        if let Some(schema_url) = &span.instrumentation_scope.schema_url() {
84            println!("\t\tSchemaUrl: {schema_url:?}");
85        }
86        span.instrumentation_scope
87            .attributes()
88            .enumerate()
89            .for_each(|(index, kv)| {
90                if index == 0 {
91                    println!("\t\tScope Attributes:");
92                }
93                println!("\t\t\t ->  {}: {}", kv.key, kv.value);
94            });
95
96        println!();
97        println!("\tName         : {}", &span.name);
98        println!("\tTraceId      : {}", &span.span_context.trace_id());
99        println!("\tSpanId       : {}", &span.span_context.span_id());
100        println!("\tTraceFlags   : {:?}", &span.span_context.trace_flags());
101        if span.parent_span_id == opentelemetry::SpanId::INVALID {
102            println!("\tParentSpanId : None (root span)");
103        } else {
104            println!("\tParentSpanId : {}", &span.parent_span_id);
105        }
106        println!("\tKind         : {:?}", &span.span_kind);
107
108        let datetime: DateTime<Utc> = span.start_time.into();
109        println!(
110            "\tStart time   : {}",
111            datetime.format("%Y-%m-%d %H:%M:%S%.6f")
112        );
113        let datetime: DateTime<Utc> = span.end_time.into();
114        println!(
115            "\tEnd time     : {}",
116            datetime.format("%Y-%m-%d %H:%M:%S%.6f")
117        );
118        println!("\tStatus       : {:?}", &span.status);
119
120        let mut print_header = true;
121        for kv in span.attributes.iter() {
122            if print_header {
123                println!("\tAttributes:");
124                print_header = false;
125            }
126            println!("\t\t ->  {}: {:?}", kv.key, kv.value);
127        }
128
129        span.events.iter().enumerate().for_each(|(index, event)| {
130            if index == 0 {
131                println!("\tEvents:");
132            }
133            println!("\tEvent #{index}");
134            println!("\tName      : {}", event.name);
135            let datetime: DateTime<Utc> = event.timestamp.into();
136            println!("\tTimestamp : {}", datetime.format("%Y-%m-%d %H:%M:%S%.6f"));
137
138            event.attributes.iter().enumerate().for_each(|(index, kv)| {
139                if index == 0 {
140                    println!("\tAttributes:");
141                }
142                println!("\t\t ->  {}: {:?}", kv.key, kv.value);
143            });
144        });
145
146        span.links.iter().enumerate().for_each(|(index, link)| {
147            if index == 0 {
148                println!("\tLinks:");
149            }
150            println!("\tLink #{index}");
151            println!("\tTraceId: {}", link.span_context.trace_id());
152            println!("\tSpanId : {}", link.span_context.span_id());
153
154            link.attributes.iter().enumerate().for_each(|(index, kv)| {
155                if index == 0 {
156                    println!("\tAttributes:");
157                }
158                println!("\t\t ->  {}: {:?}", kv.key, kv.value);
159            });
160        });
161    }
162}