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

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

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

impl Default for TraceListenerOptions {
    fn default() -> Self {
        return TraceListenerOptions {
            level: Level::TRACE,
            is_stdout: true,
            cleanup_file: false,
            include_thread_ids: false,
            include_thread_names: false,
            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, cleanup_file: bool) -> Self {
        let file_path = file_path.to_string();

        return TraceListenerOptions { file_path, cleanup_file: cleanup_file, ..self };
    }

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

    pub fn with_thread_ids(self, include_thread_ids: bool) -> Self {
        return TraceListenerOptions { include_thread_ids, ..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,
) -> Box<dyn Subscriber + Send + Sync> {
    // get the file path and stdout options
    let TraceListenerOptions {
        file_path,
        is_stdout,
        cleanup_file,
        include_thread_names,
        include_thread_ids,
        filter,
        ..
    } = options;

    let file_writer = FileWriter::new(&file_path, cleanup_file);
    let stdout_writer: Box<dyn Write + Send + Sync> = Box::new(stdout());
    let mut writers: Vec<Box<dyn Write + Send + Sync>> = vec![];

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

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

    let make_composite_writer = MakeCompositeWriter::new(writers);
    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)
        // include thread name
        .with_thread_ids(include_thread_ids)
        .with_thread_names(include_thread_names)
        // setup writers based on options
        .with_writer(make_composite_writer)
        .with_env_filter(filter)
        .finish();

    return Box::new(collector);
}

// pub use spin;

// static START: spin::Once<()> = spin::Once::new();

// START.call_once(|| {
//     // run initialization here
// });