spdlog/sink/
write_sink.rs1use 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
10pub 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 #[must_use]
56 pub fn builder() -> WriteSinkBuilder<W, ()> {
57 WriteSinkBuilder {
58 prop: SinkProp::default(),
59 target: None,
60 _phantom: PhantomData,
61 }
62 }
63
64 #[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 #[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#[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 #[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 #[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 #[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 #[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 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}