logforth_core/logger/
log_impl.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 std::io::Write;
16use std::sync::OnceLock;
17
18use crate::Append;
19use crate::Diagnostic;
20use crate::Error;
21use crate::Filter;
22use crate::filter::FilterResult;
23use crate::record::Metadata;
24use crate::record::Record;
25
26static DEFAULT_LOGGER: OnceLock<Logger> = OnceLock::new();
27
28/// Return the default global logger instance.
29///
30/// If no default logger has been set, `None` is returned.
31pub fn default_logger() -> Option<&'static Logger> {
32    DEFAULT_LOGGER.get()
33}
34
35/// Set the default global logger instance.
36///
37/// If a default logger has already been set, the function returns the provided logger
38/// as an error.
39pub fn set_default_logger(logger: Logger) -> Result<(), Logger> {
40    DEFAULT_LOGGER.set(logger)
41}
42
43/// A logger facade that dispatches log records to one or more dispatcher.
44#[derive(Debug)]
45pub struct Logger {
46    dispatches: Vec<Dispatch>,
47}
48
49impl Logger {
50    pub(super) fn new(dispatches: Vec<Dispatch>) -> Self {
51        Self { dispatches }
52    }
53}
54
55impl Logger {
56    /// Determines if a log message with the specified metadata would be logged.
57    pub fn enabled(&self, metadata: &Metadata) -> bool {
58        self.dispatches
59            .iter()
60            .any(|dispatch| dispatch.enabled(metadata))
61    }
62
63    /// Logs the Record.
64    pub fn log(&self, record: &Record) {
65        for dispatch in &self.dispatches {
66            if let Err(err) = dispatch.log(record) {
67                handle_log_error(record, err);
68            }
69        }
70    }
71
72    /// Flushes any buffered records.
73    pub fn flush(&self) {
74        for dispatch in &self.dispatches {
75            if let Err(err) = dispatch.flush() {
76                handle_flush_error(err);
77            }
78        }
79    }
80}
81
82/// A grouped set of appenders and filters.
83///
84/// The [`Logger`] facade dispatches log records to one or more [`Dispatch`] instances.
85/// Each [`Dispatch`] instance contains a set of filters and appenders.
86///
87/// `filters` are used to determine whether a log record should be passed to the appenders.
88/// `appends` are used to write log records to a destination.
89#[derive(Debug)]
90pub(super) struct Dispatch {
91    filters: Vec<Box<dyn Filter>>,
92    diagnostics: Vec<Box<dyn Diagnostic>>,
93    appends: Vec<Box<dyn Append>>,
94}
95
96impl Dispatch {
97    pub(super) fn new(
98        filters: Vec<Box<dyn Filter>>,
99        diagnostics: Vec<Box<dyn Diagnostic>>,
100        appends: Vec<Box<dyn Append>>,
101    ) -> Self {
102        debug_assert!(
103            !appends.is_empty(),
104            "A Dispatch must have at least one filter"
105        );
106
107        Self {
108            filters,
109            diagnostics,
110            appends,
111        }
112    }
113
114    fn enabled(&self, metadata: &Metadata) -> bool {
115        let diagnostics = &self.diagnostics;
116
117        for filter in &self.filters {
118            match filter.enabled(metadata, diagnostics) {
119                FilterResult::Reject => return false,
120                FilterResult::Accept => return true,
121                FilterResult::Neutral => {}
122            }
123        }
124
125        true
126    }
127
128    fn log(&self, record: &Record) -> Result<(), Error> {
129        let diagnostics = &self.diagnostics;
130
131        for filter in &self.filters {
132            match filter.matches(record, diagnostics) {
133                FilterResult::Reject => return Ok(()),
134                FilterResult::Accept => break,
135                FilterResult::Neutral => {}
136            }
137        }
138
139        for append in &self.appends {
140            append.append(record, diagnostics)?;
141        }
142        Ok(())
143    }
144
145    fn flush(&self) -> Result<(), Error> {
146        for append in &self.appends {
147            append.flush()?;
148        }
149        Ok(())
150    }
151}
152
153fn handle_log_error(record: &Record, error: Error) {
154    let Err(fallback_error) = write!(
155        std::io::stderr(),
156        r###"
157Error perform logging.
158    Attempted to log: {args}
159    Record: {record:?}
160    Error: {error:?}
161"###,
162        args = record.args(),
163        record = record,
164        error = error,
165    ) else {
166        return;
167    };
168
169    panic!(
170        r###"
171Error performing stderr logging after error occurred during regular logging.
172    Attempted to log: {args}
173    Record: {record:?}
174    Error: {error:?}
175    Fallback error: {fallback_error}
176"###,
177        args = record.args(),
178        record = record,
179        error = error,
180        fallback_error = fallback_error,
181    );
182}
183
184fn handle_flush_error(error: Error) {
185    let Err(fallback_error) = write!(
186        std::io::stderr(),
187        r###"
188Error perform flush.
189    Error: {error:?}
190"###,
191    ) else {
192        return;
193    };
194
195    panic!(
196        r###"
197Error performing stderr logging after error occurred during regular flush.
198    Error: {error:?}
199    Fallback error: {fallback_error}
200"###,
201    );
202}