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
use tracing::Level;
use std::io::{stdout, Write};
use tracing_subscriber::{self, EnvFilter};

use crate::writers::{FileWriter, CompositeWriter};

pub struct TraceListenerOptions {
    level: Level,
    is_stdout: bool,
    file_path: String,
    filter: EnvFilter,
}

impl Default for TraceListenerOptions {
    fn default() -> Self {
        return TraceListenerOptions {
            level: Level::TRACE,
            is_stdout: true,
            file_path: String::new(),
            filter: EnvFilter::default(),
        };
    }
}

impl TraceListenerOptions {
    pub fn new() -> Self {
        return TraceListenerOptions::default();
    }

    pub fn with_stdout(self, is_stdout: bool) -> Self {
        return TraceListenerOptions { is_stdout, ..self };
    }

    pub fn with_level(self, level: Level) -> Self {
        return TraceListenerOptions { level, ..self };
    }

    pub fn with_file_path<S: AsRef<str> + ToString>(self, file_path: S) -> Self {
        let file_path = file_path.to_string();

        return TraceListenerOptions { file_path, ..self };
    }

    pub fn with_env_filter(self, filter: impl Into<EnvFilter>) -> Self {
        let filter = filter.into();

        return TraceListenerOptions { filter, ..self };
    }
}

pub fn create_trace_listener(
    options: TraceListenerOptions,
) {
    // get the file path and stdout options
    let TraceListenerOptions {
        file_path,
        is_stdout,
        filter,
        ..
    } = options;

    let _collector = tracing_subscriber::fmt()
        // make sure the tracing level filtering is respected
        .with_max_level(options.level)
        // otherwise the log line will always contain trace::trace
        // since we wrap the tracing functionality and the macros
        // are called from within the trace create
        .with_target(false)
        // setup writers based on options
        .with_writer(move || {
            // create writer that writes to multiple writers under the hood
            let mut writers: Vec<Box<dyn Write>> = vec![];

            // if file path is set, use the file writer
            if file_path.len() > 0 {
                writers.push(FileWriter::new(&file_path));
            }

            // is stdout set, write to console
            if is_stdout {
                writers.push(Box::new(stdout()));
            }

            let composite_writer = CompositeWriter::new(writers);

            // composite_writer
            //     .write_all("\n\n".as_bytes())
            //     .expect("Cannot write to composite writer.");

            // composite_writer.flush()
            //     .expect("Cannot flush composite writer.");

            return composite_writer;
        })
        .with_env_filter(filter)
        .init();
}