//! Abstractions for creating [`io::Write`] instances. //! //! [`io::Write`]: std::io::Write use std::{ fmt, io::{self, Write}, sync::{Arc, Mutex, MutexGuard}, }; use tracing_core::Metadata; /// A type that can create [`io::Write`] instances. /// /// `MakeWriter` is used by [`fmt::Layer`] or [`fmt::Subscriber`] to print /// formatted text representations of [`Event`]s. /// /// This trait is already implemented for function pointers and /// immutably-borrowing closures that return an instance of [`io::Write`], such /// as [`io::stdout`] and [`io::stderr`]. Additionally, it is implemented for /// [`std::sync::Mutex`][mutex] when the tyoe inside the mutex implements /// [`io::Write`]. /// /// # Examples /// /// The simplest usage is to pass in a named function that returns a writer. For /// example, to log all events to stderr, we could write: /// ``` /// let subscriber = tracing_subscriber::fmt() /// .with_writer(std::io::stderr) /// .finish(); /// # drop(subscriber); /// ``` /// /// Any function that returns a writer can be used: /// /// ``` /// fn make_my_great_writer() -> impl std::io::Write { /// // ... /// # std::io::stdout() /// } /// /// let subscriber = tracing_subscriber::fmt() /// .with_writer(make_my_great_writer) /// .finish(); /// # drop(subscriber); /// ``` /// /// A closure can be used to introduce arbitrary logic into how the writer is /// created. Consider the (admittedly rather silly) example of sending every 5th /// event to stderr, and all other events to stdout: /// /// ``` /// use std::io; /// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// /// let n = AtomicUsize::new(0); /// let subscriber = tracing_subscriber::fmt() /// .with_writer(move || -> Box { /// if n.fetch_add(1, Relaxed) % 5 == 0 { /// Box::new(io::stderr()) /// } else { /// Box::new(io::stdout()) /// } /// }) /// .finish(); /// # drop(subscriber); /// ``` /// /// A single instance of a type implementing [`io::Write`] may be used as a /// `MakeWriter` by wrapping it in a [`Mutex`][mutex]. For example, we could /// write to a file like so: /// /// ``` /// use std::{fs::File, sync::Mutex}; /// /// # fn docs() -> Result<(), Box> { /// let log_file = File::create("my_cool_trace.log")?; /// let subscriber = tracing_subscriber::fmt() /// .with_writer(Mutex::new(log_file)) /// .finish(); /// # drop(subscriber); /// # Ok(()) /// # } /// ``` /// /// [`io::Write`]: std::io::Write /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber /// [`Event`]: tracing_core::event::Event /// [`io::stdout`]: std::io::stdout() /// [`io::stderr`]: std::io::stderr() /// [mutex]: std::sync::Mutex /// [`MakeWriter::make_writer_for`]: MakeWriter::make_writer_for /// [`Metadata`]: tracing_core::Metadata /// [levels]: tracing_core::Level /// [targets]: tracing_core::Metadata::target pub trait MakeWriter<'a> { /// The concrete [`io::Write`] implementation returned by [`make_writer`]. /// /// [`io::Write`]: std::io::Write /// [`make_writer`]: MakeWriter::make_writer type Writer: io::Write; /// Returns an instance of [`Writer`]. /// /// # Implementer notes /// /// [`fmt::Layer`] or [`fmt::Subscriber`] will call this method each time an event is recorded. Ensure any state /// that must be saved across writes is not lost when the [`Writer`] instance is dropped. If /// creating a [`io::Write`] instance is expensive, be sure to cache it when implementing /// [`MakeWriter`] to improve performance. /// /// [`Writer`]: MakeWriter::Writer /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber /// [`io::Write`]: std::io::Write fn make_writer(&'a self) -> Self::Writer; /// Returns a [`Writer`] for writing data from the span or event described /// by the provided [`Metadata`]. /// /// By default, this calls [`self.make_writer()`][make_writer], ignoring /// the provided metadata, but implementations can override this to provide /// metadata-specific behaviors. /// /// This method allows `MakeWriter` implementations to implement different /// behaviors based on the span or event being written. The `MakeWriter` /// type might return different writers based on the provided metadata, or /// might write some values to the writer before or after providing it to /// the caller. /// /// For example, we might want to write data from spans and events at the /// [`ERROR`] and [`WARN`] levels to `stderr`, and data from spans or events /// at lower levels to stdout: /// /// ``` /// use std::io::{self, Stdout, Stderr, StdoutLock, StderrLock}; /// use tracing_subscriber::fmt::writer::MakeWriter; /// use tracing_core::{Metadata, Level}; /// /// pub struct MyMakeWriter { /// stdout: Stdout, /// stderr: Stderr, /// } /// /// /// A lock on either stdout or stderr, depending on the verbosity level /// /// of the event being written. /// pub enum StdioLock<'a> { /// Stdout(StdoutLock<'a>), /// Stderr(StderrLock<'a>), /// } /// /// impl<'a> io::Write for StdioLock<'a> { /// fn write(&mut self, buf: &[u8]) -> io::Result { /// match self { /// StdioLock::Stdout(lock) => lock.write(buf), /// StdioLock::Stderr(lock) => lock.write(buf), /// } /// } /// /// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { /// // ... /// # match self { /// # StdioLock::Stdout(lock) => lock.write_all(buf), /// # StdioLock::Stderr(lock) => lock.write_all(buf), /// # } /// } /// /// fn flush(&mut self) -> io::Result<()> { /// // ... /// # match self { /// # StdioLock::Stdout(lock) => lock.flush(), /// # StdioLock::Stderr(lock) => lock.flush(), /// # } /// } /// } /// /// impl<'a> MakeWriter<'a> for MyMakeWriter { /// type Writer = StdioLock<'a>; /// /// fn make_writer(&'a self) -> Self::Writer { /// // We must have an implementation of `make_writer` that makes /// // a "default" writer without any configuring metadata. Let's /// // just return stdout in that case. /// StdioLock::Stdout(self.stdout.lock()) /// } /// /// fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { /// // Here's where we can implement our special behavior. We'll /// // check if the metadata's verbosity level is WARN or ERROR, /// // and return stderr in that case. /// if meta.level() <= &Level::WARN { /// return StdioLock::Stderr(self.stderr.lock()); /// } /// /// // Otherwise, we'll return stdout. /// StdioLock::Stdout(self.stdout.lock()) /// } /// } /// ``` /// /// [`Writer`]: MakeWriter::Writer /// [`Metadata`]: tracing_core::Metadata /// [make_writer]: MakeWriter::make_writer /// [`WARN`]: tracing_core::Level::WARN /// [`ERROR`]: tracing_core::Level::ERROR fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { let _ = meta; self.make_writer() } } /// Extension trait adding combinators for working with types implementing /// [`MakeWriter`]. /// /// This is not intended to be implemented directly for user-defined /// [`MakeWriter`]s; instead, it should be imported when the desired methods are /// used. pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// Wraps `self` and returns a [`MakeWriter`] that will only write output /// for events at or below the provided verbosity [`Level`]. For instance, /// `Level::TRACE` is considered to be _more verbose` than `Level::INFO`. /// /// Events whose level is more verbose than `level` will be ignored, and no /// output will be written. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stderr` only if the span or /// // event's level is >= WARN (WARN and ERROR). /// let mk_writer = std::io::stderr.with_max_level(Level::WARN); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// Writing the `ERROR` and `WARN` levels to `stderr`, and everything else /// to `stdout`: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// let mk_writer = std::io::stderr /// .with_max_level(Level::WARN) /// .or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// Writing the `ERROR` level to `stderr`, the `INFO` and `WARN` levels to /// `stdout`, and the `INFO` and DEBUG` levels to a file: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let debug_log = Arc::new(File::create("debug.log")?); /// /// let mk_writer = std::io::stderr /// .with_max_level(Level::ERROR) /// .or_else(std::io::stdout /// .with_max_level(Level::INFO) /// .and(debug_log.with_max_level(Level::DEBUG)) /// ); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) } /// ``` /// /// [`Level`]: tracing_core::Level /// [`io::Write`]: std::io::Write fn with_max_level(self, level: tracing_core::Level) -> WithMaxLevel where Self: Sized, { WithMaxLevel::new(self, level) } /// Wraps `self` and returns a [`MakeWriter`] that will only write output /// for events at or above the provided verbosity [`Level`]. /// /// Events whose level is less verbose than `level` will be ignored, and no /// output will be written. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stdout` only if the span or /// // event's level is <= DEBUG (DEBUG and TRACE). /// let mk_writer = std::io::stdout.with_min_level(Level::DEBUG); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// This can be combined with [`MakeWriterExt::with_max_level`] to write /// only within a range of levels: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// // Only write the `DEBUG` and `INFO` levels to stdout. /// let mk_writer = std::io::stdout /// .with_max_level(Level::DEBUG) /// .with_min_level(Level::INFO) /// // Write the `WARN` and `ERROR` levels to stderr. /// .and(std::io::stderr.with_min_level(Level::WARN)); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// [`Level`]: tracing_core::Level /// [`io::Write`]: std::io::Write fn with_min_level(self, level: tracing_core::Level) -> WithMinLevel where Self: Sized, { WithMinLevel::new(self, level) } /// Wraps `self` with a predicate that takes a span or event's [`Metadata`] /// and returns a `bool`. The returned [`MakeWriter`]'s /// [`MakeWriter::make_writer_for`][mwf] method will check the predicate to /// determine if a writer should be produced for a given span or event. /// /// If the predicate returns `false`, the wrapped [`MakeWriter`]'s /// [`make_writer_for`][mwf] will return [`OptionalWriter::none`][own]. /// Otherwise, it calls the wrapped [`MakeWriter`]'s /// [`make_writer_for`][mwf] method, and returns the produced writer. /// /// This can be used to filter an output based on arbitrary [`Metadata`] /// parameters. /// /// # Examples /// /// Writing events with a specific target to an HTTP access log, and other /// events to stdout: /// /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let access_log = Arc::new(File::create("access.log")?); /// /// let mk_writer = access_log /// // Only write events with the target "http::access_log" to the /// // access log file. /// .with_filter(|meta| meta.target() == "http::access_log") /// // Write events with all other targets to stdout. /// .or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) /// # } /// ``` /// /// Conditionally enabling or disabling a log file: /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{ /// sync::{Arc, atomic::{AtomicBool, Ordering}}, /// fs::File, /// }; /// /// static DEBUG_LOG_ENABLED: AtomicBool = AtomicBool::new(false); /// /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// // Create the debug log file /// let debug_file = Arc::new(File::create("debug.log")?) /// // Enable the debug log only if the flag is enabled. /// .with_filter(|_| DEBUG_LOG_ENABLED.load(Ordering::Acquire)); /// /// // Always write to stdout /// let mk_writer = std::io::stdout /// // Write to the debug file if it's enabled /// .and(debug_file); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// /// // ... /// /// // Later, we can toggle on or off the debug log file. /// DEBUG_LOG_ENABLED.store(true, Ordering::Release); /// # Ok(()) /// # } /// ``` /// /// [`Metadata`]: tracing_core::Metadata /// [mwf]: MakeWriter::make_writer_for /// [own]: EitherWriter::none fn with_filter(self, filter: F) -> WithFilter where Self: Sized, F: Fn(&Metadata<'_>) -> bool, { WithFilter::new(self, filter) } /// Combines `self` with another type implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that produces [writers] that write to *both* /// outputs. /// /// If writing to either writer returns an error, the returned writer will /// return that error. However, both writers will still be written to before /// the error is returned, so it is possible for one writer to fail while /// the other is written to successfully. /// /// # Examples /// /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stdout` *and* `stderr`. /// let mk_writer = std::io::stdout.and(std::io::stderr); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// `and` can be used in conjunction with filtering combinators. For /// example, if we want to write to a number of outputs depending on the /// level of an event, we could write: /// /// ``` /// use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let debug_log = Arc::new(File::create("debug.log")?); /// /// // Write everything to the debug log. /// let mk_writer = debug_log /// // Write the `ERROR` and `WARN` levels to stderr. /// .and(std::io::stderr.with_max_level(Level::WARN)) /// // Write `INFO` to `stdout`. /// .and(std::io::stdout /// .with_max_level(Level::INFO) /// .with_min_level(Level::INFO) /// ); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) } /// ``` /// /// [writers]: std::io::Write fn and(self, other: B) -> Tee where Self: Sized, B: MakeWriter<'a> + Sized, { Tee::new(self, other) } /// Combines `self` with another type implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that calls `other`'s [`make_writer`] if `self`'s /// `make_writer` returns [`OptionalWriter::none`][own]. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Produces a writer that writes to `stderr` if the level is >= WARN, /// // or returns `OptionalWriter::none()` otherwise. /// let stderr = std::io::stderr.with_max_level(Level::WARN); /// /// // If the `stderr` `MakeWriter` is disabled by the max level filter, /// // write to stdout instead: /// let mk_writer = stderr.or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// [`make_writer`]: MakeWriter::make_writer /// [own]: EitherWriter::none fn or_else(self, other: B) -> OrElse where Self: MakeWriter<'a, Writer = OptionalWriter> + Sized, B: MakeWriter<'a> + Sized, W: Write, { OrElse::new(self, other) } } /// A writer intended to support [`libtest`'s output capturing][capturing] for use in unit tests. /// /// `TestWriter` is used by [`fmt::Subscriber`] or [`fmt::Layer`] to enable capturing support. /// /// `cargo test` can only capture output from the standard library's [`print!`] macro. See /// [`libtest`'s output capturing][capturing] for more details about output capturing. /// /// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using /// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable. /// /// [`fmt::Subscriber`]: super::Subscriber /// [`fmt::Layer`]: super::Layer /// [capturing]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output /// [nocapture]: https://doc.rust-lang.org/cargo/commands/cargo-test.html /// [`io::stdout`]: std::io::stdout /// [`io::stderr`]: std::io::stderr /// [`print!`]: std::print! #[derive(Default, Debug)] pub struct TestWriter { _p: (), } /// A writer that erases the specific [`io::Write`] and [`MakeWriter`] types being used. /// /// This is useful in cases where the concrete type of the writer cannot be known /// until runtime. /// /// # Examples /// /// A function that returns a [`Subscriber`] that will write to either stdout or stderr: /// /// ```rust /// # use tracing::Subscriber; /// # use tracing_subscriber::fmt::writer::BoxMakeWriter; /// /// fn dynamic_writer(use_stderr: bool) -> impl Subscriber { /// let writer = if use_stderr { /// BoxMakeWriter::new(std::io::stderr) /// } else { /// BoxMakeWriter::new(std::io::stdout) /// }; /// /// tracing_subscriber::fmt().with_writer(writer).finish() /// } /// ``` /// /// [`Subscriber`]: tracing::Subscriber /// [`io::Write`]: std::io::Write pub struct BoxMakeWriter { inner: Box MakeWriter<'a, Writer = Box> + Send + Sync>, name: &'static str, } /// A [writer] that is one of two types implementing [`io::Write`][writer]. /// /// This may be used by [`MakeWriter`] implementations that may conditionally /// return one of two writers. /// /// [writer]: std::io::Write #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum EitherWriter { /// A writer of type `A`. A(A), /// A writer of type `B`. B(B), } /// A [writer] which may or may not be enabled. /// /// This may be used by [`MakeWriter`] implementations that wish to /// conditionally enable or disable the returned writer based on a span or /// event's [`Metadata`]. /// /// [writer]: std::io::Write pub type OptionalWriter = EitherWriter; /// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans /// and events with metadata at or below a specified verbosity [`Level`]. /// /// This is returned by the [`MakeWriterExt::with_max_level`] method. See the /// method documentation for details. /// /// [writer]: std::io::Write /// [`Level`]: tracing_core::Level #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithMaxLevel { make: M, level: tracing_core::Level, } /// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans /// and events with metadata at or above a specified verbosity [`Level`]. /// /// This is returned by the [`MakeWriterExt::with_min_level`] method. See the /// method documentation for details. /// /// [writer]: std::io::Write /// [`Level`]: tracing_core::Level #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithMinLevel { make: M, level: tracing_core::Level, } /// A [`MakeWriter`] combinator that wraps a [`MakeWriter`] with a predicate for /// span and event [`Metadata`], so that the [`MakeWriter::make_writer_for`] /// method returns [`OptionalWriter::some`][ows] when the predicate returns `true`, /// and [`OptionalWriter::none`][own] when the predicate returns `false`. /// /// This is returned by the [`MakeWriterExt::with_filter`] method. See the /// method documentation for details. /// /// [`Metadata`]: tracing_core::Metadata /// [ows]: EitherWriter::some /// [own]: EitherWriter::none #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithFilter { make: M, filter: F, } /// Combines a [`MakeWriter`] that returns an [`OptionalWriter`] with another /// [`MakeWriter`], so that the second [`MakeWriter`] is used when the first /// [`MakeWriter`] returns [`OptionalWriter::none`][own]. /// /// This is returned by the [`MakeWriterExt::or_else] method. See the /// method documentation for details. /// /// [own]: EitherWriter::none #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct OrElse { inner: A, or_else: B, } /// Combines two types implementing [`MakeWriter`] (or [`std::io::Write`]) to /// produce a writer that writes to both [`MakeWriter`]'s returned writers. /// /// This is returned by the [`MakeWriterExt::and`] method. See the method /// documentation for details. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Tee { a: A, b: B, } /// A type implementing [`io::Write`] for a [`MutexGuard`] where the type /// inside the [`Mutex`] implements [`io::Write`]. /// /// This is used by the [`MakeWriter`] implementation for [`Mutex`], because /// [`MutexGuard`] itself will not implement [`io::Write`] — instead, it /// _dereferences_ to a type implementing [`io::Write`]. Because [`MakeWriter`] /// requires the `Writer` type to implement [`io::Write`], it's necessary to add /// a newtype that forwards the trait implementation. /// /// [`io::Write`]: std::io::Write /// [`MutexGuard`]: std::sync::MutexGuard /// [`Mutex`]: std::sync::Mutex #[derive(Debug)] pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>); /// Implements [`std::io::Write`] for an [`Arc`] where `&W: Write`. /// /// This is an implementation detail of the [`MakeWriter`] impl for [`Arc`]. #[derive(Clone, Debug)] pub struct ArcWriter(Arc); /// A bridge between `fmt::Write` and `io::Write`. /// /// This is used by the timestamp formatting implementation for the `time` /// crate and by the JSON formatter. In both cases, this is needed because /// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a /// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s /// `format_into` methods expect an `io::Write`. #[cfg(any(feature = "json", feature = "time"))] pub(in crate::fmt) struct WriteAdaptor<'a> { fmt_write: &'a mut dyn fmt::Write, } impl<'a, F, W> MakeWriter<'a> for F where F: Fn() -> W, W: io::Write, { type Writer = W; fn make_writer(&'a self) -> Self::Writer { (self)() } } impl<'a, W> MakeWriter<'a> for Arc where &'a W: io::Write + 'a, { type Writer = &'a W; fn make_writer(&'a self) -> Self::Writer { self } } impl<'a> MakeWriter<'a> for std::fs::File { type Writer = &'a std::fs::File; fn make_writer(&'a self) -> Self::Writer { self } } // === impl TestWriter === impl TestWriter { /// Returns a new `TestWriter` with the default configuration. pub fn new() -> Self { Self::default() } } impl io::Write for TestWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let out_str = String::from_utf8_lossy(buf); print!("{}", out_str); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl<'a> MakeWriter<'a> for TestWriter { type Writer = Self; fn make_writer(&'a self) -> Self::Writer { Self::default() } } // === impl BoxMakeWriter === impl BoxMakeWriter { /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`]. /// pub fn new(make_writer: M) -> Self where M: for<'a> MakeWriter<'a> + Send + Sync + 'static, { Self { inner: Box::new(Boxed(make_writer)), name: std::any::type_name::(), } } } impl fmt::Debug for BoxMakeWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxMakeWriter") .field(&format_args!("<{}>", self.name)) .finish() } } impl<'a> MakeWriter<'a> for BoxMakeWriter { type Writer = Box; #[inline] fn make_writer(&'a self) -> Self::Writer { self.inner.make_writer() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { self.inner.make_writer_for(meta) } } struct Boxed(M); impl<'a, M> MakeWriter<'a> for Boxed where M: MakeWriter<'a>, { type Writer = Box; fn make_writer(&'a self) -> Self::Writer { let w = self.0.make_writer(); Box::new(w) } fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { let w = self.0.make_writer_for(meta); Box::new(w) } } // === impl Mutex/MutexGuardWriter === impl<'a, W> MakeWriter<'a> for Mutex where W: io::Write + 'a, { type Writer = MutexGuardWriter<'a, W>; fn make_writer(&'a self) -> Self::Writer { MutexGuardWriter(self.lock().expect("lock poisoned")) } } impl<'a, W> io::Write for MutexGuardWriter<'a, W> where W: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } #[inline] fn flush(&mut self) -> io::Result<()> { self.0.flush() } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { self.0.write_vectored(bufs) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.0.write_all(buf) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { self.0.write_fmt(fmt) } } // === impl EitherWriter === impl io::Write for EitherWriter where A: io::Write, B: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { match self { EitherWriter::A(a) => a.write(buf), EitherWriter::B(b) => b.write(buf), } } #[inline] fn flush(&mut self) -> io::Result<()> { match self { EitherWriter::A(a) => a.flush(), EitherWriter::B(b) => b.flush(), } } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { match self { EitherWriter::A(a) => a.write_vectored(bufs), EitherWriter::B(b) => b.write_vectored(bufs), } } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { match self { EitherWriter::A(a) => a.write_all(buf), EitherWriter::B(b) => b.write_all(buf), } } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { match self { EitherWriter::A(a) => a.write_fmt(fmt), EitherWriter::B(b) => b.write_fmt(fmt), } } } impl OptionalWriter { /// Returns a [disabled writer]. /// /// Any bytes written to the returned writer are discarded. /// /// This is equivalent to returning [`Option::None`]. /// /// [disabled writer]: std::io::sink #[inline] pub fn none() -> Self { EitherWriter::B(std::io::sink()) } /// Returns an enabled writer of type `T`. /// /// This is equivalent to returning [`Option::Some`]. #[inline] pub fn some(t: T) -> Self { EitherWriter::A(t) } } impl From> for OptionalWriter { #[inline] fn from(opt: Option) -> Self { match opt { Some(writer) => Self::some(writer), None => Self::none(), } } } // === impl WithMaxLevel === impl WithMaxLevel { /// Wraps the provided [`MakeWriter`] with a maximum [`Level`], so that it /// returns [`OptionalWriter::none`] for spans and events whose level is /// more verbose than the maximum level. /// /// See [`MakeWriterExt::with_max_level`] for details. /// /// [`Level`]: tracing_core::Level pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMaxLevel { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() <= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } OptionalWriter::none() } } // === impl WithMinLevel === impl WithMinLevel { /// Wraps the provided [`MakeWriter`] with a minimum [`Level`], so that it /// returns [`OptionalWriter::none`] for spans and events whose level is /// less verbose than the maximum level. /// /// See [`MakeWriterExt::with_min_level`] for details. /// /// [`Level`]: tracing_core::Level pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMinLevel { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() >= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } OptionalWriter::none() } } // ==== impl WithFilter === impl WithFilter { /// Wraps `make` with the provided `filter`, returning a [`MakeWriter`] that /// will call `make.make_writer_for()` when `filter` returns `true` for a /// span or event's [`Metadata`], and returns a [`sink`] otherwise. /// /// See [`MakeWriterExt::with_filter`] for details. /// /// [`Metadata`]: tracing_core::Metadata /// [`sink`]: std::io::sink pub fn new(make: M, filter: F) -> Self where F: Fn(&Metadata<'_>) -> bool, { Self { make, filter } } } impl<'a, M, F> MakeWriter<'a> for WithFilter where M: MakeWriter<'a>, F: Fn(&Metadata<'_>) -> bool, { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { OptionalWriter::some(self.make.make_writer()) } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if (self.filter)(meta) { OptionalWriter::some(self.make.make_writer_for(meta)) } else { OptionalWriter::none() } } } // === impl Tee === impl Tee { /// Combines two types implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that produces [writers] that write to *both* /// outputs. /// /// See the documentation for [`MakeWriterExt::and`] for details. /// /// [writers]: std::io::Write pub fn new(a: A, b: B) -> Self { Self { a, b } } } impl<'a, A, B> MakeWriter<'a> for Tee where A: MakeWriter<'a>, B: MakeWriter<'a>, { type Writer = Tee; #[inline] fn make_writer(&'a self) -> Self::Writer { Tee::new(self.a.make_writer(), self.b.make_writer()) } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { Tee::new(self.a.make_writer_for(meta), self.b.make_writer_for(meta)) } } macro_rules! impl_tee { ($self_:ident.$f:ident($($arg:ident),*)) => { { let res_a = $self_.a.$f($($arg),*); let res_b = $self_.b.$f($($arg),*); (res_a?, res_b?) } } } impl io::Write for Tee where A: io::Write, B: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { let (a, b) = impl_tee!(self.write(buf)); Ok(std::cmp::max(a, b)) } #[inline] fn flush(&mut self) -> io::Result<()> { impl_tee!(self.flush()); Ok(()) } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { let (a, b) = impl_tee!(self.write_vectored(bufs)); Ok(std::cmp::max(a, b)) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { impl_tee!(self.write_all(buf)); Ok(()) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { impl_tee!(self.write_fmt(fmt)); Ok(()) } } // === impl OrElse === impl OrElse { /// Combines pub fn new<'a, W>(inner: A, or_else: B) -> Self where A: MakeWriter<'a, Writer = OptionalWriter>, B: MakeWriter<'a>, W: Write, { Self { inner, or_else } } } impl<'a, A, B, W> MakeWriter<'a> for OrElse where A: MakeWriter<'a, Writer = OptionalWriter>, B: MakeWriter<'a>, W: io::Write, { type Writer = EitherWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { match self.inner.make_writer() { EitherWriter::A(writer) => EitherWriter::A(writer), EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer()), } } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { match self.inner.make_writer_for(meta) { EitherWriter::A(writer) => EitherWriter::A(writer), EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer_for(meta)), } } } // === impl ArcWriter === impl io::Write for ArcWriter where for<'a> &'a W: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { (&*self.0).write(buf) } #[inline] fn flush(&mut self) -> io::Result<()> { (&*self.0).flush() } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { (&*self.0).write_vectored(bufs) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { (&*self.0).write_all(buf) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { (&*self.0).write_fmt(fmt) } } // === impl WriteAdaptor === #[cfg(any(feature = "json", feature = "time"))] impl<'a> WriteAdaptor<'a> { pub(in crate::fmt) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self { Self { fmt_write } } } #[cfg(any(feature = "json", feature = "time"))] impl<'a> io::Write for WriteAdaptor<'a> { fn write(&mut self, buf: &[u8]) -> io::Result { let s = std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; self.fmt_write .write_str(s) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; Ok(s.as_bytes().len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[cfg(any(feature = "json", feature = "time"))] impl<'a> fmt::Debug for WriteAdaptor<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("WriteAdaptor { .. }") } } // === blanket impls === impl<'a, M> MakeWriterExt<'a> for M where M: MakeWriter<'a> {} #[cfg(test)] mod test { use super::*; use crate::fmt::format::Format; use crate::fmt::test::{MockMakeWriter, MockWriter}; use crate::fmt::Subscriber; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use tracing::{debug, error, info, trace, warn, Level}; use tracing_core::dispatcher::{self, Dispatch}; fn test_writer(make_writer: T, msg: &str, buf: &Mutex>) where T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static, { let subscriber = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .finish() }; let dispatch = Dispatch::from(subscriber); dispatcher::with_default(&dispatch, || { error!("{}", msg); }); let expected = format!("ERROR {}: {}\n", module_path!(), msg); let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); assert!(actual.contains(expected.as_str())); } fn has_lines(buf: &Mutex>, msgs: &[(tracing::Level, &str)]) { let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); let mut expected_lines = msgs.iter(); for line in actual.lines() { let line = dbg!(line).trim(); let (level, msg) = expected_lines .next() .unwrap_or_else(|| panic!("expected no more lines, but got: {:?}", line)); let expected = format!("{} {}: {}", level, module_path!(), msg); assert_eq!(line, expected.as_str()); } } #[test] fn custom_writer_closure() { let buf = Arc::new(Mutex::new(Vec::new())); let buf2 = buf.clone(); let make_writer = move || MockWriter::new(buf2.clone()); let msg = "my custom writer closure error"; test_writer(make_writer, msg, &buf); } #[test] fn custom_writer_struct() { let buf = Arc::new(Mutex::new(Vec::new())); let make_writer = MockMakeWriter::new(buf.clone()); let msg = "my custom writer struct error"; test_writer(make_writer, msg, &buf); } #[test] fn custom_writer_mutex() { let buf = Arc::new(Mutex::new(Vec::new())); let writer = MockWriter::new(buf.clone()); let make_writer = Mutex::new(writer); let msg = "my mutex writer error"; test_writer(make_writer, msg, &buf); } #[test] fn combinators_level_filters() { let info_buf = Arc::new(Mutex::new(Vec::new())); let info = MockMakeWriter::new(info_buf.clone()); let debug_buf = Arc::new(Mutex::new(Vec::new())); let debug = MockMakeWriter::new(debug_buf.clone()); let warn_buf = Arc::new(Mutex::new(Vec::new())); let warn = MockMakeWriter::new(warn_buf.clone()); let err_buf = Arc::new(Mutex::new(Vec::new())); let err = MockMakeWriter::new(err_buf.clone()); let make_writer = info .with_max_level(Level::INFO) .and(debug.with_max_level(Level::DEBUG)) .and(warn.with_max_level(Level::WARN)) .and(err.with_max_level(Level::ERROR)); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); trace!("trace"); debug!("debug"); info!("info"); warn!("warn"); error!("error"); let all_lines = [ (Level::TRACE, "trace"), (Level::DEBUG, "debug"), (Level::INFO, "info"), (Level::WARN, "warn"), (Level::ERROR, "error"), ]; println!("max level debug"); has_lines(&debug_buf, &all_lines[1..]); println!("max level info"); has_lines(&info_buf, &all_lines[2..]); println!("max level warn"); has_lines(&warn_buf, &all_lines[3..]); println!("max level error"); has_lines(&err_buf, &all_lines[4..]); } #[test] fn combinators_or_else() { let some_buf = Arc::new(Mutex::new(Vec::new())); let some = MockMakeWriter::new(some_buf.clone()); let or_else_buf = Arc::new(Mutex::new(Vec::new())); let or_else = MockMakeWriter::new(or_else_buf.clone()); let return_some = AtomicBool::new(true); let make_writer = move || { if return_some.swap(false, Ordering::Relaxed) { OptionalWriter::some(some.make_writer()) } else { OptionalWriter::none() } }; let make_writer = make_writer.or_else(or_else); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); info!("hello"); info!("world"); info!("goodbye"); has_lines(&some_buf, &[(Level::INFO, "hello")]); has_lines( &or_else_buf, &[(Level::INFO, "world"), (Level::INFO, "goodbye")], ); } #[test] fn combinators_or_else_chain() { let info_buf = Arc::new(Mutex::new(Vec::new())); let info = MockMakeWriter::new(info_buf.clone()); let debug_buf = Arc::new(Mutex::new(Vec::new())); let debug = MockMakeWriter::new(debug_buf.clone()); let warn_buf = Arc::new(Mutex::new(Vec::new())); let warn = MockMakeWriter::new(warn_buf.clone()); let err_buf = Arc::new(Mutex::new(Vec::new())); let err = MockMakeWriter::new(err_buf.clone()); let make_writer = err.with_max_level(Level::ERROR).or_else( warn.with_max_level(Level::WARN).or_else( info.with_max_level(Level::INFO) .or_else(debug.with_max_level(Level::DEBUG)), ), ); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); trace!("trace"); debug!("debug"); info!("info"); warn!("warn"); error!("error"); println!("max level debug"); has_lines(&debug_buf, &[(Level::DEBUG, "debug")]); println!("max level info"); has_lines(&info_buf, &[(Level::INFO, "info")]); println!("max level warn"); has_lines(&warn_buf, &[(Level::WARN, "warn")]); println!("max level error"); has_lines(&err_buf, &[(Level::ERROR, "error")]); } #[test] fn combinators_and() { let a_buf = Arc::new(Mutex::new(Vec::new())); let a = MockMakeWriter::new(a_buf.clone()); let b_buf = Arc::new(Mutex::new(Vec::new())); let b = MockMakeWriter::new(b_buf.clone()); let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")]; let make_writer = a.and(b); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); info!("hello"); info!("world"); has_lines(&a_buf, &lines[..]); has_lines(&b_buf, &lines[..]); } }