diff --git a/Cargo.lock b/Cargo.lock index 83986b5..3b5138c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,9 +155,10 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "sheen" -version = "0.2.0" +version = "0.3.0" dependencies = [ "chrono", + "log", "owo-colors", ] diff --git a/Cargo.toml b/Cargo.toml index 73978fc..5f60565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sheen" -version = "0.2.0" +version = "0.3.0" edition = "2024" license = "MIT" repository = "https://github.com/arferreira/sheen" @@ -13,3 +13,13 @@ categories = ["development-tools::debugging"] [dependencies] owo-colors = "4.2.3" chrono = { version = "0.4.43", default-features = false, features = ["clock"] } +# Log impl +log = { version = "0.4.29", optional = true, features = ["std"] } + + +[dev-dependencies] +log = "0.4.29" + +[[example]] +name = "log_adapter" +required-features = ["log"] diff --git a/README.md b/README.md index 9394d11..46be54f 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,20 @@ sheen is inspired by [charmbracelet/log](https://github.com/charmbracelet/log), - TTY detection (auto-disables colors when piped) - Builder pattern configuration - Zero config defaults +- `log` crate compatibility (optional feature flag) ## Installation ```toml [dependencies] -sheen = "0.2" +sheen = "0.3" +``` + +With `log` crate support: + +```toml +[dependencies] +sheen = { version = "0.3", features = ["log"] } ``` ## Quick Start @@ -90,6 +98,25 @@ Output: 14:32:15 INFO completed request_id="abc123" status=200 ``` +## Log Crate Integration + +Enable the `log` feature to use sheen as a backend for the [`log`](https://crates.io/crates/log) crate. This captures logs from any dependency that uses `log::info!()`, `log::warn!()`, etc. + +```rust +use sheen::{Logger, Level}; + +fn main() { + Logger::new() + .level(Level::Debug) + .init() + .unwrap(); + + // Standard log macros now go through sheen + log::info!("server started"); + log::warn!("cache nearly full"); +} +``` + ## Formatters ### Text (default) diff --git a/examples/log_adapter.rs b/examples/log_adapter.rs new file mode 100644 index 0000000..997b856 --- /dev/null +++ b/examples/log_adapter.rs @@ -0,0 +1,13 @@ +use sheen::{Level, Logger}; + +fn main() { + // sheen as the log backend + Logger::new().level(Level::Trace).init().unwrap(); + + // These are log crate macros, not sheen macros + log::trace!("starting up"); + log::debug!("loading configuration"); + log::info!("server listening on port 3000"); + log::warn!("cache is nearly full"); + log::error!("failed to connect to database"); +} diff --git a/src/level.rs b/src/level.rs index 2d9120d..cf3633a 100644 --- a/src/level.rs +++ b/src/level.rs @@ -19,6 +19,34 @@ impl Level { } } +// Converting log::Level to sheen::Level +#[cfg(feature = "log")] +impl From for Level { + fn from(level: log::Level) -> Self { + match level { + log::Level::Trace => Level::Trace, + log::Level::Debug => Level::Debug, + log::Level::Info => Level::Info, + log::Level::Warn => Level::Warn, + log::Level::Error => Level::Error, + } + } +} + +// reverse +#[cfg(feature = "log")] +impl From for log::LevelFilter { + fn from(level: Level) -> Self { + match level { + Level::Trace => log::LevelFilter::Trace, + Level::Debug => log::LevelFilter::Debug, + Level::Info => log::LevelFilter::Info, + Level::Warn => log::LevelFilter::Warn, + Level::Error => log::LevelFilter::Error, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index cc4422e..8388be3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,9 @@ mod level; mod logger; mod macros; +#[cfg(feature = "log")] +mod log_adapter; + pub use formatter::{Formatter, JsonFormatter, LogFmtFormatter, TextFormatter}; pub use global::{info, init, init_with}; pub use level::Level; diff --git a/src/log_adapter.rs b/src/log_adapter.rs new file mode 100644 index 0000000..24ebc03 --- /dev/null +++ b/src/log_adapter.rs @@ -0,0 +1,20 @@ +use crate::Logger; + +impl log::Log for Logger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + self.enabled(metadata.level().into()) + } + fn log(&self, record: &log::Record) { + if !self.enabled(record.level().into()) { + return; + } + let msg = format!("{}", record.args()); + let target = record.target(); + self.log( + record.level().into(), + &msg, + &[("target", &target as &dyn std::fmt::Debug)], + ); + } + fn flush(&self) {} +} diff --git a/src/logger.rs b/src/logger.rs index d08d0b2..e8cb925 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -118,3 +118,13 @@ impl Default for Logger { } } } + +#[cfg(feature = "log")] +impl Logger { + pub fn init(self) -> Result<(), log::SetLoggerError> { + let max_level: log::LevelFilter = self.level.into(); + log::set_boxed_logger(Box::new(self))?; + log::set_max_level(max_level); + Ok(()) + } +}