πŸ›ˆ Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

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}