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
//! Configures the global default tracing subscriber

use thiserror::Error;

pub use tracing_subscriber::{util::TryInitError as LogInitError, EnvFilter as LogFilter};

/// Configures whether logs should be emitted in plaintext (the default) or as JSON-encoded
/// messages
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
pub enum LogFormat {
    /// The default plaintext format
    Plain,

    /// The JSON-encoded format
    Json,
}

/// Indicates that an invalid log format was specified
#[derive(Debug, Error)]
#[error("invalid log level: {0} must be 'plain' or 'json'")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
pub struct InvalidLogFormat(String);

// === impl LogFormat ===

impl Default for LogFormat {
    fn default() -> Self {
        Self::Plain
    }
}

impl std::str::FromStr for LogFormat {
    type Err = InvalidLogFormat;

    fn from_str(s: &str) -> Result<Self, InvalidLogFormat> {
        match s {
            "json" => Ok(LogFormat::Json),
            "plain" => Ok(LogFormat::Plain),
            s => Err(InvalidLogFormat(s.to_string())),
        }
    }
}

impl LogFormat {
    /// Attempts to configure the global default tracing subscriber in the current scope, returning
    /// an error if one is already set
    ///
    /// This method returns an error if a global default subscriber has already been set, or if a
    /// `log` logger has already been set.
    pub fn try_init(self, filter: LogFilter) -> Result<(), LogInitError> {
        use tracing_subscriber::prelude::*;

        let registry = tracing_subscriber::registry().with(filter);

        match self {
            LogFormat::Plain => registry.with(tracing_subscriber::fmt::layer()).try_init()?,

            LogFormat::Json => {
                let event_fmt = tracing_subscriber::fmt::format()
                    // Configure the formatter to output JSON logs.
                    .json()
                    // Output the current span context as a JSON list.
                    .with_span_list(true)
                    // Don't output a field for the current span, since this
                    // would duplicate information already in the span list.
                    .with_current_span(false);

                // Use the JSON event formatter and the JSON field formatter.
                let fmt = tracing_subscriber::fmt::layer()
                    .event_format(event_fmt)
                    .fmt_fields(tracing_subscriber::fmt::format::JsonFields::default());

                registry.with(fmt).try_init()?
            }
        };

        Ok(())
    }
}