spdlog/sink/
mod.rs

1//! Provides sinks to flexibly output log messages to specified targets.
2//!
3//! # Sink
4//!
5//! Sinks are the objects that actually write logs to their targets. Each sink
6//! should be responsible for only single target (e.g file, console, database),
7//! and each sink has its own private instance of [`Formatter`] object.
8//!
9//! A sink has its own level filter that is not shared with the logger, and a
10//! [`Logger`] can combine multiple [`Sink`]s.
11//!
12//! # Combined sink
13//!
14//! A combined sink is also a sink, but instead of having its own target and
15//! formatter, it combines other sinks (as sub-sinks).
16//!
17//! Operations on a combined sink will be forwarded to its sub-sinks according
18//! to the implementation.
19//!
20//! [`Logger`]: crate::logger::Logger
21
22#[cfg(any(
23    all(target_os = "android", feature = "native", feature = "android-ndk"),
24    all(doc, not(doctest))
25))]
26mod android_sink;
27#[cfg(feature = "multi-thread")]
28pub(crate) mod async_sink;
29mod dedup_sink;
30mod file_sink;
31#[cfg(any(
32    all(target_os = "linux", feature = "native", feature = "libsystemd"),
33    all(doc, not(doctest))
34))]
35mod journald_sink;
36mod rotating_file_sink;
37mod std_stream_sink;
38#[cfg(any(all(windows, feature = "native"), all(doc, not(doctest))))]
39mod win_debug_sink;
40mod write_sink;
41
42use std::ops::Deref;
43
44#[cfg(any(
45    all(target_os = "android", feature = "native", feature = "android-ndk"),
46    all(doc, not(doctest))
47))]
48pub use android_sink::*;
49#[cfg(feature = "multi-thread")]
50pub use async_sink::*;
51pub use dedup_sink::*;
52pub use file_sink::*;
53#[cfg(any(
54    all(target_os = "linux", feature = "native", feature = "libsystemd"),
55    all(doc, not(doctest))
56))]
57pub use journald_sink::*;
58pub use rotating_file_sink::*;
59pub use std_stream_sink::*;
60#[cfg(any(all(windows, feature = "native"), all(doc, not(doctest))))]
61pub use win_debug_sink::*;
62pub use write_sink::*;
63
64use crate::{
65    formatter::{Formatter, FullFormatter},
66    sync::*,
67    AtomicLevelFilter, Error, ErrorHandler, Level, LevelFilter, Record, Result,
68};
69
70/// Contains definitions of sink properties.
71///
72/// It provides a set of common properties for sink to define. If there is no
73/// special need for properties, use it directly and then implement
74/// [`GetSinkProp`] for your sink, a blanket implementation will be enabled,
75/// which would eliminate a lot of boilerplate code.
76///
77/// If further customization of the properties is needed (e.g., using different
78/// types, changing behavior), this struct is not needed. Instead, define
79/// properties manually within your sink, and then implement [`SinkPropAccess`].
80pub struct SinkProp {
81    level_filter: AtomicLevelFilter,
82    formatter: RwLockMappable<Box<dyn Formatter>>,
83    error_handler: RwLock<ErrorHandler>,
84}
85
86impl Default for SinkProp {
87    /// Returns a `SinkProp` with default values:
88    ///
89    /// | Parameter       | Default Value               |
90    /// |-----------------|-----------------------------|
91    /// | `level_filter`  | [`LevelFilter::All`]        |
92    /// | `formatter`     | [`FullFormatter`]           |
93    /// | `error_handler` | [`ErrorHandler::default()`] |
94    fn default() -> Self {
95        Self {
96            level_filter: AtomicLevelFilter::new(LevelFilter::All),
97            formatter: RwLockMappable::new(Box::new(FullFormatter::new())),
98            error_handler: RwLock::new(ErrorHandler::default()),
99        }
100    }
101}
102
103impl SinkProp {
104    /// Gets the log level filter.
105    #[must_use]
106    pub fn level_filter(&self) -> LevelFilter {
107        self.level_filter.get()
108    }
109
110    /// Sets the log level filter.
111    pub fn set_level_filter(&self, level_filter: LevelFilter) {
112        self.level_filter.set(level_filter)
113    }
114
115    /// Gets the formatter.
116    ///
117    /// The returned value is a lock guard, so please avoid storing it in a
118    /// variable with a longer lifetime.
119    pub fn formatter<'a>(&'a self) -> impl Deref<Target = dyn Formatter> + 'a {
120        RwLockMappableReadGuard::map(self.formatter.read(), |f| &**f)
121    }
122
123    /// Sets the formatter.
124    pub fn set_formatter<F>(&self, formatter: F)
125    where
126        F: Formatter + 'static,
127    {
128        self.set_formatter_boxed(Box::new(formatter));
129    }
130
131    /// Sets the boxed formatter.
132    pub fn set_formatter_boxed(&self, formatter: Box<dyn Formatter>) {
133        *self.formatter.write() = formatter;
134    }
135
136    /// Calls the error handler with an error.
137    pub fn call_error_handler(&self, err: Error) {
138        self.error_handler.read_expect().call(err)
139    }
140
141    pub(crate) fn call_error_handler_internal(&self, from: impl AsRef<str>, err: Error) {
142        self.error_handler.read_expect().call_internal(from, err)
143    }
144
145    /// Sets a error handler.
146    ///
147    /// Most errors that occur in `Sink` will be returned as directly as
148    /// possible (e.g. returned to [`Logger`]), but some errors that cannot be
149    /// returned immediately, this function will be called. For example,
150    /// asynchronous errors.
151    ///
152    /// [`Logger`]: crate::logger::Logger
153    pub fn set_error_handler<F: Into<ErrorHandler>>(&self, handler: F) {
154        *self.error_handler.write_expect() = handler.into();
155    }
156}
157
158/// Represents the getter for the [`SinkProp`] inside a sink.
159///
160/// This trait is not mandatory for a sink. It enables a blanket implementation,
161/// where a sink that implements this trait will automatically get the
162/// [`SinkPropAccess`] trait implemented, which eliminates a lot of boilerplate
163/// code.
164pub trait GetSinkProp {
165    /// Gets the [`SinkProp`] from a sink.
166    fn prop(&self) -> &SinkProp;
167}
168
169/// Represents getters for properties of a sink.
170///
171/// The use of a sink requires these properties, and this trait describes the
172/// methods for getting them.
173///
174/// For the common case of custom sinks, users don't need to implement this
175/// trait manually, they can just store a `SinkProp` in their sink struct and
176/// implement trait [`GetSinkProp`], a blanket implementation will automatically
177/// implement `SinkPropAccess` for the sink.
178///
179/// For more details on implementing custom sink, see [. /examples] directory.
180///
181/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
182pub trait SinkPropAccess {
183    /// Gets the log level filter.
184    #[must_use]
185    fn level_filter(&self) -> LevelFilter;
186
187    /// Sets the log level filter.
188    fn set_level_filter(&self, level_filter: LevelFilter);
189
190    /// Sets the formatter.
191    fn set_formatter(&self, formatter: Box<dyn Formatter>);
192
193    /// Sets a error handler.
194    ///
195    /// Most errors that occur in `Sink` will be returned as directly as
196    /// possible (e.g. returned to [`Logger`]), but some errors that cannot be
197    /// returned immediately, this function will be called. For example,
198    /// asynchronous errors.
199    ///
200    /// [`Logger`]: crate::logger::Logger
201    fn set_error_handler(&self, handler: ErrorHandler);
202}
203
204impl<S: GetSinkProp> SinkPropAccess for S {
205    fn level_filter(&self) -> LevelFilter {
206        self.prop().level_filter()
207    }
208
209    fn set_level_filter(&self, level_filter: LevelFilter) {
210        self.prop().set_level_filter(level_filter);
211    }
212
213    fn set_formatter(&self, formatter: Box<dyn Formatter>) {
214        self.prop().set_formatter_boxed(formatter);
215    }
216
217    fn set_error_handler(&self, handler: ErrorHandler) {
218        self.prop().set_error_handler(handler);
219    }
220}
221
222/// Represents a sink
223///
224/// See [./examples] directory for how to implement a custom sink.
225///
226/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
227pub trait Sink: SinkPropAccess + Sync + Send {
228    /// Determines if a log message with the specified level would be logged.
229    #[must_use]
230    fn should_log(&self, level: Level) -> bool {
231        self.level_filter().test(level)
232    }
233
234    /// Logs a record.
235    fn log(&self, record: &Record) -> Result<()>;
236
237    /// Flushes any buffered records.
238    fn flush(&self) -> Result<()>;
239
240    /// Flushes any buffered records at program exit.
241    ///
242    /// _spdlog-rs_ will perform a flush for sinks in the default logger when
243    /// the program exits, and the flush will be called to this method
244    /// `flush_on_exit` instead of `flush`. This is because the execution
245    /// context may be in the [`atexit`] callback or in the panic handler when
246    /// exiting. In such a context, some operations are restricted, e.g.
247    /// Thread-local Storage (TLS) may not be available in `atexit` callbacks.
248    ///
249    /// This method calls directly to `flush` method by default. When users'
250    /// `flush` method implementation is not usable in a program exit context,
251    /// users should override the implementation of this method to provide an
252    /// alternative flushing implementation. See the implementation of
253    /// [`AsyncPoolSink::flush_on_exit`] as an example.
254    ///
255    /// For [combined sink]s, this method should always be overridden to
256    /// propagate the information that "the program is exiting" to their
257    /// sub-sinks. See the implementation of [`DedupSink::flush_on_exit`] as an
258    /// example.
259    ///
260    /// [`atexit`]: https://en.cppreference.com/w/c/program/atexit
261    /// [combined sink]: index.html#combined-sink
262    fn flush_on_exit(&self) -> Result<()> {
263        self.flush()
264    }
265}
266
267/// Container type for [`Sink`]s.
268pub type Sinks = Vec<Arc<dyn Sink>>;