Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subscriber: Impl TestWriter for correct capturing of logs by cargo test #938

Merged
merged 6 commits into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion tracing-subscriber/src/fmt/fmt_layer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
field::RecordFields,
fmt::{format, FormatEvent, FormatFields, MakeWriter},
fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter},
layer::{self, Context, Scope},
registry::{LookupSpan, SpanRef},
};
Expand Down Expand Up @@ -181,6 +181,38 @@ impl<S, N, E, W> Layer<S, N, E, W> {
_inner: self._inner,
}
}

/// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in
/// unit tests.
///
/// See [`TestWriter`] for additional details.
///
/// # Examples
///
/// Using [`TestWriter`] to let `cargo test` capture test output:
///
/// ```rust
/// use std::io;
/// use tracing_subscriber::fmt;
///
/// let layer = fmt::layer()
/// .with_test_writer();
/// # // this is necessary for type inference.
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
/// [capturing]:
/// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
/// [`TestWriter`]: writer/struct.TestWriter.html
pub fn with_test_writer(self) -> Layer<S, N, E, TestWriter> {
Layer {
fmt_fields: self.fmt_fields,
fmt_event: self.fmt_event,
fmt_span: self.fmt_span,
make_writer: TestWriter::default(),
_inner: self._inner,
}
}
}

impl<S, N, L, T, W> Layer<S, N, format::Format<L, T>, W>
Expand Down
33 changes: 32 additions & 1 deletion tracing-subscriber/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ use crate::{
pub use self::{
format::{format, FormatEvent, FormatFields},
time::time,
writer::MakeWriter,
writer::{MakeWriter, TestWriter},
};

/// A `Subscriber` that logs formatted representations of `tracing` events.
Expand Down Expand Up @@ -873,6 +873,37 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
inner: self.inner.with_writer(make_writer),
}
}

/// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in
/// unit tests.
///
/// See [`TestWriter`] for additional details.
///
/// # Examples
///
/// Using [`TestWriter`] to let `cargo test` capture test output. Note that we do not install it
/// globally as it may cause conflicts.
///
/// ```rust
/// use tracing_subscriber::fmt;
/// use tracing::subscriber;
///
/// subscriber::set_default(
/// fmt()
/// .with_test_writer()
/// .finish()
/// );
/// ```
///
/// [capturing]:
/// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
/// [`TestWriter`]: writer/struct.TestWriter.html
pub fn with_test_writer(self) -> SubscriberBuilder<N, E, F, TestWriter> {
SubscriberBuilder {
filter: self.filter,
inner: self.inner.with_writer(TestWriter::default()),
}
}
}

/// Install a global tracing subscriber that listens for events and
Expand Down
49 changes: 49 additions & 0 deletions tracing-subscriber/src/fmt/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,55 @@ where
}
}

/// 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`]: ../struct.Subscriber.html
/// [`fmt::Layer`]: ../struct.Layer.html
/// [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`]: https://doc.rust-lang.org/std/io/fn.stdout.html
/// [`io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
/// [`print!`]: https://doc.rust-lang.org/std/macro.print.html
#[derive(Default, Debug)]
samrg472 marked this conversation as resolved.
Show resolved Hide resolved
pub struct TestWriter {
_p: (),
}

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 MakeWriter for TestWriter {
type Writer = Self;

fn make_writer(&self) -> Self::Writer {
Self::default()
}
}

#[cfg(test)]
mod test {
use super::MakeWriter;
Expand Down