pub trait MakeWriter<'a> {
type Writer: Write;
// Required method
fn make_writer(&'a self) -> Self::Writer;
// Provided method
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { ... }
}
fmt
and std
only.Expand description
A type that can create io::Write
instances.
MakeWriter
is used by fmt::Collector
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
when the type inside the mutex implements
io::Write
.
The MakeWriter::make_writer_for
method takes Metadata
describing a
span or event and returns a writer. MakeWriter
s can optionally provide
implementations of this method with behaviors that differ based on the span
or event being written. For example, events at different levels might be
written to different output streams, or data from different targets might
be written to separate log files. When the MakeWriter
has no custom
behavior based on metadata, the default implementation of make_writer_for
simply calls self.make_writer()
, ignoring the metadata. Therefore, when
metadata is available, callers should prefer to call make_writer_for
,
passing in that metadata, so that the MakeWriter
implementation can choose
the appropriate behavior.
§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();
Any function that returns a writer can be used:
fn make_my_great_writer() -> impl std::io::Write {
// ...
}
let subscriber = tracing_subscriber::fmt()
.with_writer(make_my_great_writer)
.finish();
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();
A single instance of a type implementing io::Write
may be used as a
MakeWriter
by wrapping it in a Mutex
. For example, we could
write to a file like so:
use std::{fs::File, sync::Mutex};
let log_file = File::create("my_cool_trace.log")?;
let subscriber = tracing_subscriber::fmt()
.with_writer(Mutex::new(log_file))
.finish();
Required Associated Types§
Sourcetype Writer: Write
type Writer: Write
The concrete io::Write
implementation returned by make_writer
.
Required Methods§
Sourcefn make_writer(&'a self) -> Self::Writer
fn make_writer(&'a self) -> Self::Writer
Returns an instance of Writer
.
§Implementer notes
fmt::Subscriber
or fmt::Collector
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.
Provided Methods§
Sourcefn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> 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()
, 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<()> {
// ...
}
fn flush(&mut self) -> io::Result<()> {
// ...
}
}
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())
}
}