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
8pub struct Guard {
12 _guard: Option<GuardInner>,
13}
14
15pub struct MakeWriter(MakeWriterInner);
18
19pub struct Writer<'a>(WriterInner<'a>);
21
22#[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 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 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}