Skip to main content

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