From 95e45750809e816348f70552c42ce55827c0681a Mon Sep 17 00:00:00 2001 From: Sri Ramanujam Date: Mon, 7 Sep 2020 14:55:59 -0400 Subject: [PATCH] subscriber: Added BoxMakeWriter. (#958) ## Motivation See context from https://github.com/tokio-rs/tracing/pull/776#discussion_r468056925. This should unblock the effort to land an appender that can write the same log line to multiple writers. ## Solution Implement `BoxMakeWriter` as suggested by @hawkw in the above thread. --- tracing-subscriber/src/fmt/writer.rs | 77 +++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs index 2b93726a32..d437e5f5dc 100644 --- a/tracing-subscriber/src/fmt/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -2,7 +2,8 @@ //! //! [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html -use std::io; +use io::Write; +use std::{fmt::Debug, io}; /// A type that can create [`io::Write`] instances. /// @@ -103,6 +104,80 @@ impl MakeWriter for TestWriter { } } +/// 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() +/// } +/// ``` +/// +/// [`MakeWriter`]: trait.MakeWriter.html +/// [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html +/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +pub struct BoxMakeWriter { + inner: Box> + Send + Sync>, +} + +impl BoxMakeWriter { + /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`]. + /// + /// [`MakeWriter`]: trait.MakeWriter.html + pub fn new(make_writer: M) -> Self + where + M: MakeWriter + Send + Sync + 'static, + M::Writer: Write + 'static, + { + Self { + inner: Box::new(Boxed(make_writer)), + } + } +} + +impl Debug for BoxMakeWriter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.pad("BoxMakeWriter { ... }") + } +} + +impl MakeWriter for BoxMakeWriter { + type Writer = Box; + + fn make_writer(&self) -> Self::Writer { + self.inner.make_writer() + } +} + +struct Boxed(M); + +impl MakeWriter for Boxed +where + M: MakeWriter, + M::Writer: Write + 'static, +{ + type Writer = Box; + + fn make_writer(&self) -> Self::Writer { + Box::new(self.0.make_writer()) + } +} + #[cfg(test)] mod test { use super::MakeWriter;