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
use tracing::subscriber::set_global_default;
use tracing::{Level, Subscriber};
use tracing_log::LogTracer;
use tracing_subscriber::Layer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};

use ansi_term::Colour::{Blue, Cyan, Purple, Red, Yellow};

/// The AnsiVisitor
pub struct AnsiVisitor;

impl tracing::field::Visit for AnsiVisitor {
    fn record_f64(&mut self, _: &tracing::field::Field, value: f64) {
        println!("{}", value)
    }

    fn record_i64(&mut self, _: &tracing::field::Field, value: i64) {
        println!("{}", value)
    }

    fn record_u64(&mut self, _: &tracing::field::Field, value: u64) {
        println!("{}", value)
    }

    fn record_bool(&mut self, _: &tracing::field::Field, value: bool) {
        println!("{}", value)
    }

    fn record_str(&mut self, _: &tracing::field::Field, value: &str) {
        println!("{}", value)
    }

    fn record_error(
        &mut self,
        _: &tracing::field::Field,
        value: &(dyn std::error::Error + 'static),
    ) {
        println!("{}", value)
    }

    fn record_debug(&mut self, _: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        println!("{:?}", value)
    }
}

/// An Ansi Term layer for tracing
pub struct AsniTermLayer;

impl<S> Layer<S> for AsniTermLayer
where
    S: tracing::Subscriber,
{
    fn on_event(
        &self,
        event: &tracing::Event<'_>,
        _ctx: tracing_subscriber::layer::Context<'_, S>,
    ) {
        // Print the timestamp
        let utc: chrono::DateTime<chrono::Utc> = chrono::Utc::now();
        print!("[{}] ", Cyan.paint(utc.to_rfc2822()));

        // Print the level prefix
        match *event.metadata().level() {
            Level::ERROR => {
                eprint!("{}: ", Red.paint("ERROR"));
            }
            Level::WARN => {
                print!("{}: ", Yellow.paint("WARN"));
            }
            Level::INFO => {
                print!("{}: ", Blue.paint("INFO"));
            }
            Level::DEBUG => {
                print!("DEBUG: ");
            }
            Level::TRACE => {
                print!("{}: ", Purple.paint("TRACE"));
            }
        }

        print!("{} ", Purple.paint(event.metadata().target()));
        print!(
            "at {} ",
            Cyan.paint(
                event
                    .metadata()
                    .name()
                    .split(' ')
                    .last()
                    .unwrap_or_default()
            )
        );
        let mut visitor = AnsiVisitor;
        event.record(&mut visitor);
    }
}

/// Subscriber Composer
///
/// Builds a subscriber with multiple layers into a [tracing](https://crates.io/crates/tracing) subscriber.
pub fn get_subscriber(env_filter: String) -> impl Subscriber + Sync + Send {
    let env_filter =
        EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter));
    let formatting_layer = AsniTermLayer;
    Registry::default().with(env_filter).with(formatting_layer)
}

/// Globally registers a subscriber.
///
/// ### Panics
///
/// If it is called more than once.
pub fn init_subscriber(subscriber: impl Subscriber + Send + Sync) {
    LogTracer::init().expect("Failed to set logger");
    set_global_default(subscriber).expect("Failed to set subscriber");
}