diff --git a/tracing-appender/src/non_blocking.rs b/tracing-appender/src/non_blocking.rs index 2bf52185e5..bd7dd8675d 100644 --- a/tracing-appender/src/non_blocking.rs +++ b/tracing-appender/src/non_blocking.rs @@ -240,10 +240,10 @@ impl std::io::Write for NonBlocking { } } -impl MakeWriter for NonBlocking { +impl<'a> MakeWriter<'a> for NonBlocking { type Writer = NonBlocking; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { self.clone() } } diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 12dfd8af00..4fedbd1080 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -110,7 +110,7 @@ impl Layer where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { /// Sets the [event formatter][`FormatEvent`] that the layer will use to /// format events. @@ -171,7 +171,7 @@ impl Layer { /// [`Layer`]: ../layer/trait.Layer.html pub fn with_writer(self, make_writer: W2) -> Layer where - W2: MakeWriter + 'static, + W2: for<'writer> MakeWriter<'writer> + 'static, { Layer { fmt_fields: self.fmt_fields, @@ -477,7 +477,7 @@ where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { /// Builds a [`Layer`] with the provided configuration. /// @@ -508,7 +508,7 @@ where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { #[inline] fn make_ctx<'a>(&'a self, ctx: Context<'a, S>) -> FmtContext<'a, S, N> { @@ -589,7 +589,7 @@ where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { fn new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { let span = ctx.span(id).expect("Span not found, this is a bug"); @@ -923,9 +923,7 @@ mod test { }; use crate::Registry; use format::FmtSpan; - use lazy_static::lazy_static; use regex::Regex; - use std::sync::Mutex; use tracing::subscriber::with_default; use tracing_core::dispatcher::Dispatch; @@ -982,13 +980,9 @@ mod test { #[test] fn synthesize_span_none() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) @@ -999,19 +993,15 @@ mod test { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); - let actual = sanitize_timings(String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap()); + let actual = sanitize_timings(make_writer.get_string()); assert_eq!("", actual.as_str()); } #[test] fn synthesize_span_active() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) @@ -1022,7 +1012,7 @@ mod test { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); - let actual = sanitize_timings(String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap()); + let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: enter\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: exit\n", @@ -1032,13 +1022,9 @@ mod test { #[test] fn synthesize_span_close() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) @@ -1049,7 +1035,7 @@ mod test { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); - let actual = sanitize_timings(String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap()); + let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: close timing timing\n", actual.as_str() @@ -1058,13 +1044,9 @@ mod test { #[test] fn synthesize_span_close_no_timing() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) @@ -1076,7 +1058,7 @@ mod test { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); - let actual = sanitize_timings(String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap()); + let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: close\n", actual.as_str() @@ -1085,13 +1067,9 @@ mod test { #[test] fn synthesize_span_full() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) @@ -1102,7 +1080,7 @@ mod test { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); - let actual = sanitize_timings(String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap()); + let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: new\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: enter\n\ @@ -1114,23 +1092,19 @@ mod test { #[test] fn make_writer_based_on_meta() { - lazy_static! { - static ref BUF1: Mutex> = Mutex::new(vec![]); - static ref BUF2: Mutex> = Mutex::new(vec![]); - } - struct MakeByTarget<'a> { - make_writer1: MockMakeWriter<'a>, - make_writer2: MockMakeWriter<'a>, + struct MakeByTarget { + make_writer1: MockMakeWriter, + make_writer2: MockMakeWriter, } - impl<'a> MakeWriter for MakeByTarget<'a> { - type Writer = MockWriter<'a>; + impl<'a> MakeWriter<'a> for MakeByTarget { + type Writer = MockWriter; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { self.make_writer1.make_writer() } - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.target() == "writer2" { return self.make_writer2.make_writer(); } @@ -1138,8 +1112,8 @@ mod test { } } - let make_writer1 = MockMakeWriter::new(&BUF1); - let make_writer2 = MockMakeWriter::new(&BUF2); + let make_writer1 = MockMakeWriter::default(); + let make_writer2 = MockMakeWriter::default(); let make_writer = MakeByTarget { make_writer1: make_writer1.clone(), diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 8c4137bb93..8c984496cf 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -519,10 +519,10 @@ impl<'a> fmt::Debug for WriteAdaptor<'a> { mod test { use super::*; use crate::fmt::{format::FmtSpan, test::MockMakeWriter, time::FormatTime, SubscriberBuilder}; - use lazy_static::lazy_static; - use std::{fmt, sync::Mutex}; use tracing::{self, subscriber::with_default}; + use std::fmt; + struct MockTime; impl FormatTime for MockTime { fn format_time(&self, w: &mut dyn fmt::Write) -> fmt::Result { @@ -536,17 +536,13 @@ mod test { #[test] fn json() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); @@ -555,17 +551,14 @@ mod test { #[test] fn json_flattened_event() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"message\":\"some json test\"}\n"; + let subscriber = subscriber() .flatten_event(true) .with_current_span(true) .with_span_list(true); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); @@ -574,17 +567,13 @@ mod test { #[test] fn json_disabled_current_span_event() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(false) .with_span_list(true); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); @@ -593,17 +582,13 @@ mod test { #[test] fn json_disabled_span_list_event() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(false); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); @@ -612,17 +597,13 @@ mod test { #[test] fn json_nested_span() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3},{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); let span = tracing::span!( @@ -638,17 +619,13 @@ mod test { #[test] fn json_no_span() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); - test_json(expected, subscriber, &BUF, || { + test_json(expected, subscriber, || { tracing::info!("some json test"); }); } @@ -657,17 +634,17 @@ mod test { fn record_works() { // This test reproduces issue #707, where using `Span::record` causes // any events inside the span to be ignored. - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - let buffer = MockMakeWriter::new(&BUF); - let subscriber = crate::fmt().json().with_writer(buffer.clone()).finish(); + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt() + .json() + .with_writer(make_writer.clone()) + .finish(); with_default(subscriber, || { tracing::info!("an event outside the root span"); assert_eq!( - parse_as_json(&buffer)["fields"]["message"], + parse_as_json(&make_writer)["fields"]["message"], "an event outside the root span" ); @@ -677,7 +654,7 @@ mod test { tracing::info!("an event inside the root span"); assert_eq!( - parse_as_json(&buffer)["fields"]["message"], + parse_as_json(&make_writer)["fields"]["message"], "an event inside the root span" ); }); @@ -685,11 +662,7 @@ mod test { #[test] fn json_span_event_show_correct_context() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - - let buffer = MockMakeWriter::new(&BUF); + let buffer = MockMakeWriter::default(); let subscriber = subscriber() .with_writer(buffer.clone()) .flatten_event(false) @@ -748,11 +721,7 @@ mod test { fn json_span_event_with_no_fields() { // Check span events serialize correctly. // Discussion: https://github.com/tokio-rs/tracing/issues/829#issuecomment-661984255 - lazy_static! { - static ref BUF: Mutex> = Mutex::new(Vec::new()); - } - - let buffer = MockMakeWriter::new(&BUF); + let buffer = MockMakeWriter::default(); let subscriber = subscriber() .with_writer(buffer.clone()) .flatten_event(false) @@ -776,7 +745,7 @@ mod test { }); } - fn parse_as_json(buffer: &MockMakeWriter<'_>) -> serde_json::Value { + fn parse_as_json(buffer: &MockMakeWriter) -> serde_json::Value { let buf = String::from_utf8(buffer.buf().to_vec()).unwrap(); let json = buf .lines() @@ -794,10 +763,9 @@ mod test { fn test_json( expected: &str, builder: crate::fmt::SubscriberBuilder>, - buf: &'static Mutex>, producer: impl FnOnce() -> T, ) { - let make_writer = MockMakeWriter::new(buf); + let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_timer(MockTime) diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index 4788b9f840..c07b9f8b9d 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -1316,13 +1316,15 @@ impl Display for TimingDisplay { #[cfg(test)] pub(super) mod test { - - use crate::fmt::{test::MockWriter, time::FormatTime}; - use lazy_static::lazy_static; - use tracing::{self, subscriber::with_default}; + use crate::fmt::{test::MockMakeWriter, time::FormatTime}; + use tracing::{ + self, + dispatcher::{set_default, Dispatch}, + subscriber::with_default, + }; use super::{FmtSpan, TimingDisplay}; - use std::{fmt, sync::Mutex}; + use std::fmt; pub(crate) struct MockTime; impl FormatTime for MockTime { @@ -1334,13 +1336,9 @@ pub(super) mod test { #[test] fn disable_everything() { // This test reproduces https://github.com/tokio-rs/tracing/issues/1354 - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .without_time() .with_level(false) .with_target(false) @@ -1348,152 +1346,94 @@ pub(super) mod test { .with_thread_names(false); #[cfg(feature = "ansi")] let subscriber = subscriber.with_ansi(false); - - with_default(subscriber.finish(), || { - tracing::info!("hello"); - }); - - let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); - assert_eq!("hello\n", actual.as_str()); + run_test(subscriber, make_writer, "hello\n") } #[cfg(feature = "ansi")] #[test] fn with_ansi_true() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); - let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m tracing_subscriber::fmt::format::test: some ansi test\n"; - test_ansi(make_writer, expected, true, &BUF); + let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m tracing_subscriber::fmt::format::test: hello\n"; + test_ansi(true, expected); } #[cfg(feature = "ansi")] #[test] fn with_ansi_false() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); - let expected = "fake time INFO tracing_subscriber::fmt::format::test: some ansi test\n"; - - test_ansi(make_writer, expected, false, &BUF); + let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; + test_ansi(false, expected); } #[cfg(not(feature = "ansi"))] #[test] fn without_ansi() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); - let expected = "fake time INFO tracing_subscriber::fmt::format::test: some ansi test\n"; + let make_writer = MockMakeWriter::default(); + let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer) - .with_timer(MockTime) - .finish(); + .with_timer(MockTime); + run_test(subscriber, make_writer, expected); + } - with_default(subscriber, || { - tracing::info!("some ansi test"); - }); + #[test] + fn without_level() { + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime); + let expected = "fake time tracing_subscriber::fmt::format::test: hello\n"; - let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); - assert_eq!(expected, actual.as_str()); + run_test(subscriber, make_writer, expected); } #[cfg(feature = "ansi")] - fn test_ansi(make_writer: T, expected: &str, is_ansi: bool, buf: &Mutex>) - where - T: crate::fmt::MakeWriter + Send + Sync + 'static, - { + fn test_ansi(is_ansi: bool, expected: &str) { + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_ansi(is_ansi) - .with_timer(MockTime) - .finish(); - - with_default(subscriber, || { - tracing::info!("some ansi test"); - }); - - let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); - assert_eq!(expected, actual.as_str()); + .with_timer(MockTime); + run_test(subscriber, make_writer, expected) } - #[test] - fn without_level() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); - let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) - .with_level(false) - .with_ansi(false) - .with_timer(MockTime) - .finish(); - - with_default(subscriber, || { - tracing::info!("hello"); - }); - let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); - assert_eq!( - "fake time tracing_subscriber::fmt::format::test: hello\n", - actual.as_str() - ); + fn run_test(subscriber: impl Into, buf: MockMakeWriter, expected: &str) { + let _default = set_default(&subscriber.into()); + tracing::info!("hello"); + assert_eq!(expected, buf.get_string()) } #[test] fn overridden_parents() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); - let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + let make_writer = MockMakeWriter::default(); + let collector = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); - with_default(subscriber, || { + with_default(collector, || { let span1 = tracing::info_span!("span1"); let span2 = tracing::info_span!(parent: &span1, "span2"); tracing::info!(parent: &span2, "hello"); }); - let actual = String::from_utf8(BUF.try_lock().unwrap().to_vec()).unwrap(); assert_eq!( "fake time span1:span2: tracing_subscriber::fmt::format::test: hello\n", - actual.as_str() + make_writer.get_string() ); } #[test] fn overridden_parents_in_scope() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() - .with_writer(make_writer) + .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); - let actual = || { - let mut buf = BUF.try_lock().unwrap(); - let val = String::from_utf8(buf.to_vec()).unwrap(); - buf.clear(); - val - }; - with_default(subscriber, || { let span1 = tracing::info_span!("span1"); let span2 = tracing::info_span!(parent: &span1, "span2"); @@ -1503,13 +1443,13 @@ pub(super) mod test { tracing::info!("hello"); assert_eq!( "fake time span3: tracing_subscriber::fmt::format::test: hello\n", - actual().as_str() + make_writer.get_string().as_str() ); tracing::info!(parent: &span2, "hello"); assert_eq!( "fake time span1:span2: tracing_subscriber::fmt::format::test: hello\n", - actual().as_str() + make_writer.get_string().as_str() ); }); } diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 9a759d0d75..53912d30b8 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -429,7 +429,7 @@ where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, F: layer::Layer> + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, layer::Layered>: tracing_core::Subscriber, fmt_layer::Layer: layer::Layer, { @@ -529,7 +529,7 @@ impl SubscriberBuilder where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, F: layer::Layer> + Send + Sync + 'static, fmt_layer::Layer: layer::Layer + Send + Sync + 'static, { @@ -576,7 +576,7 @@ impl From> for tracing_core::Dispatch where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, F: layer::Layer> + Send + Sync + 'static, fmt_layer::Layer: layer::Layer + Send + Sync + 'static, { @@ -985,7 +985,7 @@ impl SubscriberBuilder { where E2: FormatEvent + 'static, N: for<'writer> FormatFields<'writer> + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, @@ -1008,7 +1008,7 @@ impl SubscriberBuilder { where E2: FormatEvent + 'static, N: for<'writer> FormatFields<'writer> + 'static, - W: MakeWriter + 'static, + W: for<'writer> MakeWriter<'writer> + 'static, { self.event_format(fmt_event) } @@ -1031,7 +1031,7 @@ impl SubscriberBuilder { /// [`MakeWriter`]: trait.MakeWriter.html pub fn with_writer(self, make_writer: W2) -> SubscriberBuilder where - W2: MakeWriter + 'static, + W2: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, @@ -1141,16 +1141,16 @@ mod test { }; use std::{ io, - sync::{Mutex, MutexGuard, TryLockError}, + sync::{Arc, Mutex, MutexGuard, TryLockError}, }; use tracing_core::dispatcher::Dispatch; - pub(crate) struct MockWriter<'a> { - buf: &'a Mutex>, + pub(crate) struct MockWriter { + buf: Arc>>, } - impl<'a> MockWriter<'a> { - pub(crate) fn new(buf: &'a Mutex>) -> Self { + impl MockWriter { + pub(crate) fn new(buf: Arc>>) -> Self { Self { buf } } @@ -1161,12 +1161,12 @@ mod test { } } - pub(crate) fn buf(&self) -> io::Result>> { + pub(crate) fn buf(&self) -> io::Result>> { self.buf.try_lock().map_err(Self::map_error) } } - impl<'a> io::Write for MockWriter<'a> { + impl io::Write for MockWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf()?.write(buf) } @@ -1176,31 +1176,35 @@ mod test { } } - #[derive(Clone)] - pub(crate) struct MockMakeWriter<'a> { - buf: &'a Mutex>, + #[derive(Clone, Default)] + pub(crate) struct MockMakeWriter { + buf: Arc>>, } - impl<'a> MockMakeWriter<'a> { - pub(crate) fn new(buf: &'a Mutex>) -> Self { + impl MockMakeWriter { + pub(crate) fn new(buf: Arc>>) -> Self { Self { buf } } - pub(crate) fn buf(&self) -> MutexGuard<'a, Vec> { + pub(crate) fn buf(&self) -> MutexGuard<'_, Vec> { self.buf.lock().unwrap() } pub(crate) fn get_string(&self) -> String { - String::from_utf8(self.buf.lock().unwrap().clone()) - .expect("subscriber must write valid UTF-8") + let mut buf = self.buf.lock().expect("lock shouldn't be poisoned"); + let string = std::str::from_utf8(&buf[..]) + .expect("formatter should not have produced invalid utf-8") + .to_owned(); + buf.clear(); + string } } - impl<'a> MakeWriter for MockMakeWriter<'a> { - type Writer = MockWriter<'a>; + impl<'a> MakeWriter<'a> for MockMakeWriter { + type Writer = MockWriter; - fn make_writer(&self) -> Self::Writer { - MockWriter::new(self.buf) + fn make_writer(&'a self) -> Self::Writer { + MockWriter::new(self.buf.clone()) } } diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs index c91ed70164..5d82e731af 100644 --- a/tracing-subscriber/src/fmt/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -4,30 +4,20 @@ use std::{ fmt::Debug, io::{self, Write}, - sync::Arc, + sync::{Arc, Mutex, MutexGuard}, }; use tracing_core::Metadata; /// A type that can create [`io::Write`] instances. /// -/// `MakeWriter` is used by [`fmt::Subscriber`] or [`fmt::Layer`] to print +/// `MakeWriter` is used by [`fmt::Layer`] 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`]. -/// -/// 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. +/// as [`io::stdout`] and [`io::stderr`]. Additionally, it is implemented for +/// [`std::sync::Mutex`][mutex] when the tyoe inside the mutex implements +/// [`io::Write`]. /// /// # Examples /// @@ -75,6 +65,23 @@ use tracing_core::Metadata; /// # drop(subscriber); /// ``` /// +/// A single instance of a type implementing [`io::Write`] may be used as a +/// `MakeWriter` by wrapping it in a [`Mutex`][mutex]. For example, we could +/// write to a file like so: +/// +/// ``` +/// use std::{fs::File, sync::Mutex}; +/// +/// # fn docs() -> Result<(), Box> { +/// let log_file = File::create("my_cool_trace.log")?; +/// let subscriber = tracing_subscriber::fmt() +/// .with_writer(Mutex::new(log_file)) +/// .finish(); +/// # drop(subscriber); +/// # Ok(()) +/// # } +/// ``` +/// /// [`io::Write`]: std::io::Write /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber @@ -86,7 +93,7 @@ use tracing_core::Metadata; /// [`Metadata`]: tracing_core::Metadata /// [levels]: tracing_core::Level /// [targets]: tracing_core::Metadata::target -pub trait MakeWriter { +pub trait MakeWriter<'a> { /// The concrete [`io::Write`] implementation returned by [`make_writer`]. /// /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html @@ -106,7 +113,7 @@ pub trait MakeWriter { /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber /// [`io::Write`]: std::io::Write - fn make_writer(&self) -> Self::Writer; + fn make_writer(&'a self) -> Self::Writer; /// Returns a [`Writer`] for writing data from the span or event described /// by the provided [`Metadata`]. @@ -126,64 +133,67 @@ pub trait MakeWriter { /// at lower levels to stdout: /// /// ``` - /// use std::io::{self, Stdout, Stderr}; + /// use std::io::{self, Stdout, Stderr, StdoutLock, StderrLock}; /// use tracing_subscriber::fmt::writer::MakeWriter; /// use tracing_core::{Metadata, Level}; /// - /// pub struct MyMakeWriter {} + /// 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 Stdio { - /// Stdout(Stdout), - /// Stderr(Stderr), + /// pub enum StdioLock<'a> { + /// Stdout(StdoutLock<'a>), + /// Stderr(StderrLock<'a>), /// } /// - /// impl io::Write for Stdio { + /// impl<'a> io::Write for StdioLock<'a> { /// fn write(&mut self, buf: &[u8]) -> io::Result { /// match self { - /// Stdio::Stdout(io) => io.write(buf), - /// Stdio::Stderr(io) => io.write(buf), + /// StdioLock::Stdout(lock) => lock.write(buf), + /// StdioLock::Stderr(lock) => lock.write(buf), /// } /// } /// /// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { /// // ... /// # match self { - /// # Stdio::Stdout(io) => io.write_all(buf), - /// # Stdio::Stderr(io) => io.write_all(buf), + /// # StdioLock::Stdout(lock) => lock.write_all(buf), + /// # StdioLock::Stderr(lock) => lock.write_all(buf), /// # } /// } /// /// fn flush(&mut self) -> io::Result<()> { /// // ... /// # match self { - /// # Stdio::Stdout(io) => io.flush(), - /// # Stdio::Stderr(io) => io.flush(), + /// # StdioLock::Stdout(lock) => lock.flush(), + /// # StdioLock::Stderr(lock) => lock.flush(), /// # } /// } /// } /// - /// impl MakeWriter for MyMakeWriter { - /// type Writer = Stdio; + /// impl<'a> MakeWriter<'a> for MyMakeWriter { + /// type Writer = StdioLock<'a>; /// - /// fn make_writer(&self) -> Self::Writer { + /// 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. - /// Stdio::Stdout(io::stdout()) + /// StdioLock::Stdout(self.stdout.lock()) /// } /// - /// fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + /// 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 Stdio::Stderr(io::stderr()); + /// return StdioLock::Stderr(self.stderr.lock()); /// } /// /// // Otherwise, we'll return stdout. - /// Stdio::Stdout(io::stdout()) + /// StdioLock::Stdout(self.stdout.lock()) /// } /// } /// ``` @@ -193,7 +203,7 @@ pub trait MakeWriter { /// [make_writer]: MakeWriter::make_writer /// [`WARN`]: tracing_core::Level::WARN /// [`ERROR`]: tracing_core::Level::ERROR - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { let _ = meta; self.make_writer() } @@ -205,7 +215,7 @@ pub trait MakeWriter { /// This is not intended to be implemented directly for user-defined /// [`MakeWriter`]s; instead, it should be imported when the desired methods are /// used. -pub trait MakeWriterExt: MakeWriter { +pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// Wraps `self` and returns a [`MakeWriter`] that will only write output /// for events at or below the provided verbosity [`Level`]. For instance, /// `Level::TRACE` is considered to be _more verbose` than `Level::INFO`. @@ -443,7 +453,7 @@ pub trait MakeWriterExt: MakeWriter { fn and(self, other: B) -> Tee where Self: Sized, - B: MakeWriter + Sized, + B: MakeWriter<'a> + Sized, { Tee::new(self, other) } @@ -473,8 +483,8 @@ pub trait MakeWriterExt: MakeWriter { /// [own]: EitherWriter::none fn or_else(self, other: B) -> OrElse where - Self: MakeWriter> + Sized, - B: MakeWriter + Sized, + Self: MakeWriter<'a, Writer = OptionalWriter> + Sized, + B: MakeWriter<'a> + Sized, W: Write, { OrElse::new(self, other) @@ -530,7 +540,7 @@ pub struct TestWriter { /// [`Subscriber`]: tracing::Subscriber /// [`io::Write`]: std::io::Write pub struct BoxMakeWriter { - inner: Box> + Send + Sync>, + inner: Box MakeWriter<'a, Writer = Box> + Send + Sync>, name: &'static str, } @@ -627,33 +637,56 @@ pub struct Tee { b: B, } -impl MakeWriter for F +/// A type implementing [`io::Write`] for a [`MutexGuard`] where the type +/// inside the [`Mutex`] implements [`io::Write`]. +/// +/// This is used by the [`MakeWriter`] implementation for [`Mutex`], because +/// [`MutexGuard`] itself will not implement [`io::Write`] — instead, it +/// _dereferences_ to a type implementing [`io::Write`]. Because [`MakeWriter`] +/// requires the `Writer` type to implement [`io::Write`], it's necessary to add +/// a newtype that forwards the trait implementation. +/// +/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +/// [`MutexGuard`]: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html +/// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html +/// [`MakeWriter`]: trait.MakeWriter.html +#[derive(Debug)] +pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>); + +/// Implements [`std::io::Write`] for an [`Arc`] where `&W: Write`. +/// +/// This is an implementation detail of the [`MakeWriter`] impl for [`Arc`]. +#[derive(Clone, Debug)] +pub struct ArcWriter(Arc); + +impl<'a, F, W> MakeWriter<'a> for F where F: Fn() -> W, W: io::Write, { type Writer = W; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { (self)() } } -impl MakeWriter for Arc +impl<'a, W> MakeWriter<'a> for Arc where - for<'a> &'a W: io::Write, + &'a W: io::Write + 'a, { - type Writer = ArcWriter; - fn make_writer(&self) -> Self::Writer { - ArcWriter(self.clone()) + type Writer = &'a W; + fn make_writer(&'a self) -> Self::Writer { + &*self } } -/// Implements [`std::io::Write`] for an [`Arc`] where `&W: Write`. -/// -/// This is an implementation detail of the [`MakeWriter`] impl for [`Arc`]. -#[derive(Clone, Debug)] -pub struct ArcWriter(Arc); +impl<'a> MakeWriter<'a> for std::fs::File { + type Writer = &'a std::fs::File; + fn make_writer(&'a self) -> Self::Writer { + self + } +} // === impl TestWriter === @@ -676,22 +709,23 @@ impl io::Write for TestWriter { } } -impl MakeWriter for TestWriter { +impl<'a> MakeWriter<'a> for TestWriter { type Writer = Self; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { Self::default() } } +// === impl BoxMakeWriter === + impl BoxMakeWriter { /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`]. /// /// [`MakeWriter`]: trait.MakeWriter.html pub fn new(make_writer: M) -> Self where - M: MakeWriter + Send + Sync + 'static, - M::Writer: Write + 'static, + M: for<'a> MakeWriter<'a> + Send + Sync + 'static, { Self { inner: Box::new(Boxed(make_writer)), @@ -708,35 +742,72 @@ impl Debug for BoxMakeWriter { } } -impl MakeWriter for BoxMakeWriter { - type Writer = Box; +impl<'a> MakeWriter<'a> for BoxMakeWriter { + type Writer = Box; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { self.inner.make_writer() } - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { self.inner.make_writer_for(meta) } } struct Boxed(M); -impl MakeWriter for Boxed +impl<'a, M> MakeWriter<'a> for Boxed where - M: MakeWriter, - M::Writer: Write + 'static, + M: MakeWriter<'a>, { - type Writer = Box; + type Writer = Box; - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { let w = self.0.make_writer(); Box::new(w) } +} - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { - let w = self.0.make_writer_for(meta); - Box::new(w) +// === impl Mutex/MutexGuardWriter === + +impl<'a, W> MakeWriter<'a> for Mutex +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 { + 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 { + 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) } } @@ -824,29 +895,28 @@ impl From> for OptionalWriter { impl WithMaxLevel { /// Wraps the provided [`MakeWriter`] with a maximum [`Level`], so that it - /// returns [`OptionalWriter::none`][own] for spans and events whose level is + /// returns [`OptionalWriter::none`] for spans and events whose level is /// more verbose than the maximum level. /// /// See [`MakeWriterExt::with_max_level`] for details. /// /// [`Level`]: tracing_core::Level - /// [own]: EitherWriter::none pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } -impl MakeWriter for WithMaxLevel { +impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMaxLevel { type Writer = OptionalWriter; #[inline] - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() <= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } @@ -858,29 +928,28 @@ impl MakeWriter for WithMaxLevel { impl WithMinLevel { /// Wraps the provided [`MakeWriter`] with a minimum [`Level`], so that it - /// returns [`OptionalWriter::none`][own] for spans and events whose level is + /// returns [`OptionalWriter::none`] for spans and events whose level is /// less verbose than the maximum level. /// /// See [`MakeWriterExt::with_min_level`] for details. /// /// [`Level`]: tracing_core::Level - /// [own]: EitherWriter::none pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } -impl MakeWriter for WithMinLevel { +impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMinLevel { type Writer = OptionalWriter; #[inline] - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() >= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } @@ -907,20 +976,20 @@ impl WithFilter { } } -impl MakeWriter for WithFilter +impl<'a, M, F> MakeWriter<'a> for WithFilter where - M: MakeWriter, + M: MakeWriter<'a>, F: Fn(&Metadata<'_>) -> bool, { type Writer = OptionalWriter; #[inline] - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { OptionalWriter::some(self.make.make_writer()) } #[inline] - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if (self.filter)(meta) { OptionalWriter::some(self.make.make_writer_for(meta)) } else { @@ -937,27 +1006,25 @@ impl Tee { /// outputs. /// /// See the documentation for [`MakeWriterExt::and`] for details. - /// - /// [writers]: std::io::Write pub fn new(a: A, b: B) -> Self { Self { a, b } } } -impl MakeWriter for Tee +impl<'a, A, B> MakeWriter<'a> for Tee where - A: MakeWriter, - B: MakeWriter, + A: MakeWriter<'a>, + B: MakeWriter<'a>, { type Writer = Tee; #[inline] - fn make_writer(&self) -> Self::Writer { + fn make_writer(&'a self) -> Self::Writer { Tee::new(self.a.make_writer(), self.b.make_writer()) } #[inline] - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { Tee::new(self.a.make_writer_for(meta), self.b.make_writer_for(meta)) } } @@ -1012,26 +1079,26 @@ where impl OrElse { /// Combines - pub fn new(inner: A, or_else: B) -> Self + pub fn new<'a, W>(inner: A, or_else: B) -> Self where - A: MakeWriter>, - B: MakeWriter, + A: MakeWriter<'a, Writer = OptionalWriter>, + B: MakeWriter<'a>, W: Write, { Self { inner, or_else } } } -impl MakeWriter for OrElse +impl<'a, A, B, W> MakeWriter<'a> for OrElse where - A: MakeWriter>, - B: MakeWriter, + A: MakeWriter<'a, Writer = OptionalWriter>, + B: MakeWriter<'a>, W: io::Write, { type Writer = EitherWriter; #[inline] - fn make_writer(&self) -> Self::Writer { + 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()), @@ -1039,7 +1106,7 @@ where } #[inline] - fn make_writer_for(&self, meta: &Metadata<'_>) -> Self::Writer { + 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)), @@ -1081,43 +1148,31 @@ where // === blanket impls === -impl MakeWriterExt for M where M: MakeWriter {} - +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 lazy_static::lazy_static; - use std::sync::{ - atomic::{AtomicBool, Ordering}, - Mutex, - }; + 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(make_writer: T, msg: &str, buf: &Mutex>) where - T: MakeWriter + Send + Sync + 'static, + T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static, { let subscriber = { #[cfg(feature = "ansi")] - { - let f = Format::default().without_time().with_ansi(false); - Subscriber::builder() - .event_format(f) - .with_writer(make_writer) - .finish() - } + 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 f = Format::default().without_time(); + Subscriber::builder() + .event_format(f) + .with_writer(make_writer) + .finish() }; let dispatch = Dispatch::from(subscriber); @@ -1145,39 +1200,43 @@ mod test { #[test] fn custom_writer_closure() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = || MockWriter::new(&BUF); + 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_writer(make_writer, msg, &buf); } #[test] fn custom_writer_struct() { - lazy_static! { - static ref BUF: Mutex> = Mutex::new(vec![]); - } - - let make_writer = MockMakeWriter::new(&BUF); + 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_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() { - lazy_static! { - static ref INFO_BUF: Mutex> = Mutex::new(vec![]); - static ref DEBUG_BUF: Mutex> = Mutex::new(vec![]); - static ref WARN_BUF: Mutex> = Mutex::new(vec![]); - static ref ERR_BUF: Mutex> = Mutex::new(vec![]); - } + 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 info = MockMakeWriter::new(&INFO_BUF); - let debug = MockMakeWriter::new(&DEBUG_BUF); - let warn = MockMakeWriter::new(&WARN_BUF); - let err = MockMakeWriter::new(&ERR_BUF); + 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) @@ -1214,27 +1273,25 @@ mod test { ]; println!("max level debug"); - has_lines(&DEBUG_BUF, &all_lines[1..]); + has_lines(&debug_buf, &all_lines[1..]); println!("max level info"); - has_lines(&INFO_BUF, &all_lines[2..]); + has_lines(&info_buf, &all_lines[2..]); println!("max level warn"); - has_lines(&WARN_BUF, &all_lines[3..]); + has_lines(&warn_buf, &all_lines[3..]); println!("max level error"); - has_lines(&ERR_BUF, &all_lines[4..]); + has_lines(&err_buf, &all_lines[4..]); } #[test] fn combinators_or_else() { - lazy_static! { - static ref SOME_BUF: Mutex> = Mutex::new(vec![]); - static ref OR_ELSE_BUF: Mutex> = Mutex::new(vec![]); - } + let some_buf = Arc::new(Mutex::new(Vec::new())); + let some = MockMakeWriter::new(some_buf.clone()); - let some = MockMakeWriter::new(&SOME_BUF); - let or_else = MockMakeWriter::new(&OR_ELSE_BUF); + 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 || { @@ -1262,26 +1319,26 @@ mod test { info!("world"); info!("goodbye"); - has_lines(&SOME_BUF, &[(Level::INFO, "hello")]); + has_lines(&some_buf, &[(Level::INFO, "hello")]); has_lines( - &OR_ELSE_BUF, + &or_else_buf, &[(Level::INFO, "world"), (Level::INFO, "goodbye")], ); } #[test] fn combinators_or_else_chain() { - lazy_static! { - static ref INFO_BUF: Mutex> = Mutex::new(vec![]); - static ref DEBUG_BUF: Mutex> = Mutex::new(vec![]); - static ref WARN_BUF: Mutex> = Mutex::new(vec![]); - static ref ERR_BUF: Mutex> = Mutex::new(vec![]); - } + 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 info = MockMakeWriter::new(&INFO_BUF); - let debug = MockMakeWriter::new(&DEBUG_BUF); - let warn = MockMakeWriter::new(&WARN_BUF); - let err = MockMakeWriter::new(&ERR_BUF); + 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( @@ -1311,27 +1368,25 @@ mod test { error!("error"); println!("max level debug"); - has_lines(&DEBUG_BUF, &[(Level::DEBUG, "debug")]); + has_lines(&debug_buf, &[(Level::DEBUG, "debug")]); println!("max level info"); - has_lines(&INFO_BUF, &[(Level::INFO, "info")]); + has_lines(&info_buf, &[(Level::INFO, "info")]); println!("max level warn"); - has_lines(&WARN_BUF, &[(Level::WARN, "warn")]); + has_lines(&warn_buf, &[(Level::WARN, "warn")]); println!("max level error"); - has_lines(&ERR_BUF, &[(Level::ERROR, "error")]); + has_lines(&err_buf, &[(Level::ERROR, "error")]); } #[test] fn combinators_and() { - lazy_static! { - static ref A_BUF: Mutex> = Mutex::new(vec![]); - static ref B_BUF: Mutex> = Mutex::new(vec![]); - } + let a_buf = Arc::new(Mutex::new(Vec::new())); + let a = MockMakeWriter::new(a_buf.clone()); - let a = MockMakeWriter::new(&A_BUF); - let b = MockMakeWriter::new(&B_BUF); + let b_buf = Arc::new(Mutex::new(Vec::new())); + let b = MockMakeWriter::new(b_buf.clone()); let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")]; @@ -1352,7 +1407,7 @@ mod test { info!("hello"); info!("world"); - has_lines(&A_BUF, &lines[..]); - has_lines(&B_BUF, &lines[..]); + has_lines(&a_buf, &lines[..]); + has_lines(&b_buf, &lines[..]); } }