logo
pub trait MakeWriter {
    type Writer: Write;

    fn make_writer(&self) -> Self::Writer;

    fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { ... }
}
Expand description

A type that can create io::Write instances.

MakeWriter is used by fmt::Subscriber or [fmt::Layer] to print formatted text representations of Events.

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.

The MakeWriter::make_writer_for method takes Metadata describing a span or event and returns a writer. MakeWriters 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();

Required Associated Types

The concrete io::Write implementation returned by make_writer.

Required Methods

Returns an instance of Writer.

Implementer notes

fmt::Layer or fmt::Subscriber 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

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};
use tracing_subscriber::fmt::writer::MakeWriter;
use tracing_core::{Metadata, Level};

pub struct MyMakeWriter {}

/// A lock on either stdout or stderr, depending on the verbosity level
/// of the event being written.
pub enum Stdio {
    Stdout(Stdout),
    Stderr(Stderr),
}

impl io::Write for Stdio {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self {
            Stdio::Stdout(io) => io.write(buf),
            Stdio::Stderr(io) => io.write(buf),
        }
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        // ...
    }

    fn flush(&mut self) -> io::Result<()> {
        // ...
    }
}

impl MakeWriter for MyMakeWriter {
    type Writer = Stdio;

    fn make_writer(&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.
        Stdio::Stdout(io::stdout())
    }

    fn make_writer_for(&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 Stdio::Stderr(io::stderr());
        }

        // Otherwise, we'll return stdout.
        Stdio::Stdout(io::stdout())
    }
}

Implementations on Foreign Types

Implementors