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