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 Error, ErrorHandler, Level, LevelFilter, Record, Result,
68};
69
70pub(crate) const SINK_DEFAULT_LEVEL_FILTER: LevelFilter = LevelFilter::All;
71
72/// Contains definitions of sink properties.
73///
74/// It provides a set of common properties for sink to define. If there is no
75/// special need for properties, use it directly and then implement
76/// [`GetSinkProp`] for your sink, a blanket implementation will be enabled,
77/// which would eliminate a lot of boilerplate code.
78///
79/// If further customization of the properties is needed (e.g., using different
80/// types, changing behavior), this struct is not needed. Instead, define
81/// properties manually within your sink, and then implement [`SinkPropAccess`].
82pub struct SinkProp {
83 level_filter: Atomic<LevelFilter>,
84 formatter: RwLockMappable<Box<dyn Formatter>>,
85 error_handler: RwLock<ErrorHandler>,
86}
87
88impl Default for SinkProp {
89 fn default() -> Self {
90 Self {
91 level_filter: Atomic::new(SINK_DEFAULT_LEVEL_FILTER),
92 formatter: RwLockMappable::new(Box::new(FullFormatter::new())),
93 error_handler: RwLock::new(ErrorHandler::default()),
94 }
95 }
96}
97
98impl SinkProp {
99 /// Gets the log level filter.
100 #[must_use]
101 pub fn level_filter(&self) -> LevelFilter {
102 self.level_filter.load(Ordering::Relaxed)
103 }
104
105 /// Sets the log level filter.
106 pub fn set_level_filter(&self, level_filter: LevelFilter) {
107 self.level_filter.store(level_filter, Ordering::Relaxed)
108 }
109
110 /// Gets the formatter.
111 ///
112 /// The returned value is a lock guard, so please avoid storing it in a
113 /// variable with a longer lifetime.
114 pub fn formatter<'a>(&'a self) -> impl Deref<Target = dyn Formatter> + 'a {
115 RwLockMappableReadGuard::map(self.formatter.read(), |f| &**f)
116 }
117
118 /// Sets the formatter.
119 pub fn set_formatter<F>(&self, formatter: F)
120 where
121 F: Formatter + 'static,
122 {
123 self.set_formatter_boxed(Box::new(formatter));
124 }
125
126 /// Sets the boxed formatter.
127 pub fn set_formatter_boxed(&self, formatter: Box<dyn Formatter>) {
128 *self.formatter.write() = formatter;
129 }
130
131 /// Calls the error handler with an error.
132 pub fn call_error_handler(&self, err: Error) {
133 self.error_handler.read_expect().call(err)
134 }
135
136 pub(crate) fn call_error_handler_internal(&self, from: impl AsRef<str>, err: Error) {
137 self.error_handler.read_expect().call_internal(from, err)
138 }
139
140 /// Sets a error handler.
141 ///
142 /// Most errors that occur in `Sink` will be returned as directly as
143 /// possible (e.g. returned to [`Logger`]), but some errors that cannot be
144 /// returned immediately, this function will be called. For example,
145 /// asynchronous errors.
146 ///
147 /// [`Logger`]: crate::logger::Logger
148 pub fn set_error_handler<F: Into<ErrorHandler>>(&self, handler: F) {
149 *self.error_handler.write_expect() = handler.into();
150 }
151}
152
153/// Represents the getter for the [`SinkProp`] inside a sink.
154///
155/// This trait is not mandatory for a sink. It enables a blanket implementation,
156/// where a sink that implements this trait will automatically get the
157/// [`SinkPropAccess`] trait implemented, which eliminates a lot of boilerplate
158/// code.
159pub trait GetSinkProp {
160 /// Gets the [`SinkProp`] from a sink.
161 fn prop(&self) -> &SinkProp;
162}
163
164/// Represents getters for properties of a sink.
165///
166/// The use of a sink requires these properties, and this trait describes the
167/// methods for getting them.
168///
169/// For the common case of custom sinks, users don't need to implement this
170/// trait manually, they can just store a `SinkProp` in their sink struct and
171/// implement trait [`GetSinkProp`], a blanket implementation will automatically
172/// implement `SinkPropAccess` for the sink.
173///
174/// For more details on implementing custom sink, see [. /examples] directory.
175///
176/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
177pub trait SinkPropAccess {
178 /// Gets the log level filter.
179 #[must_use]
180 fn level_filter(&self) -> LevelFilter;
181
182 /// Sets the log level filter.
183 fn set_level_filter(&self, level_filter: LevelFilter);
184
185 /// Sets the formatter.
186 fn set_formatter(&self, formatter: Box<dyn Formatter>);
187
188 /// Sets a error handler.
189 ///
190 /// Most errors that occur in `Sink` will be returned as directly as
191 /// possible (e.g. returned to [`Logger`]), but some errors that cannot be
192 /// returned immediately, this function will be called. For example,
193 /// asynchronous errors.
194 ///
195 /// [`Logger`]: crate::logger::Logger
196 fn set_error_handler(&self, handler: ErrorHandler);
197}
198
199impl<S: GetSinkProp> SinkPropAccess for S {
200 fn level_filter(&self) -> LevelFilter {
201 self.prop().level_filter()
202 }
203
204 fn set_level_filter(&self, level_filter: LevelFilter) {
205 self.prop().set_level_filter(level_filter);
206 }
207
208 fn set_formatter(&self, formatter: Box<dyn Formatter>) {
209 self.prop().set_formatter_boxed(formatter);
210 }
211
212 fn set_error_handler(&self, handler: ErrorHandler) {
213 self.prop().set_error_handler(handler);
214 }
215}
216
217/// Represents a sink
218///
219/// See [./examples] directory for how to implement a custom sink.
220///
221/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
222pub trait Sink: SinkPropAccess + Sync + Send {
223 /// Determines if a log message with the specified level would be logged.
224 #[must_use]
225 fn should_log(&self, level: Level) -> bool {
226 self.level_filter().test(level)
227 }
228
229 /// Logs a record.
230 fn log(&self, record: &Record) -> Result<()>;
231
232 /// Flushes any buffered records.
233 fn flush(&self) -> Result<()>;
234}
235
236/// Container type for [`Sink`]s.
237pub type Sinks = Vec<Arc<dyn Sink>>;