Skip to main content

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;
20
21/// Create a new empty [`LoggerBuilder`] instance for configuring log dispatching.
22///
23/// # Examples
24///
25/// ```
26/// use logforth_core::append;
27///
28/// let logger = logforth_core::builder()
29///     .dispatch(|d| d.append(append::Stderr::default()))
30///     .build();
31/// ```
32pub fn builder() -> LoggerBuilder {
33    LoggerBuilder { dispatches: vec![] }
34}
35
36/// A builder for configuring log dispatching.
37///
38/// # Examples
39///
40/// ```
41/// use logforth_core::append;
42///
43/// let logger = logforth_core::builder()
44///     .dispatch(|d| d.append(append::Stdout::default()))
45///     .build();
46/// ```
47#[must_use = "call `build` to construct a logger instance"]
48#[derive(Debug)]
49pub struct LoggerBuilder {
50    // stashed dispatches
51    dispatches: Vec<Dispatch>,
52}
53
54impl LoggerBuilder {
55    /// Register a new dispatch with the [`LoggerBuilder`].
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use logforth_core::append;
61    ///
62    /// let logger = logforth_core::builder()
63    ///     .dispatch(|d| d.append(append::Stderr::default()))
64    ///     .build();
65    /// ```
66    pub fn dispatch<F>(mut self, f: F) -> Self
67    where
68        F: FnOnce(DispatchBuilder<false>) -> DispatchBuilder<true>,
69    {
70        self.dispatches.push(f(DispatchBuilder::new()).build());
71        self
72    }
73
74    /// Build the [`Logger`].
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use logforth_core::record::Record;
80    ///
81    /// let l = logforth_core::builder().build();
82    /// let r = Record::builder()
83    ///     .payload(format_args!("hello world!"))
84    ///     .build();
85    /// l.log(&r);
86    /// ```
87    pub fn build(self) -> Logger {
88        Logger::new(self.dispatches)
89    }
90}
91
92/// A builder for configuring a log dispatch, including filters and appenders.
93///
94/// # Examples
95///
96/// ```
97/// use logforth_core::append;
98/// use logforth_core::record::Level;
99/// use logforth_core::record::LevelFilter;
100///
101/// let logger = logforth_core::builder()
102///     .dispatch(|d| {
103///         d.filter(LevelFilter::MoreSevereEqual(Level::Info))
104///             .append(append::Stdout::default())
105///     })
106///     .build();
107/// ```
108#[derive(Debug)]
109pub struct DispatchBuilder<const APPEND: bool> {
110    filters: Vec<Box<dyn Filter>>,
111    diagnostics: Vec<Box<dyn Diagnostic>>,
112    appends: Vec<Box<dyn Append>>,
113}
114
115impl DispatchBuilder<false> {
116    fn new() -> Self {
117        DispatchBuilder {
118            filters: vec![],
119            diagnostics: vec![],
120            appends: vec![],
121        }
122    }
123
124    /// Add a filter to this dispatch.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use logforth_core::append;
130    /// use logforth_core::record::Level;
131    /// use logforth_core::record::LevelFilter;
132    ///
133    /// let logger = logforth_core::builder()
134    ///     .dispatch(|d| {
135    ///         d.filter(LevelFilter::MoreSevereEqual(Level::Error))
136    ///             .append(append::Stderr::default())
137    ///     })
138    ///     .build();
139    /// ```
140    pub fn filter(mut self, filter: impl Into<Box<dyn Filter>>) -> Self {
141        self.filters.push(filter.into());
142        self
143    }
144
145    /// Add a diagnostic to this dispatch.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use logforth_core::append;
151    /// use logforth_core::diagnostic;
152    /// use logforth_core::record::Level;
153    /// use logforth_core::record::LevelFilter;
154    ///
155    /// let logger = logforth_core::builder()
156    ///     .dispatch(|d| {
157    ///         d.filter(LevelFilter::MoreSevereEqual(Level::Error))
158    ///             .diagnostic(diagnostic::ThreadLocalDiagnostic::default())
159    ///             .append(append::Stderr::default())
160    ///     })
161    ///     .build();
162    /// ```
163    pub fn diagnostic(mut self, diagnostic: impl Into<Box<dyn Diagnostic>>) -> Self {
164        self.diagnostics.push(diagnostic.into());
165        self
166    }
167}
168
169impl DispatchBuilder<true> {
170    fn build(self) -> Dispatch {
171        Dispatch::new(self.filters, self.diagnostics, self.appends)
172    }
173}
174
175impl<const APPEND: bool> DispatchBuilder<APPEND> {
176    /// Add an appender to this dispatch.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use logforth_core::append;
182    ///
183    /// let logger = logforth_core::builder()
184    ///     .dispatch(|d| d.append(append::Stdout::default()))
185    ///     .build();
186    /// ```
187    pub fn append(mut self, append: impl Into<Box<dyn Append>>) -> DispatchBuilder<true> {
188        self.appends.push(append.into());
189        DispatchBuilder {
190            filters: self.filters,
191            diagnostics: self.diagnostics,
192            appends: self.appends,
193        }
194    }
195}