webview_bundle_cli/
logging.rs

1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3use tracing::subscriber::Interest;
4use tracing::Metadata;
5use tracing_subscriber::filter::LevelFilter;
6use tracing_subscriber::layer::{Context, Filter, SubscriberExt};
7use tracing_subscriber::util::SubscriberInitExt;
8use tracing_subscriber::{registry, Layer};
9
10pub fn setup_logging(level: LoggingLevel, kind: LoggingKind) {
11  if level == LoggingLevel::None {
12    return;
13  }
14  let format = tracing_subscriber::fmt::layer()
15    .with_level(true)
16    .with_target(false)
17    .with_thread_names(true)
18    .with_file(true)
19    .with_ansi(true);
20  match kind {
21    LoggingKind::Pretty => {
22      let format = format.pretty();
23      registry()
24        .with(format.with_filter(LoggingFilter { level }))
25        .init()
26    }
27    LoggingKind::Compact => {
28      let format = format.compact();
29      registry()
30        .with(format.with_filter(LoggingFilter { level }))
31        .init()
32    }
33    LoggingKind::Json => {
34      let format = format.json().flatten_event(true);
35
36      registry()
37        .with(format.with_filter(LoggingFilter { level }))
38        .init()
39    }
40  };
41}
42
43#[derive(Copy, Debug, Default, Clone, Ord, PartialOrd, Eq, PartialEq)]
44pub enum LoggingLevel {
45  /// No logs should be shown
46  #[default]
47  None,
48  Debug,
49  Info,
50  Warn,
51  Error,
52}
53
54impl LoggingLevel {
55  fn to_filter_level(self) -> Option<LevelFilter> {
56    match self {
57      LoggingLevel::None => None,
58      LoggingLevel::Info => Some(LevelFilter::INFO),
59      LoggingLevel::Warn => Some(LevelFilter::WARN),
60      LoggingLevel::Error => Some(LevelFilter::ERROR),
61      LoggingLevel::Debug => Some(LevelFilter::DEBUG),
62    }
63  }
64}
65
66impl FromStr for LoggingLevel {
67  type Err = String;
68  fn from_str(s: &str) -> Result<Self, Self::Err> {
69    match s {
70      "none" => Ok(Self::None),
71      "info" => Ok(Self::Info),
72      "warn" => Ok(Self::Warn),
73      "error" => Ok(Self::Error),
74      "debug" => Ok(Self::Debug),
75      _ => Err("Unexpected value".to_string()),
76    }
77  }
78}
79
80impl Display for LoggingLevel {
81  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
82    match self {
83      LoggingLevel::None => write!(f, "none"),
84      LoggingLevel::Debug => write!(f, "debug"),
85      LoggingLevel::Info => write!(f, "info"),
86      LoggingLevel::Warn => write!(f, "warn"),
87      LoggingLevel::Error => write!(f, "error"),
88    }
89  }
90}
91
92/// Tracing filter enabling:
93/// - All spans and events at level info or higher
94/// - All spans and events at level debug in crates whose name starts with `webview-bundle`
95struct LoggingFilter {
96  level: LoggingLevel,
97}
98
99/// Tracing filter used for spans emitted by `webview-bundle*` crates
100const SELF_FILTER: LevelFilter = if cfg!(debug_assertions) {
101  LevelFilter::TRACE
102} else {
103  LevelFilter::DEBUG
104};
105
106impl LoggingFilter {
107  fn check_enabled(&self, meta: &Metadata<'_>) -> bool {
108    let filter = if meta.target().starts_with("webview_bundle") {
109      if let Some(level) = self.level.to_filter_level() {
110        level
111      } else {
112        return false;
113      }
114    } else {
115      LevelFilter::INFO
116    };
117    meta.level() <= &filter
118  }
119}
120
121impl<S> Filter<S> for LoggingFilter {
122  fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool {
123    self.check_enabled(meta)
124  }
125
126  fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
127    if self.check_enabled(meta) {
128      Interest::always()
129    } else {
130      Interest::never()
131    }
132  }
133
134  fn max_level_hint(&self) -> Option<LevelFilter> {
135    Some(SELF_FILTER)
136  }
137}
138
139#[derive(Copy, Debug, Default, Clone, Eq, PartialEq)]
140pub enum LoggingKind {
141  /// A pretty log on multiple lines with nice colours
142  #[default]
143  Pretty,
144  /// A more cluttered logging
145  Compact,
146  /// Logs are emitted in JSON format
147  Json,
148}
149
150impl Display for LoggingKind {
151  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
152    match self {
153      LoggingKind::Pretty => write!(f, "pretty"),
154      LoggingKind::Compact => write!(f, "compact"),
155      LoggingKind::Json => write!(f, "json"),
156    }
157  }
158}
159
160impl FromStr for LoggingKind {
161  type Err = String;
162
163  fn from_str(s: &str) -> Result<Self, Self::Err> {
164    match s {
165      "compact" => Ok(Self::Compact),
166      "pretty" => Ok(Self::Pretty),
167      "json" => Ok(Self::Json),
168      _ => Err("Unexpected value".to_string()),
169    }
170  }
171}