1use {
4 crate::{
5 Logger,
6 LoggerSubscriber,
7 file::{self, FileWriter},
8 pre_init::PreInitLogger,
9 term,
10 },
11 std::{
12 collections::HashMap,
13 io::{self, Write},
14 },
15 tracing::Subscriber,
16 tracing_subscriber::{fmt::MakeWriter, prelude::*, registry::LookupSpan},
17};
18
19pub struct LoggerBuilder<W, S> {
21 pre_init_logger: PreInitLogger,
23
24 stderr: W,
26
27 subscriber: S,
29
30 stderr_filters: HashMap<Option<String>, String>,
32
33 file_filters: HashMap<Option<String>, String>,
35}
36
37impl LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
38 pub fn new() -> Self {
40 let pre_init_logger = PreInitLogger::new();
42
43 Self {
44 pre_init_logger,
45 stderr: io::stderr,
46 subscriber: LoggerSubscriber::default(),
47 stderr_filters: [(None, "info".to_owned())].into(),
48 file_filters: [(None, "debug".to_owned())].into(),
49 }
50 }
51}
52
53impl<W, S> LoggerBuilder<W, S> {
54 pub fn stderr<W2>(self, stderr: W2) -> LoggerBuilder<W2, S> {
56 LoggerBuilder { stderr, ..self }
57 }
58
59 pub fn layer<L>(self, layer: L) -> LoggerBuilder<W, tracing_subscriber::layer::Layered<L, S>>
61 where
62 S: Subscriber,
63 L: tracing_subscriber::Layer<S>,
64 {
65 LoggerBuilder {
66 subscriber: self.subscriber.with(layer),
67 ..self
68 }
69 }
70
71 pub fn stderr_filter_default(mut self, filter: &str) -> Self {
73 self.stderr_filters.insert(None, filter.to_owned());
74 self
75 }
76
77 pub fn stderr_filter(mut self, key: &str, filter: &str) -> Self {
79 self.stderr_filters.insert(Some(key.to_owned()), filter.to_owned());
80 self
81 }
82
83 pub fn file_filter_default(mut self, filter: &str) -> Self {
85 self.file_filters.insert(None, filter.to_owned());
86 self
87 }
88
89 pub fn file_filter(mut self, key: &str, filter: &str) -> Self {
91 self.file_filters.insert(Some(key.to_owned()), filter.to_owned());
92 self
93 }
94
95 pub fn filter(self, key: &str, filter: &str) -> Self {
97 self.stderr_filter(key, filter).file_filter(key, filter)
98 }
99
100 pub fn build(self) -> Logger
102 where
103 W: for<'a> MakeWriter<'a> + Clone + Send + Sync + 'static,
104 S: Subscriber + for<'a> LookupSpan<'a> + Send + Sync + 'static,
105 {
106 let file_writer = FileWriter::memory();
108
109 let file_layer = file::layer(file_writer.clone(), self::filters_iter(&self.file_filters));
112 let term_layer = term::layer(self.stderr.clone(), self::filters_iter(&self.stderr_filters));
113 let subscriber = self.subscriber.with(file_layer).with(term_layer);
114 if let Err(err) = subscriber.try_init() {
115 eprintln!("Failed to set global logger: {err}");
116 }
117
118 self.pre_init_logger
120 .into_output()
121 .with_bytes(|bytes| {
122 self.stderr.make_writer().write_all(bytes)?;
123 file_writer.make_writer().write_all(bytes)
124 })
125 .expect("Unable to write pre-init output");
126
127 tracing::info!("Successfully initialized logger");
128
129 Logger { file_writer }
130 }
131}
132
133impl Default for LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139fn filters_iter(filters: &HashMap<Option<String>, String>) -> impl Iterator<Item = (Option<&'_ str>, &'_ str)> {
141 filters.iter().map(|(key, value)| (key.as_deref(), value.as_str()))
142}