tracing_subscriber/fmt/writer.rs
1//! Abstractions for creating [`io::Write`] instances.
2//!
3//! [`io::Write`]: std::io::Write
4
5use std::{
6 fmt,
7 io::{self, Write},
8 sync::{Mutex, MutexGuard},
9};
10use tracing_core::Metadata;
11
12/// A type that can create [`io::Write`] instances.
13///
14/// `MakeWriter` is used by [`fmt::Collector`] or [`fmt::Subscriber`] to print
15/// formatted text representations of [`Event`]s.
16///
17/// This trait is already implemented for function pointers and
18/// immutably-borrowing closures that return an instance of [`io::Write`], such
19/// as [`io::stdout`] and [`io::stderr`]. Additionally, it is implemented for
20/// [`std::sync::Mutex`] when the type inside the mutex implements
21/// [`io::Write`].
22///
23/// The [`MakeWriter::make_writer_for`] method takes [`Metadata`] describing a
24/// span or event and returns a writer. `MakeWriter`s can optionally provide
25/// implementations of this method with behaviors that differ based on the span
26/// or event being written. For example, events at different [levels] might be
27/// written to different output streams, or data from different [targets] might
28/// be written to separate log files. When the `MakeWriter` has no custom
29/// behavior based on metadata, the default implementation of `make_writer_for`
30/// simply calls `self.make_writer()`, ignoring the metadata. Therefore, when
31/// metadata _is_ available, callers should prefer to call `make_writer_for`,
32/// passing in that metadata, so that the `MakeWriter` implementation can choose
33/// the appropriate behavior.
34///
35/// # Examples
36///
37/// The simplest usage is to pass in a named function that returns a writer. For
38/// example, to log all events to stderr, we could write:
39/// ```
40/// let subscriber = tracing_subscriber::fmt()
41/// .with_writer(std::io::stderr)
42/// .finish();
43/// # drop(subscriber);
44/// ```
45///
46/// Any function that returns a writer can be used:
47///
48/// ```
49/// fn make_my_great_writer() -> impl std::io::Write {
50/// // ...
51/// # std::io::stdout()
52/// }
53///
54/// let subscriber = tracing_subscriber::fmt()
55/// .with_writer(make_my_great_writer)
56/// .finish();
57/// # drop(subscriber);
58/// ```
59///
60/// A closure can be used to introduce arbitrary logic into how the writer is
61/// created. Consider the (admittedly rather silly) example of sending every 5th
62/// event to stderr, and all other events to stdout:
63///
64/// ```
65/// use std::io;
66/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
67///
68/// let n = AtomicUsize::new(0);
69/// let subscriber = tracing_subscriber::fmt()
70/// .with_writer(move || -> Box<dyn io::Write> {
71/// if n.fetch_add(1, Relaxed) % 5 == 0 {
72/// Box::new(io::stderr())
73/// } else {
74/// Box::new(io::stdout())
75/// }
76/// })
77/// .finish();
78/// # drop(subscriber);
79/// ```
80///
81/// A single instance of a type implementing [`io::Write`] may be used as a
82/// `MakeWriter` by wrapping it in a [`Mutex`]. For example, we could
83/// write to a file like so:
84///
85/// ```
86/// use std::{fs::File, sync::Mutex};
87///
88/// # fn docs() -> Result<(), Box<dyn std::error::Error>> {
89/// let log_file = File::create("my_cool_trace.log")?;
90/// let subscriber = tracing_subscriber::fmt()
91/// .with_writer(Mutex::new(log_file))
92/// .finish();
93/// # drop(subscriber);
94/// # Ok(())
95/// # }
96/// ```
97///
98/// [`io::Write`]: std::io::Write
99/// [`fmt::Collector`]: super::super::fmt::Collector
100/// [`fmt::Subscriber`]: super::super::fmt::Subscriber
101/// [`Event`]: tracing_core::event::Event
102/// [`io::stdout`]: std::io::stdout()
103/// [`io::stderr`]: std::io::stderr()
104/// [`MakeWriter::make_writer_for`]: MakeWriter::make_writer_for
105/// [`Metadata`]: tracing_core::Metadata
106/// [levels]: tracing_core::Level
107/// [targets]: tracing_core::Metadata::target
108pub trait MakeWriter<'a> {
109 /// The concrete [`io::Write`] implementation returned by [`make_writer`].
110 ///
111 /// [`io::Write`]: std::io::Write
112 /// [`make_writer`]: MakeWriter::make_writer
113 type Writer: io::Write;
114
115 /// Returns an instance of [`Writer`].
116 ///
117 /// # Implementer notes
118 ///
119 /// [`fmt::Subscriber`] or [`fmt::Collector`] will call this method each
120 /// time an event is recorded. Ensure any state that must be saved across
121 /// writes is not lost when the [`Writer`] instance is dropped. If creating
122 /// a [`io::Write`] instance is expensive, be sure to cache it when
123 /// implementing [`MakeWriter`] to improve performance.
124 ///
125 /// [`Writer`]: MakeWriter::Writer
126 /// [`fmt::Subscriber`]: super::super::fmt::Subscriber
127 /// [`fmt::Collector`]: super::super::fmt::Collector
128 /// [`io::Write`]: std::io::Write
129 fn make_writer(&'a self) -> Self::Writer;
130
131 /// Returns a [`Writer`] for writing data from the span or event described
132 /// by the provided [`Metadata`].
133 ///
134 /// By default, this calls [`self.make_writer()`][make_writer], ignoring
135 /// the provided metadata, but implementations can override this to provide
136 /// metadata-specific behaviors.
137 ///
138 /// This method allows `MakeWriter` implementations to implement different
139 /// behaviors based on the span or event being written. The `MakeWriter`
140 /// type might return different writers based on the provided metadata, or
141 /// might write some values to the writer before or after providing it to
142 /// the caller.
143 ///
144 /// For example, we might want to write data from spans and events at the
145 /// [`ERROR`] and [`WARN`] levels to `stderr`, and data from spans or events
146 /// at lower levels to stdout:
147 ///
148 /// ```
149 /// use std::io::{self, Stdout, Stderr, StdoutLock, StderrLock};
150 /// use tracing_subscriber::fmt::writer::MakeWriter;
151 /// use tracing_core::{Metadata, Level};
152 ///
153 /// pub struct MyMakeWriter {
154 /// stdout: Stdout,
155 /// stderr: Stderr,
156 /// }
157 ///
158 /// /// A lock on either stdout or stderr, depending on the verbosity level
159 /// /// of the event being written.
160 /// pub enum StdioLock<'a> {
161 /// Stdout(StdoutLock<'a>),
162 /// Stderr(StderrLock<'a>),
163 /// }
164 ///
165 /// impl<'a> io::Write for StdioLock<'a> {
166 /// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
167 /// match self {
168 /// StdioLock::Stdout(lock) => lock.write(buf),
169 /// StdioLock::Stderr(lock) => lock.write(buf),
170 /// }
171 /// }
172 ///
173 /// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
174 /// // ...
175 /// # match self {
176 /// # StdioLock::Stdout(lock) => lock.write_all(buf),
177 /// # StdioLock::Stderr(lock) => lock.write_all(buf),
178 /// # }
179 /// }
180 ///
181 /// fn flush(&mut self) -> io::Result<()> {
182 /// // ...
183 /// # match self {
184 /// # StdioLock::Stdout(lock) => lock.flush(),
185 /// # StdioLock::Stderr(lock) => lock.flush(),
186 /// # }
187 /// }
188 /// }
189 ///
190 /// impl<'a> MakeWriter<'a> for MyMakeWriter {
191 /// type Writer = StdioLock<'a>;
192 ///
193 /// fn make_writer(&'a self) -> Self::Writer {
194 /// // We must have an implementation of `make_writer` that makes
195 /// // a "default" writer without any configuring metadata. Let's
196 /// // just return stdout in that case.
197 /// StdioLock::Stdout(self.stdout.lock())
198 /// }
199 ///
200 /// fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
201 /// // Here's where we can implement our special behavior. We'll
202 /// // check if the metadata's verbosity level is WARN or ERROR,
203 /// // and return stderr in that case.
204 /// if meta.level() <= &Level::WARN {
205 /// return StdioLock::Stderr(self.stderr.lock());
206 /// }
207 ///
208 /// // Otherwise, we'll return stdout.
209 /// StdioLock::Stdout(self.stdout.lock())
210 /// }
211 /// }
212 /// ```
213 ///
214 /// [`Writer`]: MakeWriter::Writer
215 /// [`Metadata`]: tracing_core::Metadata
216 /// [make_writer]: MakeWriter::make_writer
217 /// [`WARN`]: tracing_core::Level::WARN
218 /// [`ERROR`]: tracing_core::Level::ERROR
219 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
220 let _ = meta;
221 self.make_writer()
222 }
223}
224
225/// Extension trait adding combinators for working with types implementing
226/// [`MakeWriter`].
227///
228/// This is not intended to be implemented directly for user-defined
229/// [`MakeWriter`]s; instead, it should be imported when the desired methods are
230/// used.
231pub trait MakeWriterExt<'a>: MakeWriter<'a> {
232 /// Wraps `self` and returns a [`MakeWriter`] that will only write output
233 /// for events at or below the provided verbosity [`Level`]. For instance,
234 /// `Level::TRACE` is considered to be _more verbose` than `Level::INFO`.
235 ///
236 /// Events whose level is more verbose than `level` will be ignored, and no
237 /// output will be written.
238 ///
239 /// # Examples
240 ///
241 /// ```
242 /// use tracing::Level;
243 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
244 ///
245 /// // Construct a writer that outputs events to `stderr` only if the span or
246 /// // event's level is <= WARN (WARN and ERROR).
247 /// let mk_writer = std::io::stderr.with_max_level(Level::WARN);
248 ///
249 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
250 /// ```
251 ///
252 /// Writing the `ERROR` and `WARN` levels to `stderr`, and everything else
253 /// to `stdout`:
254 ///
255 /// ```
256 /// # use tracing::Level;
257 /// # use tracing_subscriber::fmt::writer::MakeWriterExt;
258 ///
259 /// let mk_writer = std::io::stderr
260 /// .with_max_level(Level::WARN)
261 /// .or_else(std::io::stdout);
262 ///
263 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
264 /// ```
265 ///
266 /// Writing the `ERROR` level to `stderr`, the `INFO` and `WARN` levels to
267 /// `stdout`, and the `INFO` and DEBUG` levels to a file:
268 ///
269 /// ```
270 /// # use tracing::Level;
271 /// # use tracing_subscriber::fmt::writer::MakeWriterExt;
272 /// use std::fs::File;
273 /// # // don't actually create the file when running the tests.
274 /// # fn docs() -> std::io::Result<()> {
275 /// let debug_log = File::create("debug.log")?;
276 ///
277 /// let mk_writer = std::io::stderr
278 /// .with_max_level(Level::ERROR)
279 /// .or_else(std::io::stdout
280 /// .with_max_level(Level::INFO)
281 /// .and(debug_log.with_max_level(Level::DEBUG))
282 /// );
283 ///
284 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
285 /// # Ok(()) }
286 /// ```
287 ///
288 /// [`Level`]: tracing_core::Level
289 /// [`io::Write`]: std::io::Write
290 fn with_max_level(self, level: tracing_core::Level) -> WithMaxLevel<Self>
291 where
292 Self: Sized,
293 {
294 WithMaxLevel::new(self, level)
295 }
296
297 /// Wraps `self` and returns a [`MakeWriter`] that will only write output
298 /// for events at or above the provided verbosity [`Level`].
299 ///
300 /// Events whose level is less verbose than `level` will be ignored, and no
301 /// output will be written.
302 ///
303 /// # Examples
304 ///
305 /// ```
306 /// use tracing::Level;
307 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
308 ///
309 /// // Construct a writer that outputs events to `stdout` only if the span or
310 /// // event's level is >= DEBUG (DEBUG and TRACE).
311 /// let mk_writer = std::io::stdout.with_min_level(Level::DEBUG);
312 ///
313 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
314 /// ```
315 /// This can be combined with [`MakeWriterExt::with_max_level`] to write
316 /// only within a range of levels:
317 ///
318 /// ```
319 /// # use tracing::Level;
320 /// # use tracing_subscriber::fmt::writer::MakeWriterExt;
321 /// // Only write the `DEBUG` and `INFO` levels to stdout.
322 /// let mk_writer = std::io::stdout
323 /// .with_max_level(Level::DEBUG)
324 /// .with_min_level(Level::INFO)
325 /// // Write the `WARN` and `ERROR` levels to stderr.
326 /// .and(std::io::stderr.with_min_level(Level::WARN));
327 ///
328 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
329 /// ```
330 /// [`Level`]: tracing_core::Level
331 /// [`io::Write`]: std::io::Write
332 fn with_min_level(self, level: tracing_core::Level) -> WithMinLevel<Self>
333 where
334 Self: Sized,
335 {
336 WithMinLevel::new(self, level)
337 }
338
339 /// Wraps `self` with a predicate that takes a span or event's [`Metadata`]
340 /// and returns a `bool`. The returned [`MakeWriter`]'s
341 /// [`MakeWriter::make_writer_for`] method will check the predicate to
342 /// determine if a writer should be produced for a given span or event.
343 ///
344 /// If the predicate returns `false`, the wrapped [`MakeWriter`]'s
345 /// [`make_writer_for`][mwf] will return [`OptionalWriter::none`].
346 /// Otherwise, it calls the wrapped [`MakeWriter`]'s
347 /// [`make_writer_for`][mwf] method, and returns the produced writer.
348 ///
349 /// This can be used to filter an output based on arbitrary [`Metadata`]
350 /// parameters.
351 ///
352 /// # Examples
353 ///
354 /// Writing events with a specific target to an HTTP access log, and other
355 /// events to stdout:
356 ///
357 /// ```
358 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
359 /// use std::fs::File;
360 /// # // don't actually create the file when running the tests.
361 /// # fn docs() -> std::io::Result<()> {
362 /// let access_log = File::create("access.log")?;
363 ///
364 /// let mk_writer = access_log
365 /// // Only write events with the target "http::access_log" to the
366 /// // access log file.
367 /// .with_filter(|meta| meta.target() == "http::access_log")
368 /// // Write events with all other targets to stdout.
369 /// .or_else(std::io::stdout);
370 ///
371 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
372 /// # Ok(())
373 /// # }
374 /// ```
375 ///
376 /// Conditionally enabling or disabling a log file:
377 /// ```
378 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
379 /// use std::{sync::atomic::{AtomicBool, Ordering}, fs::File};
380 ///
381 /// static DEBUG_LOG_ENABLED: AtomicBool = AtomicBool::new(false);
382 ///
383 /// # // don't actually create the file when running the tests.
384 /// # fn docs() -> std::io::Result<()> {
385 /// // Create the debug log file
386 /// let debug_file = File::create("debug.log")?
387 /// // Enable the debug log only if the flag is enabled.
388 /// .with_filter(|_| DEBUG_LOG_ENABLED.load(Ordering::Acquire));
389 ///
390 /// // Always write to stdout
391 /// let mk_writer = std::io::stdout
392 /// // Write to the debug file if it's enabled
393 /// .and(debug_file);
394 ///
395 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
396 ///
397 /// // ...
398 ///
399 /// // Later, we can toggle on or off the debug log file.
400 /// DEBUG_LOG_ENABLED.store(true, Ordering::Release);
401 /// # Ok(())
402 /// # }
403 /// ```
404 ///
405 /// [`Metadata`]: tracing_core::Metadata
406 /// [mwf]: MakeWriter::make_writer_for
407 fn with_filter<F>(self, filter: F) -> WithFilter<Self, F>
408 where
409 Self: Sized,
410 F: Fn(&Metadata<'_>) -> bool,
411 {
412 WithFilter::new(self, filter)
413 }
414
415 /// Combines `self` with another type implementing [`MakeWriter`], returning
416 /// a new [`MakeWriter`] that produces [writers] that write to *both*
417 /// outputs.
418 ///
419 /// If writing to either writer returns an error, the returned writer will
420 /// return that error. However, both writers will still be written to before
421 /// the error is returned, so it is possible for one writer to fail while
422 /// the other is written to successfully.
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
428 ///
429 /// // Construct a writer that outputs events to `stdout` *and* `stderr`.
430 /// let mk_writer = std::io::stdout.and(std::io::stderr);
431 ///
432 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
433 /// ```
434 ///
435 /// `and` can be used in conjunction with filtering combinators. For
436 /// example, if we want to write to a number of outputs depending on
437 ///
438 /// [writers]: std::io::Write
439 fn and<B>(self, other: B) -> Tee<Self, B>
440 where
441 Self: Sized,
442 B: MakeWriter<'a> + Sized,
443 {
444 Tee::new(self, other)
445 }
446
447 /// Combines `self` with another type implementing [`MakeWriter`], returning
448 /// a new [`MakeWriter`] that calls `other`'s [`make_writer`] if `self`'s
449 /// `make_writer` returns [`OptionalWriter::none`].
450 ///
451 /// # Examples
452 ///
453 /// ```
454 /// use tracing::Level;
455 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
456 ///
457 /// // Produces a writer that writes to `stderr` if the level is <= WARN,
458 /// // or returns `OptionalWriter::none()` otherwise.
459 /// let stderr = std::io::stderr.with_max_level(Level::WARN);
460 ///
461 /// // If the `stderr` `MakeWriter` is disabled by the max level filter,
462 /// // write to stdout instead:
463 /// let mk_writer = stderr.or_else(std::io::stdout);
464 ///
465 /// tracing_subscriber::fmt().with_writer(mk_writer).init();
466 /// ```
467 ///
468 /// [`make_writer`]: MakeWriter::make_writer
469 fn or_else<W, B>(self, other: B) -> OrElse<Self, B>
470 where
471 Self: MakeWriter<'a, Writer = OptionalWriter<W>> + Sized,
472 B: MakeWriter<'a> + Sized,
473 W: Write,
474 {
475 OrElse::new(self, other)
476 }
477}
478
479/// A type implementing [`io::Write`] for a [`MutexGuard`] where the type
480/// inside the [`Mutex`] implements [`io::Write`].
481///
482/// This is used by the [`MakeWriter`] implementation for [`Mutex`], because
483/// [`MutexGuard`] itself will not implement [`io::Write`] β instead, it
484/// _dereferences_ to a type implementing [`io::Write`]. Because [`MakeWriter`]
485/// requires the `Writer` type to implement [`io::Write`], it's necessary to add
486/// a newtype that forwards the trait implementation.
487///
488/// [`MutexGuard`]: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html
489/// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
490/// [`MakeWriter`]: trait.MakeWriter.html
491#[derive(Debug)]
492pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>);
493
494/// A writer intended to support [`libtest`'s output capturing][capturing] for use in unit tests.
495///
496/// `TestWriter` is used by [`fmt::Collector`] or [`fmt::Subscriber`] to enable capturing support.
497///
498/// `cargo test` can only capture output from the standard library's [`print!`] and [`eprint!`]
499/// macros. See [`libtest`'s output capturing][capturing] and
500/// [rust-lang/rust#90785](https://github.com/rust-lang/rust/issues/90785) for more details about
501/// output capturing.
502///
503/// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using
504/// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable.
505///
506/// [`fmt::Collector`]: super::Collector
507/// [`fmt::Subscriber`]: super::Subscriber
508/// [capturing]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
509/// [nocapture]: https://doc.rust-lang.org/cargo/commands/cargo-test.html
510/// [`io::stdout`]: std::io::stdout()
511/// [`io::stderr`]: std::io::stderr()
512/// [`print!`]: std::print!
513#[derive(Default, Debug)]
514pub struct TestWriter {
515 /// Whether or not to use `stderr` instead of the default `stdout` as
516 /// the underlying stream to write to.
517 use_stderr: bool,
518}
519
520/// A writer that erases the specific [`io::Write`] and [`MakeWriter`] types being used.
521///
522/// This is useful in cases where the concrete type of the writer cannot be known
523/// until runtime.
524///
525/// # Examples
526///
527/// A function that returns a [`Collect`] that will write to either stdout or stderr:
528///
529/// ```rust
530/// # use tracing::Collect;
531/// # use tracing_subscriber::fmt::writer::BoxMakeWriter;
532///
533/// fn dynamic_writer(use_stderr: bool) -> impl Collect {
534/// let writer = if use_stderr {
535/// BoxMakeWriter::new(std::io::stderr)
536/// } else {
537/// BoxMakeWriter::new(std::io::stdout)
538/// };
539///
540/// tracing_subscriber::fmt().with_writer(writer).finish()
541/// }
542/// ```
543///
544/// [`Collect`]: tracing::Collect
545pub struct BoxMakeWriter {
546 inner: Box<dyn for<'a> MakeWriter<'a, Writer = Box<dyn Write + 'a>> + Send + Sync>,
547 name: &'static str,
548}
549
550/// A [writer] that is one of two types implementing [`io::Write`].
551///
552/// This may be used by [`MakeWriter`] implementations that may conditionally
553/// return one of two writers.
554///
555/// [writer]: std::io::Write
556#[derive(Copy, Clone, Debug, Eq, PartialEq)]
557pub enum EitherWriter<A, B> {
558 /// A writer of type `A`.
559 A(A),
560 /// A writer of type `B`.
561 B(B),
562}
563
564/// A [writer] which may or may not be enabled.
565///
566/// This may be used by [`MakeWriter`] implementations that wish to
567/// conditionally enable or disable the returned writer based on a span or
568/// event's [`Metadata`].
569///
570/// [writer]: std::io::Write
571pub type OptionalWriter<T> = EitherWriter<T, std::io::Sink>;
572
573/// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans
574/// and events with metadata at or below a specified verbosity [`Level`].
575///
576/// This is returned by the [`MakeWriterExt::with_max_level] method. See the
577/// method documentation for details.
578///
579/// [writer]: std::io::Write
580/// [`Level`]: tracing_core::Level
581#[derive(Copy, Clone, Debug, Eq, PartialEq)]
582pub struct WithMaxLevel<M> {
583 make: M,
584 level: tracing_core::Level,
585}
586
587/// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans
588/// and events with metadata at or above a specified verbosity [`Level`].
589///
590/// This is returned by the [`MakeWriterExt::with_min_level] method. See the
591/// method documentation for details.
592///
593/// [writer]: std::io::Write
594/// [`Level`]: tracing_core::Level
595#[derive(Copy, Clone, Debug, Eq, PartialEq)]
596pub struct WithMinLevel<M> {
597 make: M,
598 level: tracing_core::Level,
599}
600
601/// A [`MakeWriter`] combinator that wraps a [`MakeWriter`] with a predicate for
602/// span and event [`Metadata`], so that the [`MakeWriter::make_writer_for`]
603/// method returns [`OptionalWriter::some`] when the predicate returns `true`,
604/// and [`OptionalWriter::none`] when the predicate returns `false`.
605///
606/// This is returned by the [`MakeWriterExt::with_filter`] method. See the
607/// method documentation for details.
608///
609/// [`Metadata`]: tracing_core::Metadata
610#[derive(Copy, Clone, Debug, Eq, PartialEq)]
611pub struct WithFilter<M, F> {
612 make: M,
613 filter: F,
614}
615
616/// Combines a [`MakeWriter`] that returns an [`OptionalWriter`] with another
617/// [`MakeWriter`], so that the second [`MakeWriter`] is used when the first
618/// [`MakeWriter`] returns [`OptionalWriter::none`].
619///
620/// This is returned by the [`MakeWriterExt::or_else] method. See the
621/// method documentation for details.
622#[derive(Copy, Clone, Debug, Eq, PartialEq)]
623pub struct OrElse<A, B> {
624 inner: A,
625 or_else: B,
626}
627
628/// Combines two types implementing [`MakeWriter`] (or [`std::io::Write`]) to
629/// produce a writer that writes to both [`MakeWriter`]'s returned writers.
630///
631/// This is returned by the [`MakeWriterExt::and`] method. See the method
632/// documentation for details.
633#[derive(Copy, Clone, Debug, Eq, PartialEq)]
634pub struct Tee<A, B> {
635 a: A,
636 b: B,
637}
638
639/// A bridge between `fmt::Write` and `io::Write`.
640///
641/// This is used by the timestamp formatting implementation for the `time`
642/// crate and by the JSON formatter. In both cases, this is needed because
643/// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a
644/// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s
645/// `format_into` methods expect an `io::Write`.
646#[cfg(any(feature = "json", feature = "time"))]
647pub(in crate::fmt) struct WriteAdaptor<'a> {
648 fmt_write: &'a mut dyn fmt::Write,
649}
650
651impl<'a, F, W> MakeWriter<'a> for F
652where
653 F: Fn() -> W,
654 W: io::Write,
655{
656 type Writer = W;
657
658 fn make_writer(&'a self) -> Self::Writer {
659 (self)()
660 }
661}
662
663impl<'a> MakeWriter<'a> for std::fs::File {
664 type Writer = &'a std::fs::File;
665 fn make_writer(&'a self) -> Self::Writer {
666 self
667 }
668}
669
670// === impl TestWriter ===
671
672impl TestWriter {
673 /// Returns a new `TestWriter` with the default configuration.
674 pub fn new() -> Self {
675 Self::default()
676 }
677
678 /// Returns a new `TestWriter` that writes to `stderr` instead of `stdout`.
679 pub fn with_stderr() -> Self {
680 Self {
681 use_stderr: true,
682 }
683 }
684}
685
686impl io::Write for TestWriter {
687 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
688 let out_str = String::from_utf8_lossy(buf);
689 if self.use_stderr {
690 eprint!("{}", out_str)
691 } else {
692 print!("{}", out_str)
693 }
694 Ok(buf.len())
695 }
696
697 fn flush(&mut self) -> io::Result<()> {
698 Ok(())
699 }
700}
701
702impl<'a> MakeWriter<'a> for TestWriter {
703 type Writer = Self;
704
705 fn make_writer(&'a self) -> Self::Writer {
706 Self::default()
707 }
708}
709
710// === impl BoxMakeWriter ===
711
712impl BoxMakeWriter {
713 /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`].
714 ///
715 pub fn new<M>(make_writer: M) -> Self
716 where
717 M: for<'a> MakeWriter<'a> + Send + Sync + 'static,
718 {
719 Self {
720 inner: Box::new(Boxed(make_writer)),
721 name: std::any::type_name::<M>(),
722 }
723 }
724}
725
726impl fmt::Debug for BoxMakeWriter {
727 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
728 f.debug_tuple("BoxMakeWriter")
729 .field(&format_args!("<{}>", self.name))
730 .finish()
731 }
732}
733
734impl<'a> MakeWriter<'a> for BoxMakeWriter {
735 type Writer = Box<dyn Write + 'a>;
736
737 fn make_writer(&'a self) -> Self::Writer {
738 self.inner.make_writer()
739 }
740
741 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
742 self.inner.make_writer_for(meta)
743 }
744}
745
746struct Boxed<M>(M);
747
748impl<'a, M> MakeWriter<'a> for Boxed<M>
749where
750 M: MakeWriter<'a>,
751{
752 type Writer = Box<dyn Write + 'a>;
753
754 fn make_writer(&'a self) -> Self::Writer {
755 let w = self.0.make_writer();
756 Box::new(w)
757 }
758
759 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
760 let w = self.0.make_writer_for(meta);
761 Box::new(w)
762 }
763}
764
765// === impl Mutex/MutexGuardWriter ===
766
767impl<'a, W> MakeWriter<'a> for Mutex<W>
768where
769 W: io::Write + 'a,
770{
771 type Writer = MutexGuardWriter<'a, W>;
772
773 fn make_writer(&'a self) -> Self::Writer {
774 MutexGuardWriter(self.lock().expect("lock poisoned"))
775 }
776}
777
778impl<W> io::Write for MutexGuardWriter<'_, W>
779where
780 W: io::Write,
781{
782 #[inline]
783 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
784 self.0.write(buf)
785 }
786
787 #[inline]
788 fn flush(&mut self) -> io::Result<()> {
789 self.0.flush()
790 }
791
792 #[inline]
793 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
794 self.0.write_vectored(bufs)
795 }
796
797 #[inline]
798 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
799 self.0.write_all(buf)
800 }
801
802 #[inline]
803 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
804 self.0.write_fmt(fmt)
805 }
806}
807
808// === impl EitherWriter ===
809
810impl<A, B> io::Write for EitherWriter<A, B>
811where
812 A: io::Write,
813 B: io::Write,
814{
815 #[inline]
816 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
817 match self {
818 EitherWriter::A(a) => a.write(buf),
819 EitherWriter::B(b) => b.write(buf),
820 }
821 }
822
823 #[inline]
824 fn flush(&mut self) -> io::Result<()> {
825 match self {
826 EitherWriter::A(a) => a.flush(),
827 EitherWriter::B(b) => b.flush(),
828 }
829 }
830
831 #[inline]
832 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
833 match self {
834 EitherWriter::A(a) => a.write_vectored(bufs),
835 EitherWriter::B(b) => b.write_vectored(bufs),
836 }
837 }
838
839 #[inline]
840 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
841 match self {
842 EitherWriter::A(a) => a.write_all(buf),
843 EitherWriter::B(b) => b.write_all(buf),
844 }
845 }
846
847 #[inline]
848 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
849 match self {
850 EitherWriter::A(a) => a.write_fmt(fmt),
851 EitherWriter::B(b) => b.write_fmt(fmt),
852 }
853 }
854}
855
856impl<T> OptionalWriter<T> {
857 /// Returns a [disabled writer].
858 ///
859 /// Any bytes written to the returned writer are discarded.
860 ///
861 /// This is equivalent to returning [`Option::None`].
862 ///
863 /// [disabled writer]: std::io::sink
864 #[inline]
865 pub fn none() -> Self {
866 EitherWriter::B(std::io::sink())
867 }
868
869 /// Returns an enabled writer of type `T`.
870 ///
871 /// This is equivalent to returning [`Option::Some`].
872 #[inline]
873 pub fn some(t: T) -> Self {
874 EitherWriter::A(t)
875 }
876}
877
878impl<T> From<Option<T>> for OptionalWriter<T> {
879 #[inline]
880 fn from(opt: Option<T>) -> Self {
881 match opt {
882 Some(writer) => Self::some(writer),
883 None => Self::none(),
884 }
885 }
886}
887
888// === impl WithMaxLevel ===
889
890impl<M> WithMaxLevel<M> {
891 /// Wraps the provided [`MakeWriter`] with a maximum [`Level`], so that it
892 /// returns [`OptionalWriter::none`] for spans and events whose level is
893 /// more verbose than the maximum level.
894 ///
895 /// See [`MakeWriterExt::with_max_level`] for details.
896 ///
897 /// [`Level`]: tracing_core::Level
898 pub fn new(make: M, level: tracing_core::Level) -> Self {
899 Self { make, level }
900 }
901}
902
903impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMaxLevel<M> {
904 type Writer = OptionalWriter<M::Writer>;
905
906 #[inline]
907 fn make_writer(&'a self) -> Self::Writer {
908 // If we don't know the level, assume it's disabled.
909 OptionalWriter::none()
910 }
911
912 #[inline]
913 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
914 if meta.level() <= &self.level {
915 return OptionalWriter::some(self.make.make_writer_for(meta));
916 }
917 OptionalWriter::none()
918 }
919}
920
921// === impl WithMinLevel ===
922
923impl<M> WithMinLevel<M> {
924 /// Wraps the provided [`MakeWriter`] with a minimum [`Level`], so that it
925 /// returns [`OptionalWriter::none`] for spans and events whose level is
926 /// less verbose than the maximum level.
927 ///
928 /// See [`MakeWriterExt::with_min_level`] for details.
929 ///
930 /// [`Level`]: tracing_core::Level
931 pub fn new(make: M, level: tracing_core::Level) -> Self {
932 Self { make, level }
933 }
934}
935
936impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMinLevel<M> {
937 type Writer = OptionalWriter<M::Writer>;
938
939 #[inline]
940 fn make_writer(&'a self) -> Self::Writer {
941 // If we don't know the level, assume it's disabled.
942 OptionalWriter::none()
943 }
944
945 #[inline]
946 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
947 if meta.level() >= &self.level {
948 return OptionalWriter::some(self.make.make_writer_for(meta));
949 }
950 OptionalWriter::none()
951 }
952}
953
954// ==== impl WithFilter ===
955
956impl<M, F> WithFilter<M, F> {
957 /// Wraps `make` with the provided `filter`, returning a [`MakeWriter`] that
958 /// will call `make.make_writer_for()` when `filter` returns `true` for a
959 /// span or event's [`Metadata`], and returns a [`sink`] otherwise.
960 ///
961 /// See [`MakeWriterExt::with_filter`] for details.
962 ///
963 /// [`Metadata`]: tracing_core::Metadata
964 /// [`sink`]: std::io::sink
965 pub fn new(make: M, filter: F) -> Self
966 where
967 F: Fn(&Metadata<'_>) -> bool,
968 {
969 Self { make, filter }
970 }
971}
972
973impl<'a, M, F> MakeWriter<'a> for WithFilter<M, F>
974where
975 M: MakeWriter<'a>,
976 F: Fn(&Metadata<'_>) -> bool,
977{
978 type Writer = OptionalWriter<M::Writer>;
979
980 #[inline]
981 fn make_writer(&'a self) -> Self::Writer {
982 OptionalWriter::some(self.make.make_writer())
983 }
984
985 #[inline]
986 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
987 if (self.filter)(meta) {
988 OptionalWriter::some(self.make.make_writer_for(meta))
989 } else {
990 OptionalWriter::none()
991 }
992 }
993}
994
995// === impl Tee ===
996
997impl<A, B> Tee<A, B> {
998 /// Combines two types implementing [`MakeWriter`], returning
999 /// a new [`MakeWriter`] that produces [writers] that write to *both*
1000 /// outputs.
1001 ///
1002 /// See the documentation for [`MakeWriterExt::and`] for details.
1003 ///
1004 /// [writers]: std::io::Write
1005 pub fn new(a: A, b: B) -> Self {
1006 Self { a, b }
1007 }
1008}
1009
1010impl<'a, A, B> MakeWriter<'a> for Tee<A, B>
1011where
1012 A: MakeWriter<'a>,
1013 B: MakeWriter<'a>,
1014{
1015 type Writer = Tee<A::Writer, B::Writer>;
1016
1017 #[inline]
1018 fn make_writer(&'a self) -> Self::Writer {
1019 Tee::new(self.a.make_writer(), self.b.make_writer())
1020 }
1021
1022 #[inline]
1023 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
1024 Tee::new(self.a.make_writer_for(meta), self.b.make_writer_for(meta))
1025 }
1026}
1027
1028macro_rules! impl_tee {
1029 ($self_:ident.$f:ident($($arg:ident),*)) => {
1030 {
1031 let res_a = $self_.a.$f($($arg),*);
1032 let res_b = $self_.b.$f($($arg),*);
1033 (res_a?, res_b?)
1034 }
1035 }
1036}
1037
1038impl<A, B> io::Write for Tee<A, B>
1039where
1040 A: io::Write,
1041 B: io::Write,
1042{
1043 #[inline]
1044 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1045 let (a, b) = impl_tee!(self.write(buf));
1046 Ok(std::cmp::max(a, b))
1047 }
1048
1049 #[inline]
1050 fn flush(&mut self) -> io::Result<()> {
1051 impl_tee!(self.flush());
1052 Ok(())
1053 }
1054
1055 #[inline]
1056 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
1057 let (a, b) = impl_tee!(self.write_vectored(bufs));
1058 Ok(std::cmp::max(a, b))
1059 }
1060
1061 #[inline]
1062 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
1063 impl_tee!(self.write_all(buf));
1064 Ok(())
1065 }
1066
1067 #[inline]
1068 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
1069 impl_tee!(self.write_fmt(fmt));
1070 Ok(())
1071 }
1072}
1073
1074// === impl OrElse ===
1075
1076impl<A, B> OrElse<A, B> {
1077 /// Combines
1078 pub fn new<'a, W>(inner: A, or_else: B) -> Self
1079 where
1080 A: MakeWriter<'a, Writer = OptionalWriter<W>>,
1081 B: MakeWriter<'a>,
1082 W: Write,
1083 {
1084 Self { inner, or_else }
1085 }
1086}
1087
1088impl<'a, A, B, W> MakeWriter<'a> for OrElse<A, B>
1089where
1090 A: MakeWriter<'a, Writer = OptionalWriter<W>>,
1091 B: MakeWriter<'a>,
1092 W: io::Write,
1093{
1094 type Writer = EitherWriter<W, B::Writer>;
1095
1096 #[inline]
1097 fn make_writer(&'a self) -> Self::Writer {
1098 match self.inner.make_writer() {
1099 EitherWriter::A(writer) => EitherWriter::A(writer),
1100 EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer()),
1101 }
1102 }
1103
1104 #[inline]
1105 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
1106 match self.inner.make_writer_for(meta) {
1107 EitherWriter::A(writer) => EitherWriter::A(writer),
1108 EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer_for(meta)),
1109 }
1110 }
1111}
1112
1113// === impl WriteAdaptor ===
1114
1115#[cfg(any(feature = "json", feature = "time"))]
1116impl<'a> WriteAdaptor<'a> {
1117 pub(in crate::fmt) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
1118 Self { fmt_write }
1119 }
1120}
1121#[cfg(any(feature = "json", feature = "time"))]
1122impl io::Write for WriteAdaptor<'_> {
1123 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1124 let s =
1125 std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1126
1127 self.fmt_write
1128 .write_str(s)
1129 .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
1130
1131 Ok(s.len())
1132 }
1133
1134 fn flush(&mut self) -> io::Result<()> {
1135 Ok(())
1136 }
1137}
1138
1139#[cfg(any(feature = "json", feature = "time"))]
1140impl fmt::Debug for WriteAdaptor<'_> {
1141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1142 f.pad("WriteAdaptor { .. }")
1143 }
1144}
1145// === blanket impls ===
1146
1147impl<'a, M> MakeWriterExt<'a> for M where M: MakeWriter<'a> {}
1148#[cfg(test)]
1149mod test {
1150 use super::*;
1151 use crate::fmt::format::Format;
1152 use crate::fmt::test::{MockMakeWriter, MockWriter};
1153 use crate::fmt::Collector;
1154 use std::sync::atomic::{AtomicBool, Ordering};
1155 use std::sync::{Arc, Mutex};
1156 use tracing::{debug, error, info, trace, warn, Level};
1157 use tracing_core::dispatch::{self, Dispatch};
1158
1159 fn test_writer<T>(make_writer: T, msg: &str, buf: &Mutex<Vec<u8>>)
1160 where
1161 T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
1162 {
1163 let subscriber = {
1164 #[cfg(feature = "ansi")]
1165 let f = Format::default().without_time().with_ansi(false);
1166 #[cfg(not(feature = "ansi"))]
1167 let f = Format::default().without_time();
1168 Collector::builder()
1169 .event_format(f)
1170 .with_writer(make_writer)
1171 .finish()
1172 };
1173 let dispatch = Dispatch::from(subscriber);
1174
1175 dispatch::with_default(&dispatch, || {
1176 error!("{}", msg);
1177 });
1178
1179 let expected = format!("ERROR {}: {}\n", module_path!(), msg);
1180 let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
1181 assert!(actual.contains(expected.as_str()));
1182 }
1183
1184 fn has_lines(buf: &Mutex<Vec<u8>>, msgs: &[(tracing::Level, &str)]) {
1185 let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
1186 let mut expected_lines = msgs.iter();
1187 for line in actual.lines() {
1188 let line = dbg!(line).trim();
1189 let (level, msg) = expected_lines
1190 .next()
1191 .unwrap_or_else(|| panic!("expected no more lines, but got: {:?}", line));
1192 let expected = format!("{} {}: {}", level, module_path!(), msg);
1193 assert_eq!(line, expected.as_str());
1194 }
1195 }
1196
1197 #[test]
1198 fn custom_writer_closure() {
1199 let buf = Arc::new(Mutex::new(Vec::new()));
1200 let buf2 = buf.clone();
1201 let make_writer = move || MockWriter::new(buf2.clone());
1202 let msg = "my custom writer closure error";
1203 test_writer(make_writer, msg, &buf);
1204 }
1205
1206 #[test]
1207 fn custom_writer_struct() {
1208 let buf = Arc::new(Mutex::new(Vec::new()));
1209 let make_writer = MockMakeWriter::new(buf.clone());
1210 let msg = "my custom writer struct error";
1211 test_writer(make_writer, msg, &buf);
1212 }
1213
1214 #[test]
1215 fn custom_writer_mutex() {
1216 let buf = Arc::new(Mutex::new(Vec::new()));
1217 let writer = MockWriter::new(buf.clone());
1218 let make_writer = Mutex::new(writer);
1219 let msg = "my mutex writer error";
1220 test_writer(make_writer, msg, &buf);
1221 }
1222
1223 #[test]
1224 fn combinators_level_filters() {
1225 let info_buf = Arc::new(Mutex::new(Vec::new()));
1226 let info = MockMakeWriter::new(info_buf.clone());
1227
1228 let debug_buf = Arc::new(Mutex::new(Vec::new()));
1229 let debug = MockMakeWriter::new(debug_buf.clone());
1230
1231 let warn_buf = Arc::new(Mutex::new(Vec::new()));
1232 let warn = MockMakeWriter::new(warn_buf.clone());
1233
1234 let err_buf = Arc::new(Mutex::new(Vec::new()));
1235 let err = MockMakeWriter::new(err_buf.clone());
1236
1237 let make_writer = info
1238 .with_max_level(Level::INFO)
1239 .and(debug.with_max_level(Level::DEBUG))
1240 .and(warn.with_max_level(Level::WARN))
1241 .and(err.with_max_level(Level::ERROR));
1242
1243 let c = {
1244 #[cfg(feature = "ansi")]
1245 let f = Format::default().without_time().with_ansi(false);
1246 #[cfg(not(feature = "ansi"))]
1247 let f = Format::default().without_time();
1248 Collector::builder()
1249 .event_format(f)
1250 .with_writer(make_writer)
1251 .with_max_level(Level::TRACE)
1252 .finish()
1253 };
1254
1255 let _s = tracing::collect::set_default(c);
1256
1257 trace!("trace");
1258 debug!("debug");
1259 info!("info");
1260 warn!("warn");
1261 error!("error");
1262
1263 let all_lines = [
1264 (Level::TRACE, "trace"),
1265 (Level::DEBUG, "debug"),
1266 (Level::INFO, "info"),
1267 (Level::WARN, "warn"),
1268 (Level::ERROR, "error"),
1269 ];
1270
1271 println!("max level debug");
1272 has_lines(&debug_buf, &all_lines[1..]);
1273
1274 println!("max level info");
1275 has_lines(&info_buf, &all_lines[2..]);
1276
1277 println!("max level warn");
1278 has_lines(&warn_buf, &all_lines[3..]);
1279
1280 println!("max level error");
1281 has_lines(&err_buf, &all_lines[4..]);
1282 }
1283
1284 #[test]
1285 fn combinators_or_else() {
1286 let some_buf = Arc::new(Mutex::new(Vec::new()));
1287 let some = MockMakeWriter::new(some_buf.clone());
1288
1289 let or_else_buf = Arc::new(Mutex::new(Vec::new()));
1290 let or_else = MockMakeWriter::new(or_else_buf.clone());
1291
1292 let return_some = AtomicBool::new(true);
1293 let make_writer = move || {
1294 if return_some.swap(false, Ordering::Relaxed) {
1295 OptionalWriter::some(some.make_writer())
1296 } else {
1297 OptionalWriter::none()
1298 }
1299 };
1300 let make_writer = make_writer.or_else(or_else);
1301 let c = {
1302 #[cfg(feature = "ansi")]
1303 let f = Format::default().without_time().with_ansi(false);
1304 #[cfg(not(feature = "ansi"))]
1305 let f = Format::default().without_time();
1306 Collector::builder()
1307 .event_format(f)
1308 .with_writer(make_writer)
1309 .with_max_level(Level::TRACE)
1310 .finish()
1311 };
1312
1313 let _s = tracing::collect::set_default(c);
1314 info!("hello");
1315 info!("world");
1316 info!("goodbye");
1317
1318 has_lines(&some_buf, &[(Level::INFO, "hello")]);
1319 has_lines(
1320 &or_else_buf,
1321 &[(Level::INFO, "world"), (Level::INFO, "goodbye")],
1322 );
1323 }
1324
1325 #[test]
1326 fn combinators_or_else_chain() {
1327 let info_buf = Arc::new(Mutex::new(Vec::new()));
1328 let info = MockMakeWriter::new(info_buf.clone());
1329
1330 let debug_buf = Arc::new(Mutex::new(Vec::new()));
1331 let debug = MockMakeWriter::new(debug_buf.clone());
1332
1333 let warn_buf = Arc::new(Mutex::new(Vec::new()));
1334 let warn = MockMakeWriter::new(warn_buf.clone());
1335
1336 let err_buf = Arc::new(Mutex::new(Vec::new()));
1337 let err = MockMakeWriter::new(err_buf.clone());
1338
1339 let make_writer = err.with_max_level(Level::ERROR).or_else(
1340 warn.with_max_level(Level::WARN).or_else(
1341 info.with_max_level(Level::INFO)
1342 .or_else(debug.with_max_level(Level::DEBUG)),
1343 ),
1344 );
1345
1346 let c = {
1347 #[cfg(feature = "ansi")]
1348 let f = Format::default().without_time().with_ansi(false);
1349 #[cfg(not(feature = "ansi"))]
1350 let f = Format::default().without_time();
1351 Collector::builder()
1352 .event_format(f)
1353 .with_writer(make_writer)
1354 .with_max_level(Level::TRACE)
1355 .finish()
1356 };
1357
1358 let _s = tracing::collect::set_default(c);
1359
1360 trace!("trace");
1361 debug!("debug");
1362 info!("info");
1363 warn!("warn");
1364 error!("error");
1365
1366 println!("max level debug");
1367 has_lines(&debug_buf, &[(Level::DEBUG, "debug")]);
1368
1369 println!("max level info");
1370 has_lines(&info_buf, &[(Level::INFO, "info")]);
1371
1372 println!("max level warn");
1373 has_lines(&warn_buf, &[(Level::WARN, "warn")]);
1374
1375 println!("max level error");
1376 has_lines(&err_buf, &[(Level::ERROR, "error")]);
1377 }
1378
1379 #[test]
1380 fn combinators_and() {
1381 let a_buf = Arc::new(Mutex::new(Vec::new()));
1382 let a = MockMakeWriter::new(a_buf.clone());
1383
1384 let b_buf = Arc::new(Mutex::new(Vec::new()));
1385 let b = MockMakeWriter::new(b_buf.clone());
1386
1387 let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")];
1388
1389 let make_writer = a.and(b);
1390 let c = {
1391 #[cfg(feature = "ansi")]
1392 let f = Format::default().without_time().with_ansi(false);
1393 #[cfg(not(feature = "ansi"))]
1394 let f = Format::default().without_time();
1395 Collector::builder()
1396 .event_format(f)
1397 .with_writer(make_writer)
1398 .with_max_level(Level::TRACE)
1399 .finish()
1400 };
1401
1402 let _s = tracing::collect::set_default(c);
1403 info!("hello");
1404 info!("world");
1405
1406 has_lines(&a_buf, &lines[..]);
1407 has_lines(&b_buf, &lines[..]);
1408 }
1409}