Skip to content

Commit

Permalink
subscriber: add MakeWriter combinators (tokio-rs#1274)
Browse files Browse the repository at this point in the history
This backports tokio-rs#1274 from `master`.

Depends on tokio-rs#1141.

This branch adds a `MakeWriterExt` trait which adds a number of
combinators for composing multiple types implementing `MakeWriter`.
`MakeWriter`s can now be teed together, filtered with minimum and
maximum levels, filtered with a `Metadata` predicate, and combined with
a fallback for when a writer is _not_ made for a particular `Metadata`.

I've also added a `MakeWriter` impl for `Arc<W>` when `&W` implements
`Write`. This enables `File`s to be used as `MakeWriter`s similarly to
how we will be able to in 0.3, with the additional overhead of having to
do ref-counting because we can't return a reference from `MakeWriter`
until the next breaking change.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
  • Loading branch information
hawkw authored and kaffarell committed May 22, 2024
1 parent b73aef6 commit 3f3fa35
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 1 deletion.
191 changes: 191 additions & 0 deletions tracing-subscriber/src/fmt/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1391,4 +1391,195 @@ mod test {
has_lines(&a_buf, &lines[..]);
has_lines(&b_buf, &lines[..]);
}

#[test]
fn combinators_level_filters() {
lazy_static! {
static ref INFO_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref DEBUG_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref WARN_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref ERR_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

let info = MockMakeWriter::new(&INFO_BUF);
let debug = MockMakeWriter::new(&DEBUG_BUF);
let warn = MockMakeWriter::new(&WARN_BUF);
let err = MockMakeWriter::new(&ERR_BUF);

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() {
lazy_static! {
static ref SOME_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref OR_ELSE_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

let some = MockMakeWriter::new(&SOME_BUF);
let or_else = MockMakeWriter::new(&OR_ELSE_BUF);

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() {
lazy_static! {
static ref INFO_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref DEBUG_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref WARN_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref ERR_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

let info = MockMakeWriter::new(&INFO_BUF);
let debug = MockMakeWriter::new(&DEBUG_BUF);
let warn = MockMakeWriter::new(&WARN_BUF);
let err = MockMakeWriter::new(&ERR_BUF);

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() {
lazy_static! {
static ref A_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
static ref B_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

let a = MockMakeWriter::new(&A_BUF);
let b = MockMakeWriter::new(&B_BUF);

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[..]);
}
}
2 changes: 1 addition & 1 deletion tracing-subscriber/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ use std::{any::TypeId, marker::PhantomData};
///
/// Multiple `Layer`s may be composed in the same manner:
/// ```rust
/// # use tracing_subscriber::{Layer, SubscriberExt};
/// # use tracing_subscriber::{Layer, layer::SubscriberExt};
/// # use tracing::Subscriber;
/// pub struct MyOtherLayer {
/// // ...
Expand Down

0 comments on commit 3f3fa35

Please sign in to comment.