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
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use core::fmt;
use opentelemetry::logs::LogResult;
use opentelemetry_sdk::export::logs::LogBatch;
use opentelemetry_sdk::Resource;
use std::sync::atomic;

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

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

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

#[async_trait]
impl opentelemetry_sdk::export::logs::LogExporter for LogExporter {
    /// Export spans to stdout
    async fn export(&mut self, batch: LogBatch<'_>) -> LogResult<()> {
        if self.is_shutdown.load(atomic::Ordering::SeqCst) {
            return Err("exporter is shut down".into());
        } else {
            println!("Logs");
            if self.resource_emitted {
                print_logs(batch);
            } else {
                self.resource_emitted = true;
                println!("Resource");
                if let Some(schema_url) = self.resource.schema_url() {
                    println!("\t Resource SchemaUrl: {:?}", schema_url);
                }
                self.resource.iter().for_each(|(k, v)| {
                    println!("\t ->  {}={:?}", k, v);
                });

                print_logs(batch);
            }

            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_logs(batch: LogBatch<'_>) {
    for (i, log) in batch.iter().enumerate() {
        println!("Log #{}", i);
        let (record, _library) = log;
        if let Some(event_name) = record.event_name {
            println!("\t EventName: {:?}", event_name);
        }
        if let Some(target) = &record.target {
            println!("\t Target (Scope): {:?}", target);
        }
        if let Some(trace_context) = &record.trace_context {
            println!("\t TraceId: {:?}", trace_context.trace_id);
            println!("\t SpanId: {:?}", trace_context.span_id);
        }
        if let Some(timestamp) = record.timestamp {
            let datetime: DateTime<Utc> = timestamp.into();
            println!("\t Timestamp: {}", datetime.format("%Y-%m-%d %H:%M:%S%.6f"));
        }
        if let Some(timestamp) = record.observed_timestamp {
            let datetime: DateTime<Utc> = timestamp.into();
            println!(
                "\t Observed Timestamp: {}",
                datetime.format("%Y-%m-%d %H:%M:%S%.6f")
            );
        }
        if let Some(severity) = record.severity_text {
            println!("\t SeverityText: {:?}", severity);
        }
        if let Some(severity) = record.severity_number {
            println!("\t SeverityNumber: {:?}", severity);
        }
        if let Some(body) = &record.body {
            println!("\t Body: {:?}", body);
        }

        println!("\t Attributes:");
        for (k, v) in record.attributes_iter() {
            println!("\t\t ->  {}: {:?}", k, v);
        }
    }
}