Skip to main content

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