witchcraft_log/logger.rs
1// Copyright 2019 Palantir Technologies, Inc.
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.
14use crate::{LevelFilter, Metadata, Record};
15use std::error::Error;
16use std::sync::atomic::{AtomicUsize, Ordering};
17use std::sync::OnceLock;
18use std::{fmt, mem};
19
20/// A trait encapsulating the operations required of a logger.
21pub trait Log: Sync + Send {
22 /// Determines ifa log message with the specified metadata would be logged.
23 ///
24 /// This is used by the `enabled!` macro to allow callers to avoid expensive computation of log message parameters
25 /// if the message would be discarded anyway.
26 fn enabled(&self, metadata: &Metadata<'_>) -> bool;
27
28 /// Logs a `Record`.
29 ///
30 /// Note that `enabled` is *not* necessarily called before this method. Implementations of `log` should perform all
31 /// necessary filtering internally.
32 fn log(&self, record: &Record<'_>);
33
34 /// Flushes any buffered records.
35 fn flush(&self);
36}
37
38struct NopLogger;
39
40impl Log for NopLogger {
41 fn enabled(&self, _: &Metadata<'_>) -> bool {
42 false
43 }
44
45 fn log(&self, _: &Record<'_>) {}
46
47 fn flush(&self) {}
48}
49
50static LOGGER: OnceLock<&'static dyn Log> = OnceLock::new();
51
52/// Sets the global logger to a `&'static dyn Log`.
53///
54/// The global logger can only be set once. Further calls will return an error.
55pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
56 LOGGER.set(logger).map_err(|_| SetLoggerError(()))
57}
58
59/// Sets the global logger to a `Box<dyn Log>`.
60///
61/// The global logger can only be set once. Further calls will return an error.
62pub fn set_boxed_logger(logger: Box<dyn Log>) -> Result<(), SetLoggerError> {
63 let mut logger = Some(logger);
64 LOGGER.get_or_init(|| Box::leak(logger.take().unwrap()));
65 match logger {
66 Some(_) => Err(SetLoggerError(())),
67 None => Ok(()),
68 }
69}
70
71/// Returns the global logger.
72///
73/// If one has not been set, a no-op implementation will be returned.
74pub fn logger() -> &'static dyn Log {
75 LOGGER.get().map_or(&NopLogger, |l| *l)
76}
77
78/// An error trying to set the logger when one is already installed.
79#[derive(Debug)]
80pub struct SetLoggerError(());
81
82impl fmt::Display for SetLoggerError {
83 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
84 fmt.write_str("a logger is already installed")
85 }
86}
87
88impl Error for SetLoggerError {}
89
90static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0);
91
92/// Sets the global maximum log level.
93///
94/// Generally, this should only be called by the logging implementation.
95pub fn set_max_level(level: LevelFilter) {
96 MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed);
97}
98
99/// Returns the current maximum log level.
100///
101/// The log macros check this value and discard any message logged at a higher level as an optimization. The maximum
102/// level is set by the `set_max_level` function.
103#[inline(always)]
104pub fn max_level() -> LevelFilter {
105 unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) }
106}