logforth_core/logger/builder.rs
1// Copyright 2024 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::Append;
16use crate::Diagnostic;
17use crate::Filter;
18use crate::Logger;
19use crate::logger::log_impl::Dispatch;
20use crate::logger::log_impl::set_default_logger;
21
22/// Create a new empty [`LoggerBuilder`] instance for configuring log dispatching.
23///
24/// # Examples
25///
26/// ```
27/// use logforth_core::append;
28///
29/// let builder = logforth_core::builder()
30/// .dispatch(|d| d.append(append::Stderr::default()))
31/// .apply();
32/// ```
33pub fn builder() -> LoggerBuilder {
34 LoggerBuilder { dispatches: vec![] }
35}
36
37/// A builder for configuring log dispatching and setting up the global logger.
38///
39/// # Examples
40///
41/// ```
42/// use logforth_core::append;
43///
44/// logforth_core::builder()
45/// .dispatch(|d| d.append(append::Stdout::default()))
46/// .apply();
47/// ```
48#[must_use = "call `apply` to set the global logger or `build` to construct a logger instance"]
49#[derive(Debug)]
50pub struct LoggerBuilder {
51 // stashed dispatches
52 dispatches: Vec<Dispatch>,
53}
54
55impl LoggerBuilder {
56 /// Register a new dispatch with the [`LoggerBuilder`].
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// use logforth_core::append;
62 ///
63 /// logforth_core::builder()
64 /// .dispatch(|d| d.append(append::Stderr::default()))
65 /// .apply();
66 /// ```
67 pub fn dispatch<F>(mut self, f: F) -> Self
68 where
69 F: FnOnce(DispatchBuilder<false>) -> DispatchBuilder<true>,
70 {
71 self.dispatches.push(f(DispatchBuilder::new()).build());
72 self
73 }
74
75 /// Build the [`Logger`].
76 ///
77 /// # Examples
78 ///
79 /// ```
80 /// use logforth_core::record::Record;
81 /// use logforth_core::record::RecordBuilder;
82 ///
83 /// let l = logforth_core::builder().build();
84 /// let r = RecordBuilder::default().payload("hello world!").build();
85 /// l.log(&r);
86 /// ```
87 pub fn build(self) -> Logger {
88 Logger::new(self.dispatches)
89 }
90
91 /// Set up the global logger with all the configured dispatches.
92 ///
93 /// This should be called early in the execution of a Rust program. Any log events that occur
94 /// before initialization will be ignored.
95 ///
96 /// # Errors
97 ///
98 /// Return an error if a global logger has already been set.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// if logforth_core::builder().try_apply().is_err() {
104 /// eprintln!("failed to set logger");
105 /// }
106 /// ```
107 pub fn try_apply(self) -> Result<(), Logger> {
108 set_default_logger(self.build())
109 }
110
111 /// Set up the global logger with all the configured dispatches.
112 ///
113 /// This should be called early in the execution of a Rust program. Any log events that occur
114 /// before initialization will be ignored.
115 ///
116 /// This function will panic if it is called more than once, or if another library has already
117 /// initialized a global logger.
118 ///
119 /// # Panics
120 ///
121 /// Panic if the global logger has already been set.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// logforth_core::builder().apply();
127 /// ```
128 pub fn apply(self) {
129 self.try_apply()
130 .expect("LoggerBuilder::apply must be called before the global logger initialized");
131 }
132}
133
134/// A builder for configuring a log dispatch, including filters and appenders.
135///
136/// # Examples
137///
138/// ```
139/// use logforth_core::append;
140/// use logforth_core::record::LevelFilter;
141///
142/// logforth_core::builder()
143/// .dispatch(|d| {
144/// d.filter(LevelFilter::Info)
145/// .append(append::Stdout::default())
146/// })
147/// .apply();
148/// ```
149#[derive(Debug)]
150pub struct DispatchBuilder<const APPEND: bool> {
151 filters: Vec<Box<dyn Filter>>,
152 diagnostics: Vec<Box<dyn Diagnostic>>,
153 appends: Vec<Box<dyn Append>>,
154}
155
156impl DispatchBuilder<false> {
157 fn new() -> Self {
158 DispatchBuilder {
159 filters: vec![],
160 diagnostics: vec![],
161 appends: vec![],
162 }
163 }
164
165 /// Add a filter to this dispatch.
166 ///
167 /// # Examples
168 ///
169 /// ```
170 /// use logforth_core::append;
171 /// use logforth_core::record::LevelFilter;
172 ///
173 /// logforth_core::builder()
174 /// .dispatch(|d| {
175 /// d.filter(LevelFilter::Error)
176 /// .append(append::Stderr::default())
177 /// })
178 /// .apply();
179 /// ```
180 pub fn filter(mut self, filter: impl Into<Box<dyn Filter>>) -> Self {
181 self.filters.push(filter.into());
182 self
183 }
184
185 /// Add a diagnostic to this dispatch.
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// use logforth_core::append;
191 /// use logforth_core::diagnostic;
192 /// use logforth_core::record::LevelFilter;
193 ///
194 /// logforth_core::builder()
195 /// .dispatch(|d| {
196 /// d.filter(LevelFilter::Error)
197 /// .diagnostic(diagnostic::ThreadLocalDiagnostic::default())
198 /// .append(append::Stderr::default())
199 /// })
200 /// .apply();
201 /// ```
202 pub fn diagnostic(mut self, diagnostic: impl Into<Box<dyn Diagnostic>>) -> Self {
203 self.diagnostics.push(diagnostic.into());
204 self
205 }
206}
207
208impl DispatchBuilder<true> {
209 fn build(self) -> Dispatch {
210 Dispatch::new(self.filters, self.diagnostics, self.appends)
211 }
212}
213
214impl<const APPEND: bool> DispatchBuilder<APPEND> {
215 /// Add an appender to this dispatch.
216 ///
217 /// # Examples
218 ///
219 /// ```
220 /// use logforth_core::append;
221 ///
222 /// logforth_core::builder()
223 /// .dispatch(|d| d.append(append::Stdout::default()))
224 /// .apply();
225 /// ```
226 pub fn append(mut self, append: impl Into<Box<dyn Append>>) -> DispatchBuilder<true> {
227 self.appends.push(append.into());
228 DispatchBuilder {
229 filters: self.filters,
230 diagnostics: self.diagnostics,
231 appends: self.appends,
232 }
233 }
234}