use std::{
fmt,
io::{self, Write},
sync::{Arc, Mutex, MutexGuard},
};
use tracing_core::Metadata;
pub trait MakeWriter<'a> {
type Writer: io::Write;
fn make_writer(&'a self) -> Self::Writer;
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
let _ = meta;
self.make_writer()
}
}
pub trait MakeWriterExt<'a>: MakeWriter<'a> {
fn with_max_level(self, level: tracing_core::Level) -> WithMaxLevel<Self>
where
Self: Sized,
{
WithMaxLevel::new(self, level)
}
fn with_min_level(self, level: tracing_core::Level) -> WithMinLevel<Self>
where
Self: Sized,
{
WithMinLevel::new(self, level)
}
fn with_filter<F>(self, filter: F) -> WithFilter<Self, F>
where
Self: Sized,
F: Fn(&Metadata<'_>) -> bool,
{
WithFilter::new(self, filter)
}
fn and<B>(self, other: B) -> Tee<Self, B>
where
Self: Sized,
B: MakeWriter<'a> + Sized,
{
Tee::new(self, other)
}
fn or_else<W, B>(self, other: B) -> OrElse<Self, B>
where
Self: MakeWriter<'a, Writer = OptionalWriter<W>> + Sized,
B: MakeWriter<'a> + Sized,
W: Write,
{
OrElse::new(self, other)
}
}
#[derive(Default, Debug)]
pub struct TestWriter {
_p: (),
}
pub struct BoxMakeWriter {
inner: Box<dyn for<'a> MakeWriter<'a, Writer = Box<dyn Write + 'a>> + Send + Sync>,
name: &'static str,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EitherWriter<A, B> {
A(A),
B(B),
}
pub type OptionalWriter<T> = EitherWriter<T, std::io::Sink>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WithMaxLevel<M> {
make: M,
level: tracing_core::Level,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WithMinLevel<M> {
make: M,
level: tracing_core::Level,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WithFilter<M, F> {
make: M,
filter: F,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct OrElse<A, B> {
inner: A,
or_else: B,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Tee<A, B> {
a: A,
b: B,
}
#[derive(Debug)]
pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>);
#[derive(Clone, Debug)]
pub struct ArcWriter<W>(Arc<W>);
#[cfg(any(feature = "json", feature = "time"))]
pub(in crate::fmt) struct WriteAdaptor<'a> {
fmt_write: &'a mut dyn fmt::Write,
}
impl<'a, F, W> MakeWriter<'a> for F
where
F: Fn() -> W,
W: io::Write,
{
type Writer = W;
fn make_writer(&'a self) -> Self::Writer {
(self)()
}
}
impl<'a, W> MakeWriter<'a> for Arc<W>
where
&'a W: io::Write + 'a,
{
type Writer = &'a W;
fn make_writer(&'a self) -> Self::Writer {
&*self
}
}
impl<'a> MakeWriter<'a> for std::fs::File {
type Writer = &'a std::fs::File;
fn make_writer(&'a self) -> Self::Writer {
self
}
}
impl TestWriter {
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<'a> MakeWriter<'a> for TestWriter {
type Writer = Self;
fn make_writer(&'a self) -> Self::Writer {
Self::default()
}
}
impl BoxMakeWriter {
pub fn new<M>(make_writer: M) -> Self
where
M: for<'a> MakeWriter<'a> + Send + Sync + 'static,
{
Self {
inner: Box::new(Boxed(make_writer)),
name: std::any::type_name::<M>(),
}
}
}
impl fmt::Debug for BoxMakeWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("BoxMakeWriter")
.field(&format_args!("<{}>", self.name))
.finish()
}
}
impl<'a> MakeWriter<'a> for BoxMakeWriter {
type Writer = Box<dyn Write + 'a>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
self.inner.make_writer()
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
self.inner.make_writer_for(meta)
}
}
struct Boxed<M>(M);
impl<'a, M> MakeWriter<'a> for Boxed<M>
where
M: MakeWriter<'a>,
{
type Writer = Box<dyn Write + 'a>;
fn make_writer(&'a self) -> Self::Writer {
let w = self.0.make_writer();
Box::new(w)
}
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
let w = self.0.make_writer_for(meta);
Box::new(w)
}
}
impl<'a, W> MakeWriter<'a> for Mutex<W>
where
W: io::Write + 'a,
{
type Writer = MutexGuardWriter<'a, W>;
fn make_writer(&'a self) -> Self::Writer {
MutexGuardWriter(self.lock().expect("lock poisoned"))
}
}
impl<'a, W> io::Write for MutexGuardWriter<'a, W>
where
W: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
}
#[inline]
fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
self.0.write_fmt(fmt)
}
}
impl<A, B> io::Write for EitherWriter<A, B>
where
A: io::Write,
B: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
EitherWriter::A(a) => a.write(buf),
EitherWriter::B(b) => b.write(buf),
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
match self {
EitherWriter::A(a) => a.flush(),
EitherWriter::B(b) => b.flush(),
}
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
match self {
EitherWriter::A(a) => a.write_vectored(bufs),
EitherWriter::B(b) => b.write_vectored(bufs),
}
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self {
EitherWriter::A(a) => a.write_all(buf),
EitherWriter::B(b) => b.write_all(buf),
}
}
#[inline]
fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
match self {
EitherWriter::A(a) => a.write_fmt(fmt),
EitherWriter::B(b) => b.write_fmt(fmt),
}
}
}
impl<T> OptionalWriter<T> {
#[inline]
pub fn none() -> Self {
EitherWriter::B(std::io::sink())
}
#[inline]
pub fn some(t: T) -> Self {
EitherWriter::A(t)
}
}
impl<T> From<Option<T>> for OptionalWriter<T> {
#[inline]
fn from(opt: Option<T>) -> Self {
match opt {
Some(writer) => Self::some(writer),
None => Self::none(),
}
}
}
impl<M> WithMaxLevel<M> {
pub fn new(make: M, level: tracing_core::Level) -> Self {
Self { make, level }
}
}
impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMaxLevel<M> {
type Writer = OptionalWriter<M::Writer>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
OptionalWriter::none()
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
if meta.level() <= &self.level {
return OptionalWriter::some(self.make.make_writer_for(meta));
}
OptionalWriter::none()
}
}
impl<M> WithMinLevel<M> {
pub fn new(make: M, level: tracing_core::Level) -> Self {
Self { make, level }
}
}
impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMinLevel<M> {
type Writer = OptionalWriter<M::Writer>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
OptionalWriter::none()
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
if meta.level() >= &self.level {
return OptionalWriter::some(self.make.make_writer_for(meta));
}
OptionalWriter::none()
}
}
impl<M, F> WithFilter<M, F> {
pub fn new(make: M, filter: F) -> Self
where
F: Fn(&Metadata<'_>) -> bool,
{
Self { make, filter }
}
}
impl<'a, M, F> MakeWriter<'a> for WithFilter<M, F>
where
M: MakeWriter<'a>,
F: Fn(&Metadata<'_>) -> bool,
{
type Writer = OptionalWriter<M::Writer>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
OptionalWriter::some(self.make.make_writer())
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
if (self.filter)(meta) {
OptionalWriter::some(self.make.make_writer_for(meta))
} else {
OptionalWriter::none()
}
}
}
impl<A, B> Tee<A, B> {
pub fn new(a: A, b: B) -> Self {
Self { a, b }
}
}
impl<'a, A, B> MakeWriter<'a> for Tee<A, B>
where
A: MakeWriter<'a>,
B: MakeWriter<'a>,
{
type Writer = Tee<A::Writer, B::Writer>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
Tee::new(self.a.make_writer(), self.b.make_writer())
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
Tee::new(self.a.make_writer_for(meta), self.b.make_writer_for(meta))
}
}
macro_rules! impl_tee {
($self_:ident.$f:ident($($arg:ident),*)) => {
{
let res_a = $self_.a.$f($($arg),*);
let res_b = $self_.b.$f($($arg),*);
(res_a?, res_b?)
}
}
}
impl<A, B> io::Write for Tee<A, B>
where
A: io::Write,
B: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let (a, b) = impl_tee!(self.write(buf));
Ok(std::cmp::max(a, b))
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
impl_tee!(self.flush());
Ok(())
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
let (a, b) = impl_tee!(self.write_vectored(bufs));
Ok(std::cmp::max(a, b))
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
impl_tee!(self.write_all(buf));
Ok(())
}
#[inline]
fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
impl_tee!(self.write_fmt(fmt));
Ok(())
}
}
impl<A, B> OrElse<A, B> {
pub fn new<'a, W>(inner: A, or_else: B) -> Self
where
A: MakeWriter<'a, Writer = OptionalWriter<W>>,
B: MakeWriter<'a>,
W: Write,
{
Self { inner, or_else }
}
}
impl<'a, A, B, W> MakeWriter<'a> for OrElse<A, B>
where
A: MakeWriter<'a, Writer = OptionalWriter<W>>,
B: MakeWriter<'a>,
W: io::Write,
{
type Writer = EitherWriter<W, B::Writer>;
#[inline]
fn make_writer(&'a self) -> Self::Writer {
match self.inner.make_writer() {
EitherWriter::A(writer) => EitherWriter::A(writer),
EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer()),
}
}
#[inline]
fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
match self.inner.make_writer_for(meta) {
EitherWriter::A(writer) => EitherWriter::A(writer),
EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer_for(meta)),
}
}
}
impl<W> io::Write for ArcWriter<W>
where
for<'a> &'a W: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&*self.0).write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(&*self.0).flush()
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
(&*self.0).write_vectored(bufs)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
(&*self.0).write_all(buf)
}
#[inline]
fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
(&*self.0).write_fmt(fmt)
}
}
#[cfg(any(feature = "json", feature = "time"))]
impl<'a> WriteAdaptor<'a> {
pub(in crate::fmt) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
Self { fmt_write }
}
}
#[cfg(any(feature = "json", feature = "time"))]
impl<'a> io::Write for WriteAdaptor<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let s =
std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
self.fmt_write
.write_str(s)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
Ok(s.as_bytes().len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(any(feature = "json", feature = "time"))]
impl<'a> fmt::Debug for WriteAdaptor<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("WriteAdaptor { .. }")
}
}
impl<'a, M> MakeWriterExt<'a> for M where M: MakeWriter<'a> {}
#[cfg(test)]
mod test {
use super::*;
use crate::fmt::format::Format;
use crate::fmt::test::{MockMakeWriter, MockWriter};
use crate::fmt::Subscriber;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use tracing::{debug, error, info, trace, warn, Level};
use tracing_core::dispatcher::{self, Dispatch};
fn test_writer<T>(make_writer: T, msg: &str, buf: &Mutex<Vec<u8>>)
where
T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
{
let subscriber = {
#[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)
.finish()
};
let dispatch = Dispatch::from(subscriber);
dispatcher::with_default(&dispatch, || {
error!("{}", msg);
});
let expected = format!("ERROR {}: {}\n", module_path!(), msg);
let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
assert!(actual.contains(expected.as_str()));
}
fn has_lines(buf: &Mutex<Vec<u8>>, msgs: &[(tracing::Level, &str)]) {
let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
let mut expected_lines = msgs.iter();
for line in actual.lines() {
let line = dbg!(line).trim();
let (level, msg) = expected_lines
.next()
.unwrap_or_else(|| panic!("expected no more lines, but got: {:?}", line));
let expected = format!("{} {}: {}", level, module_path!(), msg);
assert_eq!(line, expected.as_str());
}
}
#[test]
fn custom_writer_closure() {
let buf = Arc::new(Mutex::new(Vec::new()));
let buf2 = buf.clone();
let make_writer = move || MockWriter::new(buf2.clone());
let msg = "my custom writer closure error";
test_writer(make_writer, msg, &buf);
}
#[test]
fn custom_writer_struct() {
let buf = Arc::new(Mutex::new(Vec::new()));
let make_writer = MockMakeWriter::new(buf.clone());
let msg = "my custom writer struct error";
test_writer(make_writer, msg, &buf);
}
#[test]
fn custom_writer_mutex() {
let buf = Arc::new(Mutex::new(Vec::new()));
let writer = MockWriter::new(buf.clone());
let make_writer = Mutex::new(writer);
let msg = "my mutex writer error";
test_writer(make_writer, msg, &buf);
}
#[test]
fn combinators_level_filters() {
let info_buf = Arc::new(Mutex::new(Vec::new()));
let info = MockMakeWriter::new(info_buf.clone());
let debug_buf = Arc::new(Mutex::new(Vec::new()));
let debug = MockMakeWriter::new(debug_buf.clone());
let warn_buf = Arc::new(Mutex::new(Vec::new()));
let warn = MockMakeWriter::new(warn_buf.clone());
let err_buf = Arc::new(Mutex::new(Vec::new()));
let err = MockMakeWriter::new(err_buf.clone());
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() {
let some_buf = Arc::new(Mutex::new(Vec::new()));
let some = MockMakeWriter::new(some_buf.clone());
let or_else_buf = Arc::new(Mutex::new(Vec::new()));
let or_else = MockMakeWriter::new(or_else_buf.clone());
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() {
let info_buf = Arc::new(Mutex::new(Vec::new()));
let info = MockMakeWriter::new(info_buf.clone());
let debug_buf = Arc::new(Mutex::new(Vec::new()));
let debug = MockMakeWriter::new(debug_buf.clone());
let warn_buf = Arc::new(Mutex::new(Vec::new()));
let warn = MockMakeWriter::new(warn_buf.clone());
let err_buf = Arc::new(Mutex::new(Vec::new()));
let err = MockMakeWriter::new(err_buf.clone());
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() {
let a_buf = Arc::new(Mutex::new(Vec::new()));
let a = MockMakeWriter::new(a_buf.clone());
let b_buf = Arc::new(Mutex::new(Vec::new()));
let b = MockMakeWriter::new(b_buf.clone());
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[..]);
}
}