From b09649f68c1451fb7fcd199b965db9c868e25102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hadnagy?= Date: Sun, 5 Mar 2023 20:06:17 +0100 Subject: [PATCH 1/5] Tracing-subscriber support --- Cargo.toml | 6 ++- src/lib.rs | 9 ++++ src/tracing_subscriber.rs | 91 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/tracing_subscriber.rs diff --git a/Cargo.toml b/Cargo.toml index 6db1f52..8d6775a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ travis-ci = { repository = "gin66/tui-logger" } log = "0.4" chrono = "0.4" tui = { version = "0.19", default-features = false } +tracing = {version = "0.1.37", optional = true} +tracing-subscriber = {version = "0.3", optional = true} lazy_static = "1.0" fxhash = "0.2" parking_lot = "0.12" @@ -28,4 +30,6 @@ termion = "1.5" env_logger = "0.10.0" [features] -default = ["slog"] +default = ["slog", "tracing-subscriber", "tracing-support"] +tracing-support = ["tracing", "tracing-subscriber"] + diff --git a/src/lib.rs b/src/lib.rs index 5fc84b8..42b30a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,10 +172,14 @@ use tui::widgets::{Block, Borders, Widget}; mod circular; #[cfg(feature = "slog")] mod slog; +#[cfg(feature = "tracing-support")] +mod tracing_subscriber; pub use crate::circular::CircularBuffer; #[cfg(feature = "slog")] pub use crate::slog::TuiSlogDrain; +#[cfg(feature = "tracing-support")] +pub use crate::tracing_subscriber::TuiTracingSubscriber; struct ExtLogRecord { timestamp: DateTime, @@ -396,6 +400,11 @@ pub fn slog_drain() -> TuiSlogDrain { TuiSlogDrain } +#[cfg(feature = "tracing-support")] +pub fn tracing_subscriber_layer() -> TuiTracingSubscriber { + TuiTracingSubscriber +} + /// Set the depth of the hot buffer in order to avoid message loss. /// This is effective only after a call to move_events() pub fn set_hot_buffer_depth(depth: usize) { diff --git a/src/tracing_subscriber.rs b/src/tracing_subscriber.rs new file mode 100644 index 0000000..45b35d2 --- /dev/null +++ b/src/tracing_subscriber.rs @@ -0,0 +1,91 @@ +//! `tracing-subscriber` support for `tui-logger` + +use std::collections::HashMap; +use super::TUI_LOGGER; +use log::{self, Log, Record}; +use tracing_subscriber::Layer; + +#[derive(Default)] +struct ToStringVisitor<'a>(HashMap<&'a str, String>); + +impl ToStringVisitor<'_> { + fn to_string<'a>(&self) -> String { + self.0 + .iter() + .fold("".to_string(), |mut str: String, (k, v)| { + str += &*format!(" {}: {}", k, v); + str + }) + } +} + +impl<'a> tracing::field::Visit for ToStringVisitor<'a> { + fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_error( + &mut self, + field: &tracing::field::Field, + value: &(dyn std::error::Error + 'static), + ) { + self.0.insert(field.name(), format_args!("{}", value).to_string()); + } + + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + self.0.insert(field.name(), format_args!("{:?}", value).to_string()); + } +} + +pub struct TuiTracingSubscriber; + +impl Layer for TuiTracingSubscriber + where + S: tracing::Subscriber, +{ + fn on_event( + &self, + event: &tracing::Event<'_>, + _ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + + let mut visitor = ToStringVisitor::default(); + event.record(&mut visitor); + + let level = match *event.metadata().level() { + tracing::Level::ERROR => log::Level::Error, + tracing::Level::WARN => log::Level::Warn, + tracing::Level::INFO => log::Level::Info, + tracing::Level::DEBUG => log::Level::Debug, + tracing::Level::TRACE => log::Level::Trace, + }; + + + TUI_LOGGER.log( + &Record::builder() + .args(format_args!("{}", visitor.to_string())) + .level(level) + .target(event.metadata().target()) + .file(event.metadata().file()) + .line(event.metadata().line()) + .module_path(event.metadata().module_path()) + .build(), + ); + } +} \ No newline at end of file From dff49acc1200e77e14c0e49fad4ea8d339f7cac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hadnagy?= Date: Sun, 5 Mar 2023 22:57:00 +0100 Subject: [PATCH 2/5] Add docs --- src/lib.rs | 10 ++++++++-- src/tracing_subscriber.rs | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 42b30a5..a7dac33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,6 +108,12 @@ //! `tui-logger` provides a TuiSlogDrain which implements `slog::Drain` and will route all records //! it receives to the `tui-logger` widget //! +//! //! ## `tracing-subscriber` support +//! +//! `tui-logger` provides a TuiTracingSubscriberLayer which implements +//! `tracing_subscriber::Layer` and will collect all events +//! it receives to the `tui-logger` widget +//! //! ## Custom filtering //! ```rust //! #[macro_use] @@ -401,8 +407,8 @@ pub fn slog_drain() -> TuiSlogDrain { } #[cfg(feature = "tracing-support")] -pub fn tracing_subscriber_layer() -> TuiTracingSubscriber { - TuiTracingSubscriber +pub fn tracing_subscriber_layer() -> TuiTracingSubscriberLayer { + TuiTracingSubscriberLayer } /// Set the depth of the hot buffer in order to avoid message loss. diff --git a/src/tracing_subscriber.rs b/src/tracing_subscriber.rs index 45b35d2..9506d8c 100644 --- a/src/tracing_subscriber.rs +++ b/src/tracing_subscriber.rs @@ -53,9 +53,23 @@ impl<'a> tracing::field::Visit for ToStringVisitor<'a> { } } -pub struct TuiTracingSubscriber; -impl Layer for TuiTracingSubscriber +#[allow(clippy::needless_doctest_main)] +/// tracing-subscriber-compatible layer that feeds messages to `tui-logger`. +/// +/// ## Basic usage: +/// ``` +/// //use tui_logger; +/// +/// fn main() { +/// tracing_subscriber::registry() +/// .with(tui_logger::tracing_subscriber_layer()) +/// .init(); +/// info!(log, "Logging via tracing works!"); +/// } +pub struct TuiTracingSubscriberLayer; + +impl Layer for TuiTracingSubscriberLayer where S: tracing::Subscriber, { @@ -76,7 +90,6 @@ impl Layer for TuiTracingSubscriber tracing::Level::TRACE => log::Level::Trace, }; - TUI_LOGGER.log( &Record::builder() .args(format_args!("{}", visitor.to_string())) From 5ea5d925b15e5c4a54d8a3643595fa1c47428061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hadnagy?= Date: Sun, 5 Mar 2023 23:02:54 +0100 Subject: [PATCH 3/5] FMT --- src/lib.rs | 2 +- src/tracing_subscriber.rs | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a7dac33..1368869 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,7 +185,7 @@ pub use crate::circular::CircularBuffer; #[cfg(feature = "slog")] pub use crate::slog::TuiSlogDrain; #[cfg(feature = "tracing-support")] -pub use crate::tracing_subscriber::TuiTracingSubscriber; +pub use crate::tracing_subscriber::TuiTracingSubscriberLayer; struct ExtLogRecord { timestamp: DateTime, diff --git a/src/tracing_subscriber.rs b/src/tracing_subscriber.rs index 9506d8c..953fb18 100644 --- a/src/tracing_subscriber.rs +++ b/src/tracing_subscriber.rs @@ -1,8 +1,8 @@ //! `tracing-subscriber` support for `tui-logger` -use std::collections::HashMap; use super::TUI_LOGGER; use log::{self, Log, Record}; +use std::collections::HashMap; use tracing_subscriber::Layer; #[derive(Default)] @@ -21,23 +21,28 @@ impl ToStringVisitor<'_> { impl<'a> tracing::field::Visit for ToStringVisitor<'a> { fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_str(&mut self, field: &tracing::field::Field, value: &str) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_error( @@ -45,15 +50,16 @@ impl<'a> tracing::field::Visit for ToStringVisitor<'a> { field: &tracing::field::Field, value: &(dyn std::error::Error + 'static), ) { - self.0.insert(field.name(), format_args!("{}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{}", value).to_string()); } fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - self.0.insert(field.name(), format_args!("{:?}", value).to_string()); + self.0 + .insert(field.name(), format_args!("{:?}", value).to_string()); } } - #[allow(clippy::needless_doctest_main)] /// tracing-subscriber-compatible layer that feeds messages to `tui-logger`. /// @@ -70,15 +76,14 @@ impl<'a> tracing::field::Visit for ToStringVisitor<'a> { pub struct TuiTracingSubscriberLayer; impl Layer for TuiTracingSubscriberLayer - where - S: tracing::Subscriber, +where + S: tracing::Subscriber, { fn on_event( &self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>, ) { - let mut visitor = ToStringVisitor::default(); event.record(&mut visitor); @@ -101,4 +106,4 @@ impl Layer for TuiTracingSubscriberLayer .build(), ); } -} \ No newline at end of file +} From 015b14c59659407c5efc1959c8439224360375da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hadnagy?= Date: Sun, 5 Mar 2023 23:09:35 +0100 Subject: [PATCH 4/5] Make Clippy happy --- src/tracing_subscriber.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/tracing_subscriber.rs b/src/tracing_subscriber.rs index 953fb18..5dd84a3 100644 --- a/src/tracing_subscriber.rs +++ b/src/tracing_subscriber.rs @@ -3,19 +3,17 @@ use super::TUI_LOGGER; use log::{self, Log, Record}; use std::collections::HashMap; +use std::fmt; use tracing_subscriber::Layer; #[derive(Default)] struct ToStringVisitor<'a>(HashMap<&'a str, String>); -impl ToStringVisitor<'_> { - fn to_string<'a>(&self) -> String { +impl fmt::Display for ToStringVisitor<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 .iter() - .fold("".to_string(), |mut str: String, (k, v)| { - str += &*format!(" {}: {}", k, v); - str - }) + .try_for_each(|(k, v)| -> fmt::Result { write!(f, " {}: {}", k, v) }) } } @@ -97,7 +95,7 @@ where TUI_LOGGER.log( &Record::builder() - .args(format_args!("{}", visitor.to_string())) + .args(format_args!("{}", visitor)) .level(level) .target(event.metadata().target()) .file(event.metadata().file()) From d7c95e383ce5b708dd5c2494f7053eeff2863bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81kos=20Hadnagy?= Date: Mon, 6 Mar 2023 11:42:30 +0100 Subject: [PATCH 5/5] Make tracing-subscriber-support opt-in --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d6775a..7896ba8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,6 @@ termion = "1.5" env_logger = "0.10.0" [features] -default = ["slog", "tracing-subscriber", "tracing-support"] +default = ["slog"] tracing-support = ["tracing", "tracing-subscriber"]