tracing_configuration/
writer.rs

1use std::{error::Error as _, fmt, fs::File, io, sync::Arc};
2
3use tracing_appender::{
4    non_blocking::{NonBlocking, NonBlockingBuilder, WorkerGuard},
5    rolling::{RollingFileAppender, RollingWriter},
6};
7
8/// A thread guard in the case of [`NonBlocking`](crate::NonBlocking) config.
9///
10/// See [`WorkerGuard`] for more.
11pub struct Guard {
12    _guard: Option<GuardInner>,
13}
14
15/// Implementor of [`tracing_subscriber::fmt::MakeWriter`],
16/// constructed from [`Writer`](crate::Writer) in [`Self::new`].
17pub struct MakeWriter(MakeWriterInner);
18
19/// Implementor of [`io::Write`], used by [`MakeWriter`].
20pub struct Writer<'a>(WriterInner<'a>);
21
22/// Error that can occur when constructing a writer, including e.g [`File`]-opening errors.
23#[derive(Debug)]
24pub struct Error(io::Error);
25
26impl std::error::Error for Error {
27    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28        self.0.source()
29    }
30}
31
32impl fmt::Display for Error {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        self.0.fmt(f)
35    }
36}
37
38impl MakeWriter {
39    /// Create a new [`MakeWriter`], and a [`Guard`] that handles e.g flushing [`NonBlocking`] IO.
40    ///
41    /// Errors when opening files or directories are deferred for the subscriber to handle (typically by logging).
42    /// If you wish to handle them yourself, see [`Self::try_new`].
43    pub fn new(writer: crate::Writer) -> (Self, Guard) {
44        let (this, _guard) = MakeWriterInner::new(writer, true).expect("errors have been deferred");
45        (Self(this), Guard { _guard })
46    }
47    /// Create a new [`MakeWriter`].
48    ///
49    /// Returns [`Err`] if e.g opening a log file fails.
50    /// If you wish the subscriber to handle them (typically by logging), see [`Self::new`].
51    pub fn try_new(writer: crate::Writer) -> Result<(Self, Guard), Error> {
52        MakeWriterInner::new(writer, false).map(|(l, r)| (Self(l), Guard { _guard: r }))
53    }
54}
55impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for MakeWriter {
56    type Writer = Writer<'a>;
57
58    fn make_writer(&'a self) -> Self::Writer {
59        Writer(self.0.make_writer())
60    }
61}
62
63impl io::Write for Writer<'_> {
64    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
65        self.0.write(buf)
66    }
67
68    fn flush(&mut self) -> io::Result<()> {
69        self.0.flush()
70    }
71}
72
73impl crate::NonBlocking {
74    fn build<T: io::Write + Send + 'static>(
75        &self,
76        writer: T,
77    ) -> (tracing_appender::non_blocking::NonBlocking, WorkerGuard) {
78        let Self {
79            buffer_length,
80            behaviour,
81        } = self;
82        let mut builder = NonBlockingBuilder::default();
83        if let Some(it) = buffer_length {
84            builder = builder.buffered_lines_limit(*it)
85        }
86        let builder = match behaviour {
87            Some(crate::BackpressureBehaviour::Block) => builder.lossy(false),
88            Some(crate::BackpressureBehaviour::Drop) => builder.lossy(true),
89            None => builder,
90        };
91        builder.finish(writer)
92    }
93}
94
95impl MakeWriterInner {
96    fn new(writer: crate::Writer, defer: bool) -> Result<(Self, Option<GuardInner>), Error> {
97        match writer {
98            crate::Writer::File(crate::File {
99                path,
100                mode,
101                non_blocking,
102            }) => {
103                match match mode {
104                    crate::FileOpenMode::Truncate => File::create(&path),
105                    crate::FileOpenMode::Append => File::options().append(true).open(&path),
106                } {
107                    Ok(it) => match non_blocking {
108                        Some(nb) => {
109                            let (nb, _guard) = nb.build(it);
110                            Ok((
111                                Self::NonBlocking(nb),
112                                Some(GuardInner::NonBlocking { _guard }),
113                            ))
114                        }
115                        None => Ok((Self::File(it), None)),
116                    },
117                    Err(e) => {
118                        let e = io_extra::context(
119                            e,
120                            format!("couldn't open log file {}", path.display()),
121                        );
122                        match defer {
123                            true => Ok((Self::Deferred(Arc::new(e)), None)),
124                            false => Err(Error(e)),
125                        }
126                    }
127                }
128            }
129            crate::Writer::Rolling(crate::Rolling {
130                directory,
131                roll: rolling,
132                non_blocking,
133            }) => {
134                let crate::Roll {
135                    limit,
136                    prefix,
137                    suffix,
138                    rotation,
139                } = rolling.unwrap_or_default();
140                let mut builder = RollingFileAppender::builder();
141                if let Some(limit) = limit {
142                    builder = builder.max_log_files(limit)
143                }
144                if let Some(prefix) = prefix {
145                    builder = builder.filename_prefix(prefix)
146                }
147                if let Some(suffix) = suffix {
148                    builder = builder.filename_suffix(suffix)
149                }
150                let builder = match rotation.unwrap_or_default() {
151                    crate::Rotation::Minutely => {
152                        builder.rotation(tracing_appender::rolling::Rotation::MINUTELY)
153                    }
154                    crate::Rotation::Hourly => {
155                        builder.rotation(tracing_appender::rolling::Rotation::HOURLY)
156                    }
157                    crate::Rotation::Daily => {
158                        builder.rotation(tracing_appender::rolling::Rotation::DAILY)
159                    }
160                    crate::Rotation::Never => {
161                        builder.rotation(tracing_appender::rolling::Rotation::NEVER)
162                    }
163                };
164
165                match builder.build(&directory) {
166                    Ok(it) => match non_blocking {
167                        Some(nb) => {
168                            let (nb, _guard) = nb.build(it);
169                            Ok((
170                                Self::NonBlocking(nb),
171                                Some(GuardInner::NonBlocking { _guard }),
172                            ))
173                        }
174                        None => Ok((Self::Rolling(it), None)),
175                    },
176                    Err(e) => {
177                        let kind = e
178                            .source()
179                            .and_then(|it| it.downcast_ref::<io::Error>())
180                            .map(io::Error::kind)
181                            .unwrap_or(io::ErrorKind::Other);
182                        let e = io_extra::context(
183                            io::Error::new(kind, e),
184                            format!(
185                                "couldn't start logging in directory {}",
186                                directory.display()
187                            ),
188                        );
189                        match defer {
190                            true => Ok((Self::Deferred(Arc::new(e)), None)),
191                            false => Err(Error(e)),
192                        }
193                    }
194                }
195            }
196            crate::Writer::Stdout => Ok((Self::Stdout(io::stdout()), None)),
197            crate::Writer::Stderr => Ok((Self::Stderr(io::stderr()), None)),
198            crate::Writer::Null => Ok((Self::Null(io::sink()), None)),
199        }
200    }
201}
202
203enum GuardInner {
204    NonBlocking { _guard: WorkerGuard },
205}
206
207enum MakeWriterInner {
208    Null(io::Sink),
209    NonBlocking(tracing_appender::non_blocking::NonBlocking),
210    Stdout(io::Stdout),
211    Stderr(io::Stderr),
212    File(File),
213    Rolling(RollingFileAppender),
214    Deferred(Arc<io::Error>),
215}
216
217enum WriterInner<'a> {
218    Null(&'a io::Sink),
219    NonBlocking(NonBlocking),
220    Stdout(&'a io::Stdout),
221    Stderr(&'a io::Stderr),
222    File(&'a File),
223    Rolling(RollingWriter<'a>),
224    Deferred(&'a Arc<io::Error>),
225}
226
227impl io::Write for WriterInner<'_> {
228    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
229        match self {
230            WriterInner::NonBlocking(it) => it.write(buf),
231            WriterInner::Stdout(it) => it.write(buf),
232            WriterInner::Stderr(it) => it.write(buf),
233            WriterInner::File(it) => it.write(buf),
234            WriterInner::Rolling(it) => it.write(buf),
235            WriterInner::Null(it) => it.write(buf),
236            WriterInner::Deferred(e) => Err(io::Error::new(e.kind(), Arc::clone(e))),
237        }
238    }
239
240    fn flush(&mut self) -> io::Result<()> {
241        match self {
242            WriterInner::NonBlocking(it) => it.flush(),
243            WriterInner::Stdout(it) => it.flush(),
244            WriterInner::Stderr(it) => it.flush(),
245            WriterInner::File(it) => it.flush(),
246            WriterInner::Rolling(it) => it.flush(),
247            WriterInner::Null(it) => it.flush(),
248            WriterInner::Deferred(e) => Err(io::Error::new(e.kind(), Arc::clone(e))),
249        }
250    }
251}
252
253impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for MakeWriterInner {
254    type Writer = WriterInner<'a>;
255
256    fn make_writer(&'a self) -> Self::Writer {
257        match self {
258            MakeWriterInner::NonBlocking(it) => Self::Writer::NonBlocking(it.make_writer()),
259            MakeWriterInner::Stdout(it) => Self::Writer::Stdout(it),
260            MakeWriterInner::Stderr(it) => Self::Writer::Stderr(it),
261            MakeWriterInner::File(it) => Self::Writer::File(it.make_writer()),
262            MakeWriterInner::Rolling(it) => Self::Writer::Rolling(it.make_writer()),
263            MakeWriterInner::Null(it) => Self::Writer::Null(it),
264            MakeWriterInner::Deferred(it) => Self::Writer::Deferred(it),
265        }
266    }
267}