diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:12:43 +0000 |
commit | cf94bdc0742c13e2a0cac864c478b8626b266e1b (patch) | |
tree | 044670aa50cc5e2b4229aa0b6b3df6676730c0a6 /vendor/tracing-subscriber/src/fmt/writer.rs | |
parent | Adding debian version 1.65.0+dfsg1-2. (diff) | |
download | rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.tar.xz rustc-cf94bdc0742c13e2a0cac864c478b8626b266e1b.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tracing-subscriber/src/fmt/writer.rs')
-rw-r--r-- | vendor/tracing-subscriber/src/fmt/writer.rs | 1464 |
1 files changed, 0 insertions, 1464 deletions
diff --git a/vendor/tracing-subscriber/src/fmt/writer.rs b/vendor/tracing-subscriber/src/fmt/writer.rs deleted file mode 100644 index 4aacd6d54..000000000 --- a/vendor/tracing-subscriber/src/fmt/writer.rs +++ /dev/null @@ -1,1464 +0,0 @@ -//! 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<dyn io::Write> { -/// 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<dyn std::error::Error>> { -/// 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<usize> { - /// 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<Self> - 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<Self> - 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<F>(self, filter: F) -> WithFilter<Self, F> - 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<B>(self, other: B) -> Tee<Self, B> - 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<W, B>(self, other: B) -> OrElse<Self, B> - where - Self: MakeWriter<'a, Writer = OptionalWriter<W>> + 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<dyn for<'a> MakeWriter<'a, Writer = Box<dyn Write + 'a>> + 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, B> { - /// 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<T> = EitherWriter<T, std::io::Sink>; - -/// 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<M> { - 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<M> { - 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<M, F> { - 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<A, B> { - 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, B> { - 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`]<W> where `&W: Write`. -/// -/// This is an implementation detail of the [`MakeWriter`] impl for [`Arc`]. -#[derive(Clone, Debug)] -pub struct ArcWriter<W>(Arc<W>); - -/// 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<W> -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<usize> { - 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<M>(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::<M>(), - } - } -} - -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<dyn Write + 'a>; - - #[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>(M); - -impl<'a, M> MakeWriter<'a> for Boxed<M> -where - M: MakeWriter<'a>, -{ - type Writer = Box<dyn Write + 'a>; - - 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<W> -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<usize> { - 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<usize> { - 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<A, B> io::Write for EitherWriter<A, B> -where - A: io::Write, - B: io::Write, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - 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<usize> { - 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<T> OptionalWriter<T> { - /// 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<T> From<Option<T>> for OptionalWriter<T> { - #[inline] - fn from(opt: Option<T>) -> Self { - match opt { - Some(writer) => Self::some(writer), - None => Self::none(), - } - } -} - -// === impl WithMaxLevel === - -impl<M> WithMaxLevel<M> { - /// 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<M> { - type Writer = OptionalWriter<M::Writer>; - - #[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<M> WithMinLevel<M> { - /// 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<M> { - type Writer = OptionalWriter<M::Writer>; - - #[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<M, F> WithFilter<M, F> { - /// 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<M, F> -where - M: MakeWriter<'a>, - F: Fn(&Metadata<'_>) -> bool, -{ - type Writer = OptionalWriter<M::Writer>; - - #[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<A, B> Tee<A, B> { - /// 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<A, B> -where - A: MakeWriter<'a>, - B: MakeWriter<'a>, -{ - type Writer = Tee<A::Writer, B::Writer>; - - #[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<A, B> io::Write for Tee<A, B> -where - A: io::Write, - B: io::Write, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - 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<usize> { - 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<A, B> OrElse<A, B> { - /// Combines - pub fn new<'a, W>(inner: A, or_else: B) -> Self - where - A: MakeWriter<'a, Writer = OptionalWriter<W>>, - B: MakeWriter<'a>, - W: Write, - { - Self { inner, or_else } - } -} - -impl<'a, A, B, W> MakeWriter<'a> for OrElse<A, B> -where - A: MakeWriter<'a, Writer = OptionalWriter<W>>, - B: MakeWriter<'a>, - W: io::Write, -{ - type Writer = EitherWriter<W, B::Writer>; - - #[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<W> io::Write for ArcWriter<W> -where - for<'a> &'a W: io::Write, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - (&*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<usize> { - (&*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<usize> { - 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<T>(make_writer: T, msg: &str, buf: &Mutex<Vec<u8>>) - 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<Vec<u8>>, 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[..]); - } -} |