1#![feature(
5 nonpoison_mutex,
6 sync_nonpoison,
7 anonymous_lifetime_in_impl_trait,
8 type_changing_struct_update,
9 never_type
10)]
11
12mod builder;
14mod file;
15mod pre_init;
16mod term;
17
18pub use self::builder::LoggerBuilder;
20
21use {
23 self::file::FileWriter,
24 itertools::Itertools,
25 std::{
26 collections::{HashMap, hash_map},
27 env::{self, VarError},
28 fs,
29 io,
30 path::Path,
31 },
32 tracing_subscriber::Registry,
33};
34
35pub struct Logger {
37 file_writer: FileWriter,
39}
40
41impl Logger {
42 pub fn new() -> Self {
44 Self::builder().build()
45 }
46
47 pub fn builder() -> LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
49 LoggerBuilder::new()
50 }
51
52 pub fn set_file(&self, path: Option<&Path>) {
57 match path {
58 Some(path) => match fs::File::create(path) {
59 Ok(file) => {
60 self.file_writer.set_file(file);
61 tracing::info!("Logging to file: {path:?}");
62 },
63 Err(err) => {
64 tracing::warn!("Unable to create log file {path:?}: {err}");
65 self.file_writer.set_empty()
66 },
67 },
68 None => self.file_writer.set_empty(),
69 }
70 }
71}
72
73impl Default for Logger {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79pub type LoggerSubscriber = Registry;
82
83#[must_use]
87fn get_env_filters(env: &str, default_filters: impl IntoIterator<Item = (Option<&'_ str>, &'_ str)>) -> String {
88 let env_var;
90 let mut cur_filters = match env::var(env) {
91 Ok(var) => {
93 env_var = var;
94 env_var
95 .split(',')
96 .map(|s| match s.split_once('=') {
97 Some((src, level)) => (Some(src), level),
98 None => (None, s),
99 })
100 .collect::<HashMap<_, _>>()
101 },
102
103 Err(err) => {
105 if let VarError::NotUnicode(var) = err {
106 tracing::warn!("Ignoring non-utf8 env variable {env:?}: {var:?}");
107 }
108
109 HashMap::new()
110 },
111 };
112
113 for (src, level) in default_filters {
115 if let hash_map::Entry::Vacant(entry) = cur_filters.entry(src) {
116 let _ = entry.insert(level);
117 }
118 }
119
120 let var = cur_filters
122 .into_iter()
123 .map(|(src, level)| match src {
124 Some(src) => format!("{src}={level}"),
125 None => level.to_owned(),
126 })
127 .join(",");
128 tracing::trace!("Using {env}={var}");
129
130 var
131}