From 40a54b71df926e763277a5f14b2cda835c0da5e3 Mon Sep 17 00:00:00 2001 From: Nicolas Stalder Date: Sat, 28 Nov 2020 13:20:51 +0100 Subject: [PATCH] Towards "default local" logging macros --- Cargo.toml | 11 +- Makefile | 14 +- delog-examples/Cargo.toml | 16 ++ delog-examples/Makefile | 5 + delog-examples/README.md | 9 + .../examples}/around.rs | 6 +- {examples => delog-examples/examples}/log.rs | 8 +- delog-examples/examples/try-log.rs | 37 +++ delog-examples/src/lib.rs | 0 examples/hex.rs | 15 +- examples/try_log.rs | 24 -- gate-tests/Cargo.toml | 8 +- gate-tests/Makefile | 1 + gate-tests/lib-a/Cargo.toml | 1 - gate-tests/lib-a/src/lib.rs | 19 +- gate-tests/lib-b/Cargo.toml | 1 - gate-tests/lib-b/src/lib.rs | 25 +- gate-tests/src/main.rs | 14 +- qemu-tests/Cargo.toml | 8 +- qemu-tests/Makefile | 1 + qemu-tests/src/main.rs | 4 +- src/example.rs | 22 +- src/flushers.rs | 8 +- src/hex.rs | 4 +- src/lib.rs | 18 +- src/log_macros_global.rs | 159 ++++++----- src/log_macros_local.rs | 255 ++++++++++++++++-- src/logger.rs | 28 +- src/render.rs | 14 + src/renderers.rs | 41 +++ 30 files changed, 575 insertions(+), 201 deletions(-) create mode 100644 delog-examples/Cargo.toml create mode 100644 delog-examples/Makefile create mode 100644 delog-examples/README.md rename {examples => delog-examples/examples}/around.rs (60%) rename {examples => delog-examples/examples}/log.rs (68%) create mode 100644 delog-examples/examples/try-log.rs create mode 100644 delog-examples/src/lib.rs delete mode 100644 examples/try_log.rs create mode 100644 src/renderers.rs diff --git a/Cargo.toml b/Cargo.toml index 8487b8f..5151943 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "delog" version = "0.1.0-alpha.1" -description = "Deferred logging, an implementation and extension of `log`" +description = "Deferred logging, an implementation and extension of Rust's standard logging facade." authors = ["Nicolas Stalder "] license = "Apache-2.0 OR MIT" repository = "https://github.com/nickray/delog" @@ -12,7 +12,7 @@ keywords = ["log", "logging", "formatting"] edition = "2018" [package.metadata.docs.rs] -all-features = true +features = ["example"] targets = [] [dependencies] @@ -26,9 +26,8 @@ insta = "1.3.0" [features] default = ["fallible", "immediate"] -std = [] -example = ["std", "flushers"] -flushers = [] -semihosting = ["cortex-m-semihosting"] fallible = [] immediate = [] +example = ["std-example-flushers"] +semihosting-example-flushers = ["cortex-m-semihosting"] +std-example-flushers = [] diff --git a/Makefile b/Makefile index d0168ef..628f8a1 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,21 @@ test: non-qemu-tests qemu-tests -non-qemu-tests: +non-qemu-tests: simple-tests delog-examples gate-tests + +simple-tests: cargo test --lib cargo test --doc - cargo test --examples --features flushers,std + cargo test --examples + +.PHONY: delog-examples +delog-examples: + $(MAKE) -C delog-examples run + +.PHONY: gate-tests +gate-tests: $(MAKE) -C gate-tests +.PHONY: qemu-tests qemu-tests: $(MAKE) -C qemu-tests test diff --git a/delog-examples/Cargo.toml b/delog-examples/Cargo.toml new file mode 100644 index 0000000..7f97111 --- /dev/null +++ b/delog-examples/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "examples" +version = "0.1.0" +authors = ["Nicolas Stalder "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies.delog] +path = ".." +features = ["std-example-flushers"] + +[features] +default = ["log-all"] +log-none = [] +log-all = [] diff --git a/delog-examples/Makefile b/delog-examples/Makefile new file mode 100644 index 0000000..6596343 --- /dev/null +++ b/delog-examples/Makefile @@ -0,0 +1,5 @@ +run: + cargo run --example log + cargo run --example try-log + cargo run --example try-log --features log-none + cargo run --example around diff --git a/delog-examples/README.md b/delog-examples/README.md new file mode 100644 index 0000000..558df5e --- /dev/null +++ b/delog-examples/README.md @@ -0,0 +1,9 @@ +# delog-examples + +While it's possible to have multi-file examples, the usual examples mechanism +in `cargo` does not support defining features (we need to set a subset of the `log-*` +features to see output from the "local" logging macros), not requiring features +(for convenience, we use the `std-example-flushers` feature). + +So this is a "library" that sets things up, apart from that the examples here +have the same role as normal examples for `delog` would have. diff --git a/examples/around.rs b/delog-examples/examples/around.rs similarity index 60% rename from examples/around.rs rename to delog-examples/examples/around.rs index b5eb851..15b11c7 100644 --- a/examples/around.rs +++ b/delog-examples/examples/around.rs @@ -1,8 +1,7 @@ #[macro_use] extern crate delog; -#[cfg(not(all(feature = "flushers", feature = "std")))] -compile_error!("This example needs the `flushers` and `std` features"); +use delog::upstream::info; use delog::flushers::StdoutFlusher; @@ -11,10 +10,11 @@ delog!(Delogger, 25, StdoutFlusher); static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; fn main() { - Delogger::init(log::LevelFilter::Info, &STDOUT_FLUSHER).ok(); + Delogger::init_default(delog::LevelFilter::Info, &STDOUT_FLUSHER).ok(); let msg = "1234567890"; + // this logs more than fits the buffer (0..10).for_each(|_| { info!("{}", msg); Delogger::flush(); diff --git a/examples/log.rs b/delog-examples/examples/log.rs similarity index 68% rename from examples/log.rs rename to delog-examples/examples/log.rs index 378be41..bb5702e 100644 --- a/examples/log.rs +++ b/delog-examples/examples/log.rs @@ -1,17 +1,15 @@ #[macro_use] extern crate delog; -#[cfg(not(all(feature = "flushers", feature = "std")))] -compile_error!("This example needs the `flushers` and `std` features"); - use delog::flushers::StdoutFlusher; delog!(Delogger, 256, StdoutFlusher); +local_macros!(); -static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; +static FLUSHER: StdoutFlusher = StdoutFlusher {}; fn main() { - Delogger::init(log::LevelFilter::Info, &STDOUT_FLUSHER).ok(); + Delogger::init_default(delog::LevelFilter::Info, &FLUSHER).ok(); // do some serious work warn!("This is a warning"); diff --git a/delog-examples/examples/try-log.rs b/delog-examples/examples/try-log.rs new file mode 100644 index 0000000..aa13b8d --- /dev/null +++ b/delog-examples/examples/try-log.rs @@ -0,0 +1,37 @@ +#[macro_use] +extern crate delog; + +use delog::flushers::StdoutFlusher; +use delog::renderers::RipgrepRenderer; +delog!(Delogger, 196, StdoutFlusher, renderer: RipgrepRenderer); +local_macros!(); + +static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; +static RENDERER: RipgrepRenderer = RipgrepRenderer {}; + +fn main() { + Delogger::init(delog::LevelFilter::Info, &STDOUT_FLUSHER, &RENDERER).ok(); + + // do some serious work + global_try_warn!("This is a warning").unwrap(); + global_try_info!("This is information").unwrap(); + global_try_warn!("This is a warning").unwrap(); + global_try_info!("This is information").expect_err("should error out due to incapacity"); + + // flush the logs + Delogger::flush(); + + println!("==="); + + try_warn!("This is a warning").unwrap(); + try_info!("This is information").unwrap(); + try_warn!(target: "!", "This is a warning").unwrap(); + try_warn!("This is a warning").unwrap(); + #[cfg(not(feature = "log-none"))] + try_info!("This is information").expect_err("should error out due to incapacity"); + #[cfg(feature = "log-none")] + try_info!("This is information").ok(); + + // flush the logs + Delogger::flush(); +} diff --git a/delog-examples/src/lib.rs b/delog-examples/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/examples/hex.rs b/examples/hex.rs index 8c8de6d..7ddcc38 100644 --- a/examples/hex.rs +++ b/examples/hex.rs @@ -1,13 +1,10 @@ -use delog::hex::*; +use delog::hex_str; -// these examples are also `insta` tests, -// see for expected outputs fn main() { let buf = [1u8, 2, 3, 0xA1, 0xB7, 0xFF, 0x3]; - println!("'{:02X}'", hex_str_1(&buf)); - println!("'{:02X}'", hex_str_2(&buf)); - println!("'{:02x}'", delog::hex_str!(&buf, 2)); - println!("'{:02X}'", hex_str_4(&buf)); - println!("'{:02X}'", hex_str_4(&buf[..])); - println!("'{:X}'", hex_str_4(&buf)); + println!("'{}'", hex_str!(&buf)); + println!("'{}'", hex_str!(&buf, 2)); + println!("'{:02x}'", hex_str!(&buf, 2)); + println!("'{}'", hex_str!(&buf, 4)); + println!("'{}'", hex_str!(&buf[..], 4)); } diff --git a/examples/try_log.rs b/examples/try_log.rs deleted file mode 100644 index 6b6704c..0000000 --- a/examples/try_log.rs +++ /dev/null @@ -1,24 +0,0 @@ -use delog::{try_info, try_warn}; - -#[cfg(not(all(feature = "flushers", feature = "std")))] -compile_error!("This example needs the `flushers` and `std` features"); - -use delog::flushers::StdoutFlusher; - -delog::delog!(Delogger, 64, StdoutFlusher); - -static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; - -fn main() { - Delogger::init(log::LevelFilter::Info, &STDOUT_FLUSHER).ok(); - - // do some serious work - try_warn!("This is a warning").unwrap(); - try_info!("This is information").unwrap(); - try_warn!("This is a warning").unwrap(); - try_info!("This is information").expect_err("should error out due to incapacity"); - - // flush the logs - Delogger::flush(); -} - diff --git a/gate-tests/Cargo.toml b/gate-tests/Cargo.toml index b115e6d..71f7ab9 100644 --- a/gate-tests/Cargo.toml +++ b/gate-tests/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +cfg-if = "1" + [dependencies.lib-a] path = "lib-a" @@ -14,5 +17,8 @@ path = "lib-b" [dependencies.delog] path = ".." -features = ["std", "flushers"] +features = ["std-example-flushers"] +[features] +default = ["verbose-renderer"] +verbose-renderer = [] diff --git a/gate-tests/Makefile b/gate-tests/Makefile index 1ce76ac..fdfcee7 100644 --- a/gate-tests/Makefile +++ b/gate-tests/Makefile @@ -1,5 +1,6 @@ something: cargo run + cargo run --no-default-features --features lib-a/log-all,lib-b/log-all cargo run --features lib-a/log-all,lib-b/log-all cargo run --features lib-a/log-all cargo run --features lib-b/log-all diff --git a/gate-tests/lib-a/Cargo.toml b/gate-tests/lib-a/Cargo.toml index 2c06f35..ee40fde 100644 --- a/gate-tests/lib-a/Cargo.toml +++ b/gate-tests/lib-a/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [dependencies.delog] path = "../.." -features = ["std"] [features] log-error = [] diff --git a/gate-tests/lib-a/src/lib.rs b/gate-tests/lib-a/src/lib.rs index 8838579..ad7aba7 100644 --- a/gate-tests/lib-a/src/lib.rs +++ b/gate-tests/lib-a/src/lib.rs @@ -1,18 +1,15 @@ -#[macro_use] -extern crate delog; - -local_delog!(); +delog::local_macros!(); pub fn f() { info!("global info log from lib_a"); warn!("global info log from lib_a"); - local_log!(delog::Level::Info, "log level info from lib_a::f"); - local_log!(target: "!", delog::Level::Info, "log level info from lib_a::f"); - local_log!(target: "!", delog::Level::Warn, "log level warn from lib_a::f"); + log!(delog::Level::Info, "log level info from lib_a::f"); + log!(target: "!", delog::Level::Info, "log level info from lib_a::f"); + log!(target: "!", delog::Level::Warn, "log level warn from lib_a::f"); - local_info!("local info from lib_a::f"); - local_warn!("local warn from lib_a::f"); - local_info!(target: "!", "immediate local info from lib_a::f"); - local_warn!(target: "!", "immediate local warn from lib_a::f"); + info!("local info from lib_a::f"); + warn!("local warn from lib_a::f"); + info!(target: "!", "immediate local info from lib_a::f"); + warn!(target: "!", "immediate local warn from lib_a::f"); } diff --git a/gate-tests/lib-b/Cargo.toml b/gate-tests/lib-b/Cargo.toml index d68b1e6..a7c3a55 100644 --- a/gate-tests/lib-b/Cargo.toml +++ b/gate-tests/lib-b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" [dependencies.delog] path = "../.." -features = ["std"] [features] # default = ["log-warn"] diff --git a/gate-tests/lib-b/src/lib.rs b/gate-tests/lib-b/src/lib.rs index 69c1665..702ea11 100644 --- a/gate-tests/lib-b/src/lib.rs +++ b/gate-tests/lib-b/src/lib.rs @@ -1,18 +1,17 @@ -#[macro_use] -extern crate delog; - -local_delog!(); +delog::local_macros!(); pub fn g() { - info!("global info log from lib-b"); - warn!("global info log from lib-b"); + delog::upstream::info!("global info from B"); + warn!("info from B"); - local_log!(delog::Level::Info, "log level info from lib_b::g"); - local_log!(target: "!", delog::Level::Info, "log level info from lib_b::g"); - local_log!(target: "!", delog::Level::Warn, "log level warn from lib_b::g"); + log!(delog::Level::Info, "log level info from B"); + log!(target: "!", delog::Level::Info, "info from B"); + log!(target: "!", delog::Level::Warn, "warn from B"); - local_info!("local info from lib_b::g"); - local_warn!("local warn from lib_b::g"); - local_info!(target: "!", "immediate local info from lib_b::g"); - local_warn!(target: "!", "immediate local warn from lib_b::g"); + info!("info from B"); + warn!("warn from B"); + error!("error from B"); + error!("another error from B"); + info!(target: "!", "immediate local info from B"); + warn!(target: "!", "immediate local warn from B"); } diff --git a/gate-tests/src/main.rs b/gate-tests/src/main.rs index 6e0e96e..91283b2 100644 --- a/gate-tests/src/main.rs +++ b/gate-tests/src/main.rs @@ -3,12 +3,22 @@ extern crate delog; use delog::flushers::StdoutFlusher; -delog!(Delogger, 4096, StdoutFlusher); +cfg_if::cfg_if! { + if #[cfg(feature = "verbose-renderer")] { + use delog::renderers::RipgrepRenderer; + delog!(Delogger, 4096, StdoutFlusher, renderer: RipgrepRenderer); + static RENDERER: RipgrepRenderer = RipgrepRenderer {}; + } else { + use delog::renderers::ArgumentsRenderer; + delog!(Delogger, 4096, StdoutFlusher); + static RENDERER: ArgumentsRenderer = ArgumentsRenderer {}; + } +} static FLUSHER: StdoutFlusher = StdoutFlusher {}; fn main() { - Delogger::init(delog::LevelFilter::Info, &FLUSHER).expect("all good"); + Delogger::init(delog::LevelFilter::Info, &FLUSHER, &RENDERER).expect("all good"); lib_a::f(); lib_b::g(); println!("{:?}", delog::trylogger().unwrap().statistics()); diff --git a/qemu-tests/Cargo.toml b/qemu-tests/Cargo.toml index 48e35fa..a02ab8b 100644 --- a/qemu-tests/Cargo.toml +++ b/qemu-tests/Cargo.toml @@ -7,13 +7,19 @@ description = "Test salty using QEMU musca-b1" license = "Apache-2.0 OR MIT" [dependencies] -cortex-m = "0.6.1" +cortex-m = "0.6.0" cortex-m-rt = "0.6.10" cortex-m-semihosting = "0.3.5" panic-semihosting = { version = "0.5.3", features = ["exit"] } [dependencies.delog] path = ".." +features = ["semihosting-example-flushers"] + +[features] +default = ["log-all"] +log-all = [] +log-none = [] [profile.release] codegen-units = 1 diff --git a/qemu-tests/Makefile b/qemu-tests/Makefile index c67c342..9b5c888 100644 --- a/qemu-tests/Makefile +++ b/qemu-tests/Makefile @@ -1,5 +1,6 @@ main-test: cargo run --release + cargo run --release --features log-none test: @echo "\nTesting debug build\n" diff --git a/qemu-tests/src/main.rs b/qemu-tests/src/main.rs index 8006f99..1696910 100644 --- a/qemu-tests/src/main.rs +++ b/qemu-tests/src/main.rs @@ -17,6 +17,8 @@ impl delog::Flusher for SemihostingFlusher { } } +delog::local_macros!(); + delog!(Delogger, 256, SemihostingFlusher); static SEMIHOSTING_FLUSHER: SemihostingFlusher = SemihostingFlusher {}; @@ -37,7 +39,7 @@ fn test_runs() { #[entry] fn main() -> ! { - Delogger::init(delog::LevelFilter::Debug, &SEMIHOSTING_FLUSHER).ok(); + Delogger::init_default(delog::LevelFilter::Debug, &SEMIHOSTING_FLUSHER).ok(); test_runs(); diff --git a/src/example.rs b/src/example.rs index 7b92ff0..b9c55c5 100644 --- a/src/example.rs +++ b/src/example.rs @@ -1,11 +1,27 @@ -//! An example deferred logger, generated as `delog!(Delogger, 4096, StdoutFlusher)`. +//! An example deferred logger, generated as +//! `delog!(Delogger, 4096, StdoutFlusher, ArgumentsRenderer)`. //! //! It is included here for documentation purposes only. //! //! Do ensure that the `example` feature is not active in production! +//! +//! ``` +//! use delog::flushers::StdoutFlusher; +//! delog!(Delogger, 256, StdoutFlusher); +//! static STDOUT_FLUSHER: StdoutFlusher = StdoutFlusher {}; +//! Delogger::init(log::LevelFilter::Info, &STDOUT_FLUSHER).ok(); +//! +//! warn!("This is a warning"); +//! info_now!("This is IMMEDIATE information"); +//! info!("twenty-four bits '{}'", delog::hex_str!(&[0xa1u8, 0xfF, 0x03])); +//! +//! Delogger::flush(); +//! ``` use crate::flushers::StdoutFlusher; +use crate::renderers::ArgumentsRenderer; -crate::delog!(Delogger, 4096, StdoutFlusher); +crate::delog!(Delogger, 4096, StdoutFlusher, renderer: ArgumentsRenderer); + +crate::local_macros!(); -crate::local_delog!(); diff --git a/src/flushers.rs b/src/flushers.rs index 3c3ddad..2e18737 100644 --- a/src/flushers.rs +++ b/src/flushers.rs @@ -6,12 +6,12 @@ //! selected. Additionally setting `std` gives stdout/stderr flushers, //! while additionally setting `semihosting` gives flushers to host's stdout/stderr. -#[cfg(any(feature = "std", test))] +#[cfg(any(feature = "std-example-flushers", test))] mod std; -#[cfg(any(feature = "std", test))] +#[cfg(any(feature = "std-example-flushers", test))] pub use crate::flushers::std::*; -#[cfg(feature = "semihosting")] +#[cfg(feature = "semihosting-example-flushers")] mod semihosting; -#[cfg(feature = "semihosting")] +#[cfg(feature = "semihosting-example-flushers")] pub use crate::flushers::semihosting::*; diff --git a/src/hex.rs b/src/hex.rs index f5ed57e..7e65c6e 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -270,7 +270,7 @@ pub fn hex_str_64(value: &T) -> HexStr fmt::Debug for HexStr<'_, T, S, U> +impl fmt::Debug for HexStr<'_, T, S, U> where T: AsRef<[u8]>, S: Separator, @@ -282,7 +282,7 @@ where } } -impl fmt::Display for HexStr<'_, T, S, U> +impl fmt::Display for HexStr<'_, T, S, U> where T: AsRef<[u8]>, S: Separator, diff --git a/src/lib.rs b/src/lib.rs index 069f185..93157d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,22 +65,25 @@ //! #![deny(missing_docs)] -#![cfg_attr(not(any(feature = "std", test)), no_std)] +#![cfg_attr(not(any(feature = "std-example-flushers", test)), no_std)] use core::fmt; pub use log as upstream; -pub use log::{Level, LevelFilter}; +pub use log::{Level, LevelFilter, Record}; // TODO: figure out how to re-export `log` as module and `log!` as macro // This way, at least we can re-export `log!`, but in a weird twist of fate, // it also gets re-exported as `upstream!` (huh?!) -pub use log::{debug, error, info, log, log_enabled, trace, warn}; +// pub use log::{debug, error, info, log, log_enabled, trace, warn}; +pub use log::log_enabled; #[cfg(feature = "example")] pub mod example; -#[cfg(all(feature = "flushers", any(feature="std", feature="semihosting", test)))] +#[cfg(any(feature = "std-example-flushers", feature = "semihosting-example-flushers"))] pub mod flushers; +pub mod renderers; + pub mod hex; mod log_macros_global; @@ -101,6 +104,13 @@ pub trait Flusher: core::fmt::Debug + Send { fn flush(&self, logs: &str); } +/// A way to format logs, user supplied. +pub trait Renderer: Send + Sync { + /// Implementor must render record into `buf`, returning the slice containing the rendered + /// record. + fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8]; +} + static mut LOGGER: Option<&'static dyn logger::TryLogWithStatistics> = None; /// Returns a reference to the logger (as `TryLogWithStatistics` implementation) diff --git a/src/log_macros_global.rs b/src/log_macros_global.rs index 2df1772..8315a4e 100644 --- a/src/log_macros_global.rs +++ b/src/log_macros_global.rs @@ -7,7 +7,7 @@ mod try_global { /// Fallible version of `log!`. #[macro_export(local_inner_macros)] - macro_rules! try_log { + macro_rules! global_try_log { (target: $target:expr, $lvl:expr, $message:expr) => ({ let lvl = $lvl; if lvl <= $crate::upstream::STATIC_MAX_LEVEL && lvl <= $crate::upstream::max_level() { @@ -34,164 +34,163 @@ mod try_global { Ok(()) } }); - ($lvl:expr, $($arg:tt)+) => (try_log!(target: $crate::upstream::__log_module_path!(), $lvl, $($arg)+)) + ($lvl:expr, $($arg:tt)+) => (global_try_log!(target: $crate::upstream::__log_module_path!(), $lvl, $($arg)+)) } /// Fallible version of `debug!`. - #[macro_export(local_inner_macros)] - macro_rules! try_debug { + #[macro_export] + macro_rules! global_try_debug { (target: $target:expr, $($arg:tt)+) => ( - try_log!(target: $target, $crate::Level::Debug, $($arg)+) + global_try_log!(target: $target, $crate::Level::Debug, $($arg)+) ); ($($arg:tt)+) => ( - try_log!($crate::Level::Debug, $($arg)+) + global_try_log!($crate::Level::Debug, $($arg)+) ) } /// Fallible version of `error!`. - #[macro_export(local_inner_macros)] - macro_rules! try_error { + #[macro_export] + macro_rules! global_try_error { (target: $target:expr, $($arg:tt)+) => ( - try_log!(target: $target, $crate::Level::Error, $($arg)+) + global_try_log!(target: $target, $crate::Level::Error, $($arg)+) ); ($($arg:tt)+) => ( - try_log!($crate::Level::Error, $($arg)+) + global_try_log!($crate::Level::Error, $($arg)+) ) } /// Fallible version of `info!`. - #[macro_export(local_inner_macros)] - macro_rules! try_info { + #[macro_export] + macro_rules! global_try_info { (target: $target:expr, $($arg:tt)+) => ( - try_log!(target: $target, $crate::Level::Info, $($arg)+) + global_try_log!(target: $target, $crate::Level::Info, $($arg)+) ); ($($arg:tt)+) => ( - try_log!($crate::Level::Info, $($arg)+) + global_try_log!($crate::Level::Info, $($arg)+) ) } /// Fallible version of `trace!`. - #[macro_export(local_inner_macros)] - macro_rules! try_trace { + #[macro_export] + macro_rules! global_try_trace { (target: $target:expr, $($arg:tt)+) => ( - try_log!(target: $target, $crate::Level::Trace, $($arg)+) + global_try_log!(target: $target, $crate::Level::Trace, $($arg)+) ); ($($arg:tt)+) => ( - try_log!($crate::Level::Trace, $($arg)+) + global_try_log!($crate::Level::Trace, $($arg)+) ) } /// Fallible version of `warn!`. - #[macro_export(local_inner_macros)] - macro_rules! try_warn { + #[macro_export] + macro_rules! global_try_warn { (target: $target:expr, $($arg:tt)+) => ( - try_log!(target: $target, $crate::Level::Warn, $($arg)+) + global_try_log!(target: $target, $crate::Level::Warn, $($arg)+) ); ($($arg:tt)+) => ( - try_log!($crate::Level::Warn, $($arg)+) + global_try_log!($crate::Level::Warn, $($arg)+) ) } } -#[cfg(feature = "immediate")] -mod now_global { - /// Immediate version of `log!`. - #[macro_export(local_inner_macros)] - macro_rules! log_now { - ($lvl:expr, $($arg:tt)+) => ( - log!(target: "!", $lvl, $($arg)+) - ); - } +// #[cfg(feature = "immediate")] +// mod now_global { +// /// Immediate version of `log!`. +// #[macro_export(local_inner_macros)] +// macro_rules! log_now { +// ($lvl:expr, $($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $lvl, $($arg)+) +// ); +// } - /// Immediate version of `debug!`. - #[macro_export(local_inner_macros)] - macro_rules! debug_now { - ($($arg:tt)+) => ( - log!(target: "!", $crate::Level::Debug, $($arg)+) - ); - } +// /// Immediate version of `debug!`. +// #[macro_export(local_inner_macros)] +// macro_rules! debug_now { +// ($($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $crate::Level::Debug, $($arg)+) +// ); +// } - /// Immediate version of `error!`. - #[macro_export(local_inner_macros)] - macro_rules! error_now { - ($($arg:tt)+) => ( - log!(target: "!", $crate::Level::Error, $($arg)+) - ); - } +// /// Immediate version of `error!`. +// #[macro_export(local_inner_macros)] +// macro_rules! error_now { +// ($($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $crate::Level::Error, $($arg)+) +// ); +// } - /// Immediate version of `info!`. - #[macro_export(local_inner_macros)] - macro_rules! info_now { - ($($arg:tt)+) => ( - log!(target: "!", $crate::Level::Info, $($arg)+) - ); - } +// /// Immediate version of `info!`. +// #[macro_export(local_inner_macros)] +// macro_rules! info_now { +// ($($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $crate::Level::Info, $($arg)+) +// ); +// } - /// Immediate version of `trace!`. - #[macro_export(local_inner_macros)] - macro_rules! trace_now { - ($($arg:tt)+) => ( - log!(target: "!", $crate::Level::Trace, $($arg)+) - ); - } +// /// Immediate version of `trace!`. +// #[macro_export(local_inner_macros)] +// macro_rules! trace_now { +// ($($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $crate::Level::Trace, $($arg)+) +// ); +// } - /// Immediate version of `warn!`. - #[macro_export(local_inner_macros)] - macro_rules! warn_now { - ($($arg:tt)+) => ( - log!(target: "!", $crate::Level::Warn, $($arg)+) - ); - } -} +// /// Immediate version of `warn!`. +// #[macro_export(local_inner_macros)] +// macro_rules! warn_now { +// ($($arg:tt)+) => ( +// $crate::upstream::log!(target: "!", $crate::Level::Warn, $($arg)+) +// ); +// } +// } #[cfg(all(feature = "fallible", feature = "immediate"))] mod try_now_global { - /// Fallible immediate version of `log!`. #[macro_export(local_inner_macros)] - macro_rules! try_log_now { + macro_rules! global_try_log_now { ($lvl:expr, $($arg:tt)+) => ( - try_log!(target: "!", $lvl, $($arg)+) + $crate::upstream::try_log!(target: "!", $lvl, $($arg)+) ); } /// Fallible immediate version of `debug!`. #[macro_export(local_inner_macros)] - macro_rules! try_debug_now { + macro_rules! global_try_debug_now { ($($arg:tt)+) => ( - try_log!(target: "!", $crate::Level::Debug, $($arg)+) + $crate::upstream::try_log!(target: "!", $crate::Level::Debug, $($arg)+) ); } /// Fallible immediate version of `error!`. #[macro_export(local_inner_macros)] - macro_rules! try_error_now { + macro_rules! global_try_error_now { ($($arg:tt)+) => ( - try_log!(target: "!", $crate::Level::Error, $($arg)+) + $crate::upstream::try_log!(target: "!", $crate::Level::Error, $($arg)+) ); } /// Fallible immediate version of `info!`. #[macro_export(local_inner_macros)] - macro_rules! try_info_now { + macro_rules! global_try_info_now { ($($arg:tt)+) => ( - try_log!(target: "!", $crate::Level::Info, $($arg)+) + $crate::upstream::try_log!(target: "!", $crate::Level::Info, $($arg)+) ); } /// Fallible immediate version of `trace!`. #[macro_export(local_inner_macros)] - macro_rules! try_trace_now { + macro_rules! global_try_trace_now { ($($arg:tt)+) => ( - try_log!(target: "!", $crate::Level::Trace, $($arg)+) + $crate::upstream::try_log!(target: "!", $crate::Level::Trace, $($arg)+) ); } /// Fallible immediate version of `warn!`. #[macro_export(local_inner_macros)] - macro_rules! try_warn_now { + macro_rules! global_try_warn_now { ($($arg:tt)+) => ( - try_log!(target: "!", $crate::Level::Warn, $($arg)+) + $crate::upstream::try_log!(target: "!", $crate::Level::Warn, $($arg)+) ); } diff --git a/src/log_macros_local.rs b/src/log_macros_local.rs index 7e88308..39afbe7 100644 --- a/src/log_macros_local.rs +++ b/src/log_macros_local.rs @@ -22,79 +22,294 @@ macro_rules! with_dollar_sign { /// To see this in action, compile documentation using `cargo doc --features example`, or inspect /// the `gate-tests/` subdirectory. #[macro_export] -macro_rules! local_delog { +macro_rules! local_macros { () => { $crate::with_dollar_sign! { ($d:tt) => { /// Local version of `log!`. #[macro_export(local_inner_macros)] - macro_rules! local_log { + macro_rules! log { (target: $target:expr, $lvl:expr, $message:expr) => ( #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::log!(target: $target, $lvl, $message)); + $crate::upstream::log!(target: $target, $lvl, $message)); (target: $target:expr, $lvl:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::log!(target: $target, $lvl, $d($arg)+)); + $crate::upstream::log!(target: $target, $lvl, $d($arg)+)); ($lvl:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::log!($lvl, $d($arg)+)); + $crate::upstream::log!($lvl, $d($arg)+)); } /// Local version of `debug!`. #[macro_export(local_inner_macros)] - macro_rules! local_debug { + macro_rules! debug { (target: $target:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] - $crate::debug!(target: $target, $d($arg)+)); + $crate::upstream::debug!(target: $target, $d($arg)+)); ($d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] - $crate::debug!($d($arg)+)); + $crate::upstream::debug!($d($arg)+)); } /// Local version of `error!`. #[macro_export(local_inner_macros)] - macro_rules! local_error { + macro_rules! error { (target: $target:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] - $crate::error!(target: $target, $d($arg)+)); + $crate::upstream::error!(target: $target, $d($arg)+)); ($d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] - $crate::error!($d($arg)+)); + $crate::upstream::error!($d($arg)+)); } /// Local version of `info!`. #[macro_export(local_inner_macros)] - macro_rules! local_info { + macro_rules! info { (target: $target:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::info!(target: $target, $d($arg)+)); + $crate::upstream::info!(target: $target, $d($arg)+)); ($d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] - $crate::info!($d($arg)+)); + $crate::upstream::info!($d($arg)+)); } /// Local version of `trace!`. #[macro_export(local_inner_macros)] - macro_rules! local_trace { + macro_rules! trace { (target: $target:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] - $crate::trace!(target: $target, $d($arg)+)); + $crate::upstream::trace!(target: $target, $d($arg)+)); ($d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] - $crate::trace!($d($arg)+)); + $crate::upstream::trace!($d($arg)+)); } /// Local version of `warn!`. #[macro_export(local_inner_macros)] - macro_rules! local_warn { + macro_rules! warn { (target: $target:expr, $d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] - $crate::warn!(target: $target, $d($arg)+)); + $crate::upstream::warn!(target: $target, $d($arg)+)); ($d($arg:tt)+) => ( #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] - $crate::warn!($d($arg)+)); + $crate::upstream::warn!($d($arg)+)); } + + /// Immediate version of `log!`. + macro_rules! log_now { + ($lvl:expr, $d($arg:tt)+) => ( + log!(target: "!", $lvl, $d($arg)+) + ); + } + + /// Immediate version of `debug!`. + macro_rules! debug_now { + ($d($arg:tt)+) => ( + debug!(target: "!", $d($arg)+) + ); + } + + /// Immediate version of `error!`. + macro_rules! error_now { + ($d($arg:tt)+) => ( + error!(target: "!", $d($arg)+) + ); + } + + /// Immediate version of `info!`. + macro_rules! info_now { + ($d($arg:tt)+) => ( + info!(target: "!", $d($arg)+) + ); + } + + /// Immediate version of `trace!`. + macro_rules! trace_now { + ($d($arg:tt)+) => ( + trace!(target: "!", $d($arg)+) + ); + } + + /// Immediate version of `warn!`. + macro_rules! warn_now { + ($d($arg:tt)+) => ( + warn!(target: "!", $d($arg)+) + ); + } + + // /// Fallible version of `info!`. + // #[macro_export(local_inner_macros)] + // macro_rules! try_info { + // (target: $target:expr, $($arg:tt)+) => ( + // try_log!(target: $target, $crate::Level::Info, $($arg)+) + // ); + // ($($arg:tt)+) => ( + // try_log!($crate::Level::Info, $($arg)+) + // ) + // } + + // /// Fallible version of `log!`. + // #[macro_export(local_inner_macros)] + // macro_rules! try_log { + // (target: $target:expr, $lvl:expr, $message:expr) => ( + // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + // $crate::global_try_log!(target: $target, $lvl, $message)); + // (target: $target:expr, $lvl:expr, $d($arg:tt)+) => ( + // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + // $crate::upstream::log!(target: $target, $lvl, $d($arg)+)); + // ($lvl:expr, $d($arg:tt)+) => ( + // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + // $crate::upstream::log!($lvl, $d($arg)+)); + // } + + // /// Fallible version of `info!`. + // #[macro_export(local_inner_macros)] + // macro_rules! try_info { + + // (target: $target:expr, $d($arg:tt)+) => ( + // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + // { + // $crate::global_try_info!(target: $target, $d($arg)+) + // } + // #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] + // { + // Ok(()) + // } + // ); + + // ($d($arg:tt)+) => ( + // #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + // { + // $crate::global_try_info!($d($arg)+) + // } + // #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] + // { + // Ok(()) + // } + // ); + // } + + /// Fallible version of `debug!`. + #[cfg(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none")))] + #[macro_export(local_inner_macros)] + macro_rules! try_debug { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::global_try_debug!(target: $target, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::global_try_debug!($d($arg)+) + ); + } + + /// Fallible version of `debug!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-debug"), not(feature = "log-none"))))] + #[macro_export(local_inner_macros)] + macro_rules! try_debug { + + // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); + + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `error!`. + #[cfg(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none")))] + #[macro_export(local_inner_macros)] + macro_rules! try_error { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::global_try_error!(target: $target, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::global_try_error!($d($arg)+) + ); + } + + /// Fallible version of `error!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-error"), not(feature = "log-none"))))] + #[macro_export(local_inner_macros)] + macro_rules! try_error { + + // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); + + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `info!`. + #[cfg(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none")))] + #[macro_export(local_inner_macros)] + macro_rules! try_info { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::global_try_info!(target: $target, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::global_try_info!($d($arg)+) + ); + } + + /// Fallible version of `info!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-info"), not(feature = "log-none"))))] + #[macro_export(local_inner_macros)] + macro_rules! try_info { + + // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); + + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `trace!`. + #[cfg(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none")))] + #[macro_export(local_inner_macros)] + macro_rules! try_trace { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::global_try_trace!(target: $target, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::global_try_trace!($d($arg)+) + ); + } + + /// Fallible version of `trace!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-trace"), not(feature = "log-none"))))] + #[macro_export(local_inner_macros)] + macro_rules! try_trace { + + // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); + + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + + /// Fallible version of `warn!`. + #[cfg(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none")))] + #[macro_export(local_inner_macros)] + macro_rules! try_warn { + + (target: $target:expr, $d($arg:tt)+) => ( + $crate::global_try_warn!(target: $target, $d($arg)+) + ); + + ($d($arg:tt)+) => ( + $crate::global_try_warn!($d($arg)+) + ); + } + + /// Fallible version of `warn!`. + #[cfg(not(all(any(feature = "log-all", feature = "log-warn"), not(feature = "log-none"))))] + #[macro_export(local_inner_macros)] + macro_rules! try_warn { + + // (target: $target:expr, $d($arg:tt)+) => ( Ok() ); + + ($d($arg:tt)+) => ( core::result::Result::<(), ()>::Ok(()) ); + } + } } } diff --git a/src/logger.rs b/src/logger.rs index 37ad93c..d8820fe 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,4 +1,4 @@ -use core::{cmp, fmt, ptr}; +use core::{cmp, ptr}; use core::sync::atomic::{AtomicUsize, Ordering}; /// Semi-abstract characterization of the deferred loggers that the `delog!` macro produces. @@ -28,7 +28,7 @@ pub unsafe trait Delogger: log::Log + crate::TryLog { /// Call the flusher. fn flush(&self, logs: &str); /// Actually render the arguments (via internal static buffer). - fn render(&self, args: &fmt::Arguments) -> &'static [u8]; + fn render(&self, record: &log::Record) -> &'static [u8]; /// Capacity of circular buffer. fn capacity(&self) -> usize { self.buffer().len() } @@ -97,11 +97,22 @@ pub trait TryLogWithStatistics: TryLog { #[macro_export] macro_rules! delog { ($logger:ident, $capacity:expr, $flusher:ty) => { + delog!($logger, $capacity, $flusher, renderer: $crate::renderers::ArgumentsRenderer); + + impl $logger { + pub fn init_default(level: $crate::upstream::LevelFilter, flusher: &'static $flusher) -> Result<(), ()> { + $logger::init(level, flusher, $crate::renderers::default()) + } + } + }; + + ($logger:ident, $capacity:expr, $flusher:ty, renderer: $renderer:ty) => { #[derive(Clone, Copy)] /// Generated deferred logging implementation. pub struct $logger { flusher: &'static $flusher, + renderer: &'static $renderer, // immediate_flusher: &'static $flusher, } @@ -161,7 +172,7 @@ macro_rules! delog { #[allow(missing_docs)] impl $logger { - pub fn init(level: $crate::upstream::LevelFilter, flusher: &'static $flusher) -> Result<(), ()> { + pub fn init(level: $crate::upstream::LevelFilter, flusher: &'static $flusher, renderer: &'static $renderer) -> Result<(), ()> { use core::sync::atomic::{self, AtomicBool, AtomicUsize, Ordering}; use core::mem::MaybeUninit; @@ -171,7 +182,7 @@ macro_rules! delog { { // let logger = Self { flusher, immediate_flusher: flusher }; - let logger = Self { flusher }; + let logger = Self { flusher, renderer }; Self::get().replace(logger); $crate::trylogger().replace(Self::get().as_ref().unwrap()); $crate::upstream::set_logger(Self::get().as_ref().unwrap()) @@ -243,11 +254,12 @@ macro_rules! delog { &CLAIMED } - fn render(&self, args: &core::fmt::Arguments) -> &'static [u8] { + fn render(&self, record: &$crate::Record) -> &'static [u8] { static mut LOCAL_BUFFER: [u8; $capacity] = [0u8; $capacity]; let local_buffer = unsafe { &mut LOCAL_BUFFER }; - $crate::render::render_arguments(local_buffer, *args) + use $crate::Renderer; + self.renderer.render(local_buffer, record) } } } @@ -288,7 +300,7 @@ pub unsafe fn try_enqueue(delogger: impl Delogger, record: &log::Record) -> core if record.target() == "!" { // todo: possibly use separate immediate_flusher - let input = delogger.render(record.args()); + let input = delogger.render(record); let input = unsafe { core::str::from_utf8_unchecked(input) }; Delogger::flush(&delogger, input); delogger.log_success_count().fetch_add(1, Ordering::SeqCst); @@ -296,7 +308,7 @@ pub unsafe fn try_enqueue(delogger: impl Delogger, record: &log::Record) -> core } let capacity = delogger.capacity(); - let log = delogger.render(record.args()); + let log = delogger.render(record); let size = log.len(); let previously_claimed = loop { diff --git a/src/render.rs b/src/render.rs index e72f4e0..f7a8c3c 100644 --- a/src/render.rs +++ b/src/render.rs @@ -14,6 +14,20 @@ pub fn render_arguments<'a>(buf: &'a mut [u8], args: fmt::Arguments) -> &'a [u8] &buf[..used] } +/// Render record, based on feature flags. +pub fn render_record<'a>(buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { + if cfg!(feature = "prefix-level") { + match (record.file(), record.line()) { + (Some(file), Some(line)) => render_arguments(buf, + format_args!("{}|{}|{}:{}: {}", record.level(), record.target(), file, line, record.args())), + _ => render_arguments(buf, + format_args!("{}|{}: {}", record.level(), record.target(), record.args())), + } + } else { + render_arguments(buf, *record.args()) + } +} + // I dont' get it, why isn't this implemented already? struct WriteTo<'a> { buffer: &'a mut [u8], diff --git a/src/renderers.rs b/src/renderers.rs new file mode 100644 index 0000000..28ce569 --- /dev/null +++ b/src/renderers.rs @@ -0,0 +1,41 @@ +//! Typical renderers. + +use crate::render::render_arguments; +use crate::Renderer; + +#[derive(Clone, Copy)] +/// Renders just the `record.args()`. +pub struct ArgumentsRenderer {} + +/// The default, minimal renderer. +pub fn default() -> &'static ArgumentsRenderer { + static RENDERER: ArgumentsRenderer = ArgumentsRenderer {}; + &RENDERER +} + + +impl Renderer for ArgumentsRenderer { + fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { + render_arguments(buf, *record.args()) + } +} + +unsafe impl Send for ArgumentsRenderer {} +unsafe impl Sync for ArgumentsRenderer {} + +#[derive(Clone, Copy)] +/// Renders the `record.args()`, prefixed by level, target, and file, line if they are some. +pub struct RipgrepRenderer {} + +impl Renderer for RipgrepRenderer { + fn render<'a>(&self, buf: &'a mut [u8], record: &log::Record) -> &'a [u8] { + match (record.file(), record.line()) { + (Some(file), Some(line)) => render_arguments(buf, + format_args!("{}|{}|{}:{}: {}", record.level(), record.target(), file, line, record.args())), + (Some(file), None) => render_arguments(buf, + format_args!("{}|{}|{}: {}", record.level(), record.target(), file, record.args())), + _ => render_arguments(buf, + format_args!("{}|{}: {}", record.level(), record.target(), record.args())), + } + } +}