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>>;