logforth_core/logger/
log_impl.rs1use std::io::Write;
16use std::panic;
17use std::sync::Once;
18use std::sync::OnceLock;
19
20use crate::Append;
21use crate::Diagnostic;
22use crate::Error;
23use crate::Filter;
24use crate::filter::FilterResult;
25use crate::record::Metadata;
26use crate::record::Record;
27
28static DEFAULT_LOGGER: OnceLock<Logger> = OnceLock::new();
29
30pub fn default_logger() -> &'static Logger {
34 static NOP_LOGGER: Logger = Logger { dispatches: vec![] };
35 DEFAULT_LOGGER.get().unwrap_or(&NOP_LOGGER)
36}
37
38pub fn set_default_logger(logger: Logger) -> Result<(), Logger> {
43 static ATEXIT_CALLBACK: Once = Once::new();
44
45 DEFAULT_LOGGER.set(logger)?;
46 ATEXIT_CALLBACK.call_once(flush_default_logger_at_exit);
47 Ok(())
48}
49
50fn flush_default_logger_at_exit() {
51 extern "C" fn handler() {
57 if let Some(default_logger) = DEFAULT_LOGGER.get() {
58 default_logger.exit();
59 }
60 }
61
62 #[must_use]
63 fn try_atexit() -> bool {
64 use std::os::raw::c_int;
65
66 unsafe extern "C" {
67 fn atexit(cb: extern "C" fn()) -> c_int;
68 }
69
70 (unsafe { atexit(handler) }) == 0
71 }
72
73 fn hook_panic() {
74 let previous_hook = panic::take_hook();
75
76 panic::set_hook(Box::new(move |info| {
77 handler();
78 previous_hook(info);
79 }));
80 }
81
82 if !try_atexit() {
83 hook_panic();
85 }
86}
87
88#[derive(Debug)]
90pub struct Logger {
91 dispatches: Vec<Dispatch>,
92}
93
94impl Logger {
95 pub(super) fn new(dispatches: Vec<Dispatch>) -> Self {
96 Self { dispatches }
97 }
98}
99
100impl Logger {
101 pub fn enabled(&self, metadata: &Metadata) -> bool {
103 self.dispatches
104 .iter()
105 .any(|dispatch| dispatch.enabled(metadata))
106 }
107
108 pub fn log(&self, record: &Record) {
110 for dispatch in &self.dispatches {
111 for err in dispatch.log(record) {
112 handle_log_error(record, &err);
113 }
114 }
115 }
116
117 pub fn flush(&self) {
119 for dispatch in &self.dispatches {
120 for err in dispatch.flush() {
121 handle_flush_error(&err);
122 }
123 }
124 }
125
126 pub fn exit(&self) {
128 for dispatch in &self.dispatches {
129 for err in dispatch.exit() {
130 handle_exit_error(&err);
131 }
132 }
133 }
134}
135
136#[derive(Debug)]
144pub(super) struct Dispatch {
145 filters: Vec<Box<dyn Filter>>,
146 diagnostics: Vec<Box<dyn Diagnostic>>,
147 appends: Vec<Box<dyn Append>>,
148}
149
150impl Dispatch {
151 pub(super) fn new(
152 filters: Vec<Box<dyn Filter>>,
153 diagnostics: Vec<Box<dyn Diagnostic>>,
154 appends: Vec<Box<dyn Append>>,
155 ) -> Self {
156 debug_assert!(
157 !appends.is_empty(),
158 "A Dispatch must have at least one filter"
159 );
160
161 Self {
162 filters,
163 diagnostics,
164 appends,
165 }
166 }
167
168 fn enabled(&self, metadata: &Metadata) -> bool {
169 let diagnostics = &self.diagnostics;
170
171 for filter in &self.filters {
172 match filter.enabled(metadata, diagnostics) {
173 FilterResult::Reject => return false,
174 FilterResult::Accept => return true,
175 FilterResult::Neutral => {}
176 }
177 }
178
179 true
180 }
181
182 fn log(&self, record: &Record) -> Vec<Error> {
183 let diagnostics = &self.diagnostics;
184
185 for filter in &self.filters {
186 match filter.matches(record, diagnostics) {
187 FilterResult::Reject => return vec![],
188 FilterResult::Accept => break,
189 FilterResult::Neutral => {}
190 }
191 }
192
193 let mut errors = vec![];
194 for append in &self.appends {
195 if let Err(err) = append.append(record, diagnostics) {
196 errors.push(err);
197 }
198 }
199 errors
200 }
201
202 fn flush(&self) -> Vec<Error> {
203 let mut errors = vec![];
204 for append in &self.appends {
205 if let Err(err) = append.flush() {
206 errors.push(err);
207 }
208 }
209 errors
210 }
211
212 fn exit(&self) -> Vec<Error> {
213 let mut errors = vec![];
214 for append in &self.appends {
215 if let Err(err) = append.exit() {
216 errors.push(err);
217 }
218 }
219 errors
220 }
221}
222
223fn handle_log_error(record: &Record, error: &Error) {
224 let Err(fallback_error) = write!(
225 std::io::stderr(),
226 r###"
227Error perform logging.
228 Attempted to log: {args}
229 Record: {record:?}
230 Error: {error:?}
231"###,
232 args = record.payload(),
233 record = record,
234 error = error,
235 ) else {
236 return;
237 };
238
239 panic!(
240 r###"
241Error performing stderr logging after error occurred during regular logging.
242 Attempted to log: {args}
243 Record: {record:?}
244 Error: {error:?}
245 Fallback error: {fallback_error}
246"###,
247 args = record.payload(),
248 record = record,
249 error = error,
250 fallback_error = fallback_error,
251 );
252}
253
254fn handle_flush_error(error: &Error) {
255 let Err(fallback_error) = write!(
256 std::io::stderr(),
257 r###"
258Error perform flush.
259 Error: {error:?}
260"###,
261 ) else {
262 return;
263 };
264
265 panic!(
266 r###"
267Error performing stderr logging after error occurred during regular flush.
268 Error: {error:?}
269 Fallback error: {fallback_error}
270"###,
271 );
272}
273
274fn handle_exit_error(error: &Error) {
275 let Err(fallback_error) = write!(
276 std::io::stderr(),
277 r###"
278Error perform exit.
279 Error: {error:?}
280"###,
281 ) else {
282 return;
283 };
284
285 panic!(
286 r###"
287Error performing stderr logging after error occurred during atexit.
288 Error: {error:?}
289 Fallback error: {fallback_error}
290"###,
291 );
292}