1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use chrono::{DateTime, Utc};
use core::fmt;
use futures_util::future::BoxFuture;
use opentelemetry::trace::TraceError;
use opentelemetry_sdk::export::{self, trace::ExportResult};
use std::sync::atomic;

use opentelemetry_sdk::resource::Resource;

/// An OpenTelemetry exporter that writes Spans to stdout on export.
pub struct SpanExporter {
    resource: Resource,
    is_shutdown: atomic::AtomicBool,
    resource_emitted: bool,
}

impl fmt::Debug for SpanExporter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("SpanExporter")
    }
}

impl Default for SpanExporter {
    fn default() -> Self {
        SpanExporter {
            resource: Resource::default(),
            is_shutdown: atomic::AtomicBool::new(false),
            resource_emitted: false,
        }
    }
}

impl opentelemetry_sdk::export::trace::SpanExporter for SpanExporter {
    /// Write Spans to stdout
    fn export(&mut self, batch: Vec<export::trace::SpanData>) -> BoxFuture<'static, ExportResult> {
        if self.is_shutdown.load(atomic::Ordering::SeqCst) {
            Box::pin(std::future::ready(Err(TraceError::from(
                "exporter is shut down",
            ))))
        } else {
            println!("Spans");
            if self.resource_emitted {
                print_spans(batch);
            } else {
                self.resource_emitted = true;
                println!("Resource");
                if let Some(schema_url) = self.resource.schema_url() {
                    println!("\tResource SchemaUrl: {:?}", schema_url);
                }

                self.resource.iter().for_each(|(k, v)| {
                    println!("\t ->  {}={:?}", k, v);
                });

                print_spans(batch);
            }

            Box::pin(std::future::ready(Ok(())))
        }
    }

    fn shutdown(&mut self) {
        self.is_shutdown.store(true, atomic::Ordering::SeqCst);
    }

    fn set_resource(&mut self, res: &opentelemetry_sdk::Resource) {
        self.resource = res.clone();
    }
}

fn print_spans(batch: Vec<export::trace::SpanData>) {
    for (i, span) in batch.into_iter().enumerate() {
        println!("Span #{}", i);
        println!("\tInstrumentation Scope");
        println!("\t\tName         : {:?}", &span.instrumentation_lib.name);
        if let Some(version) = &span.instrumentation_lib.version {
            println!("\t\tVersion  : {:?}", version);
        }
        if let Some(schema_url) = &span.instrumentation_lib.schema_url {
            println!("\t\tSchemaUrl: {:?}", schema_url);
        }
        span.instrumentation_lib
            .attributes
            .iter()
            .enumerate()
            .for_each(|(index, kv)| {
                if index == 0 {
                    println!("\t\tScope Attributes:");
                }
                println!("\t\t\t ->  {}: {}", kv.key, kv.value);
            });

        println!();
        println!("\tName        : {}", &span.name);
        println!("\tTraceId     : {}", &span.span_context.trace_id());
        println!("\tSpanId      : {}", &span.span_context.span_id());
        println!("\tParentSpanId: {}", &span.parent_span_id);
        println!("\tKind        : {:?}", &span.span_kind);

        let datetime: DateTime<Utc> = span.start_time.into();
        println!("\tStart time: {}", datetime.format("%Y-%m-%d %H:%M:%S%.6f"));
        let datetime: DateTime<Utc> = span.end_time.into();
        println!("\tEnd time: {}", datetime.format("%Y-%m-%d %H:%M:%S%.6f"));
        println!("\tStatus: {:?}", &span.status);

        let mut print_header = true;
        for kv in span.attributes.iter() {
            if print_header {
                println!("\tAttributes:");
                print_header = false;
            }
            println!("\t\t ->  {}: {:?}", kv.key, kv.value);
        }

        span.events.iter().enumerate().for_each(|(index, event)| {
            if index == 0 {
                println!("\tEvents:");
            }
            println!("\tEvent #{}", index);
            println!("\tName      : {}", event.name);
            let datetime: DateTime<Utc> = event.timestamp.into();
            println!("\tTimestamp : {}", datetime.format("%Y-%m-%d %H:%M:%S%.6f"));

            event.attributes.iter().enumerate().for_each(|(index, kv)| {
                if index == 0 {
                    println!("\tAttributes:");
                }
                println!("\t\t ->  {}: {:?}", kv.key, kv.value);
            });
        });

        span.links.iter().enumerate().for_each(|(index, link)| {
            if index == 0 {
                println!("\tLinks:");
            }
            println!("\tLink #{}", index);
            println!("\tTraceId: {}", link.span_context.trace_id());
            println!("\tSpanId : {}", link.span_context.span_id());

            link.attributes.iter().enumerate().for_each(|(index, kv)| {
                if index == 0 {
                    println!("\tAttributes:");
                }
                println!("\t\t ->  {}: {:?}", kv.key, kv.value);
            });
        });
    }
}