use tracing_subscriber::EnvFilter;
#[derive(Debug, Clone)]
pub struct TracingOpts {
pub default_directive: String,
pub json: bool,
pub stderr: bool,
pub with_target: bool,
}
impl Default for TracingOpts {
fn default() -> Self {
Self {
default_directive: "info".to_string(),
json: false,
stderr: false,
with_target: false,
}
}
}
#[must_use]
pub fn build_filter(default_directive: &str) -> EnvFilter {
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(default_directive))
}
pub fn init(opts: TracingOpts) {
let filter = build_filter(&opts.default_directive);
let fmt = tracing_subscriber::fmt().with_env_filter(filter);
match (opts.json, opts.stderr, opts.with_target) {
(true, true, true) => {
let _ = fmt
.json()
.with_writer(std::io::stderr)
.with_target(true)
.try_init();
}
(true, true, false) => {
let _ = fmt
.json()
.with_writer(std::io::stderr)
.with_target(false)
.try_init();
}
(true, false, true) => {
let _ = fmt.json().with_target(true).try_init();
}
(true, false, false) => {
let _ = fmt.json().with_target(false).try_init();
}
(false, true, true) => {
let _ = fmt
.with_writer(std::io::stderr)
.with_target(true)
.try_init();
}
(false, true, false) => {
let _ = fmt
.with_writer(std::io::stderr)
.with_target(false)
.try_init();
}
(false, false, true) => {
let _ = fmt.with_target(true).try_init();
}
(false, false, false) => {
let _ = fmt.with_target(false).try_init();
}
}
}
pub fn init_default() {
init(TracingOpts::default());
}
pub fn init_with(default_directive: impl Into<String>) {
init(TracingOpts {
default_directive: default_directive.into(),
..Default::default()
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_opts_match_expected_shape() {
let o = TracingOpts::default();
assert_eq!(o.default_directive, "info");
assert!(!o.json);
assert!(!o.stderr);
assert!(!o.with_target);
}
#[test]
fn build_filter_directive_resolution() {
use std::sync::Mutex;
static ENV_LOCK: Mutex<()> = Mutex::new(());
let _g = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let prev = std::env::var("RUST_LOG").ok();
unsafe {
std::env::remove_var("RUST_LOG");
}
let filter = build_filter("warn");
let s = format!("{filter}");
assert!(s.contains("warn"), "fallback filter: {s}");
unsafe {
std::env::set_var("RUST_LOG", "myapp=trace");
}
let filter = build_filter("info");
let s = format!("{filter}");
assert!(s.contains("myapp"), "rust_log filter: {s}");
unsafe {
match prev {
Some(p) => std::env::set_var("RUST_LOG", p),
None => std::env::remove_var("RUST_LOG"),
}
}
}
#[test]
fn opts_clone_independent() {
let a = TracingOpts {
default_directive: "debug".into(),
json: true,
..Default::default()
};
let b = a.clone();
assert_eq!(a.default_directive, b.default_directive);
assert_eq!(a.json, b.json);
}
}