spdlog/sink/
write_sink.rs

1use std::{convert::Infallible, io::Write, marker::PhantomData};
2
3use crate::{
4    formatter::{Formatter, FormatterContext},
5    sink::{GetSinkProp, Sink, SinkProp},
6    sync::*,
7    Error, ErrorHandler, LevelFilter, Record, Result, StringBuf,
8};
9
10/// A sink that writes log messages into an arbitrary `impl Write` object.
11///
12/// # Performance Notice
13///
14/// Since `WriteSink` can write into any `impl Write` objects, the assumptions
15/// made on the underlying `impl Write` object is very weak and this does impact
16/// performance. You should use other sinks or implement your own sinks whenever
17/// possible. `WriteSink` is your last resort if no other sinks meet your
18/// requirement.
19///
20/// If you want to log into a file, use [`FileSink`] or [`RotatingFileSink`]
21/// instead.
22///
23/// If you want to log into the standard streams, use [`StdStreamSink`] instead.
24///
25/// [`FileSink`]: crate::sink::FileSink
26/// [`RotatingFileSink`]: crate::sink::RotatingFileSink
27/// [`StdStreamSink`]: crate::sink::StdStreamSink
28pub struct WriteSink<W>
29where
30    W: Write + Send,
31{
32    prop: SinkProp,
33    target: Mutex<W>,
34}
35
36impl<W> WriteSink<W>
37where
38    W: Write + Send,
39{
40    /// Gets a builder of `WriteSink` with default parameters:
41    ///
42    /// | Parameter         | Default Value               |
43    /// |-------------------|-----------------------------|
44    /// | [level_filter]    | `All`                       |
45    /// | [formatter]       | `FullFormatter`             |
46    /// | [error_handler]   | [`ErrorHandler::default()`] |
47    /// |                   |                             |
48    /// | [target]          | *must be specified*         |
49    ///
50    /// [level_filter]: WriteSinkBuilder::level_filter
51    /// [formatter]: WriteSinkBuilder::formatter
52    /// [error_handler]: WriteSinkBuilder::error_handler
53    /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
54    /// [target]: WriteSinkBuilder::target
55    #[must_use]
56    pub fn builder() -> WriteSinkBuilder<W, ()> {
57        WriteSinkBuilder {
58            prop: SinkProp::default(),
59            target: None,
60            _phantom: PhantomData,
61        }
62    }
63
64    /// Invoke a callback function with the underlying `impl Write` object.
65    ///
66    /// This function returns whatever the given callback function returns.
67    ///
68    /// Note that this sink cannot write into the underlying `impl Write` object
69    /// while the given callback function is running. If the underlying
70    /// `impl Write` object supports a relatively cheap `clone` operation,
71    /// consider using the [`clone_target`] method.
72    ///
73    /// [`clone_target`]: Self::clone_target
74    #[must_use]
75    pub fn with_target<F, R>(&self, callback: F) -> R
76    where
77        F: FnOnce(&mut W) -> R,
78    {
79        callback(&mut *self.lock_target())
80    }
81
82    fn lock_target(&self) -> MutexGuard<'_, W> {
83        self.target.lock_expect()
84    }
85}
86
87impl<W> WriteSink<W>
88where
89    W: Write + Send + Clone,
90{
91    /// Clone the underlying `impl Write` object.
92    #[must_use]
93    pub fn clone_target(&self) -> W {
94        self.lock_target().clone()
95    }
96}
97
98impl<W> GetSinkProp for WriteSink<W>
99where
100    W: Write + Send,
101{
102    fn prop(&self) -> &SinkProp {
103        &self.prop
104    }
105}
106
107impl<W> Sink for WriteSink<W>
108where
109    W: Write + Send,
110{
111    fn log(&self, record: &Record) -> Result<()> {
112        let mut string_buf = StringBuf::new();
113        let mut ctx = FormatterContext::new();
114        self.prop
115            .formatter()
116            .format(record, &mut string_buf, &mut ctx)?;
117
118        self.lock_target()
119            .write_all(string_buf.as_bytes())
120            .map_err(Error::WriteRecord)?;
121
122        Ok(())
123    }
124
125    fn flush(&self) -> Result<()> {
126        self.lock_target().flush().map_err(Error::FlushBuffer)
127    }
128}
129
130impl<W> Drop for WriteSink<W>
131where
132    W: Write + Send,
133{
134    fn drop(&mut self) {
135        let flush_result = self.lock_target().flush().map_err(Error::FlushBuffer);
136        if let Err(err) = flush_result {
137            self.prop.call_error_handler_internal("WriteSink", err)
138        }
139    }
140}
141
142/// #
143#[doc = include_str!("../include/doc/generic-builder-note.md")]
144pub struct WriteSinkBuilder<W, ArgW> {
145    prop: SinkProp,
146    target: Option<W>,
147    _phantom: PhantomData<ArgW>,
148}
149
150impl<W, ArgW> WriteSinkBuilder<W, ArgW>
151where
152    W: Write + Send,
153{
154    /// Specifies the target that implemented [`Write`] trait, log messages will
155    /// be written into the target.
156    ///
157    /// This parameter is **required**.
158    #[must_use]
159    pub fn target(self, target: W) -> WriteSinkBuilder<W, PhantomData<W>> {
160        WriteSinkBuilder {
161            prop: self.prop,
162            target: Some(target),
163            _phantom: PhantomData,
164        }
165    }
166
167    // Prop
168    //
169
170    /// Specifies a log level filter.
171    ///
172    /// This parameter is **optional**.
173    #[must_use]
174    pub fn level_filter(self, level_filter: LevelFilter) -> Self {
175        self.prop.set_level_filter(level_filter);
176        self
177    }
178
179    /// Specifies a formatter.
180    ///
181    /// This parameter is **optional**.
182    #[must_use]
183    pub fn formatter<F>(self, formatter: F) -> Self
184    where
185        F: Formatter + 'static,
186    {
187        self.prop.set_formatter(formatter);
188        self
189    }
190
191    /// Specifies an error handler.
192    ///
193    /// This parameter is **optional**.
194    #[must_use]
195    pub fn error_handler<F: Into<ErrorHandler>>(self, handler: F) -> Self {
196        self.prop.set_error_handler(handler);
197        self
198    }
199}
200
201impl<W> WriteSinkBuilder<W, ()>
202where
203    W: Write + Send,
204{
205    #[doc(hidden)]
206    #[deprecated(note = "\n\n\
207        builder compile-time error:\n\
208        - missing required parameter `target`\n\n\
209    ")]
210    pub fn build(self, _: Infallible) {}
211}
212
213impl<W> WriteSinkBuilder<W, PhantomData<W>>
214where
215    W: Write + Send,
216{
217    /// Builds a [`WriteSink`].
218    pub fn build(self) -> Result<WriteSink<W>> {
219        let sink = WriteSink {
220            prop: self.prop,
221            target: Mutex::new(self.target.unwrap()),
222        };
223        Ok(sink)
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use crate::{prelude::*, test_utils::*};
231
232    #[test]
233    fn validation() {
234        let sink = Arc::new(WriteSink::builder().target(Vec::new()).build().unwrap());
235        sink.set_formatter(Box::new(NoModFormatter::new()));
236        let logger = build_test_logger(|b| b.sink(sink.clone()).level_filter(LevelFilter::All));
237
238        info!(logger: logger, "hello WriteSink");
239
240        let data = sink.clone_target();
241        assert_eq!(data.as_slice(), b"hello WriteSink");
242    }
243}