Skip to content

Commit

Permalink
subscriber: Add MakeWriter impl for Option
Browse files Browse the repository at this point in the history
This adds a `MakeWriter` impl for `Option<M>` where `M: MakeWriter`.
This makes it possible to use `Option<MakeWriter>` in combination with
other `MakeWriter` implementations (e.g. `Tee`).

An example is found in #776,
where depending on the configuration, the user might want to use any or
all of `File`, `Stdout`, and `Stderr` writers.

```rust
let stdout = if enable_stdout { Some(std::io::stdout) } else { None };
let stderr = if enable_stderr { Some(std::io::stderr) } else { None };
let file = if let Some(path) { Some(File::create(path)?) } else { None };

let multi_make_writer = stdout.and(stderr).and(file);
```
  • Loading branch information
joshka committed Jan 16, 2025
1 parent b02a700 commit 4bcb352
Showing 1 changed file with 97 additions and 0 deletions.
97 changes: 97 additions & 0 deletions tracing-subscriber/src/fmt/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,27 @@ where
}
}

impl<'a, M> MakeWriter<'a> for Option<M>
where
M: MakeWriter<'a> + 'static,
{
type Writer = OptionalWriter<M::Writer>;

fn make_writer(&'a self) -> Self::Writer {
match self {
Some(inner) => OptionalWriter::some(inner.make_writer()),
None => OptionalWriter::none(),
}
}

fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
match self {
Some(inner) => OptionalWriter::some(inner.make_writer_for(meta)),
None => OptionalWriter::none(),
}
}
}

// === impl WriteAdaptor ===

#[cfg(any(feature = "json", feature = "time"))]
Expand Down Expand Up @@ -1391,4 +1412,80 @@ mod test {
has_lines(&a_buf, &lines[..]);
has_lines(&b_buf, &lines[..]);
}

#[test]
fn option_some_makewriter() {
let buf = Arc::new(Mutex::new(Vec::new()));
let make_writer = Some(MockMakeWriter::new(buf.clone()));

let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")];
let c = {
#[cfg(feature = "ansi")]
let f = Format::default().without_time().with_ansi(false);
#[cfg(not(feature = "ansi"))]
let f = Format::default().without_time();
Collector::builder()
.event_format(f)
.with_writer(make_writer)
.with_max_level(Level::TRACE)
.finish()
};
let _s = tracing::collect::set_default(c);
info!("hello");
info!("world");
has_lines(&buf, &lines[..]);
}

#[test]
fn option_none_makewriter() {
let make_writer = Option::<MockMakeWriter>::None;

let c = {
#[cfg(feature = "ansi")]
let f = Format::default().without_time().with_ansi(false);
#[cfg(not(feature = "ansi"))]
let f = Format::default().without_time();
Collector::builder()
.event_format(f)
.with_writer(make_writer)
.with_max_level(Level::TRACE)
.finish()
};
let _s = tracing::collect::set_default(c);
info!("hello");
info!("world");
}

#[test]
fn multi_tee() {
let always_buf = Arc::new(Mutex::new(Vec::new()));
let some_buf = Arc::new(Mutex::new(Vec::new()));

let always_make_writer = MockMakeWriter::new(always_buf.clone());
let some_make_writer = Some(MockMakeWriter::new(some_buf.clone()));
let none_make_writer = Option::<MockMakeWriter>::None;

let make_writer = always_make_writer
.and(some_make_writer)
.and(none_make_writer);

let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")];
let c = {
#[cfg(feature = "ansi")]
let f = Format::default().without_time().with_ansi(false);
#[cfg(not(feature = "ansi"))]
let f = Format::default().without_time();
Collector::builder()
.event_format(f)
.with_writer(make_writer)
.with_max_level(Level::TRACE)
.finish()
};
let _s = tracing::collect::set_default(c);
info!("hello");
info!("world");

has_lines(&always_buf, &lines[..]);
has_lines(&some_buf, &lines[..]);
}
}

0 comments on commit 4bcb352

Please sign in to comment.