From b54a3b7011c4fb9ca5327e5b13afe04f468c4d79 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sun, 5 Nov 2023 19:37:33 +0800 Subject: [PATCH] Specialize target and level arguments that can be statically determined --- src/__private_api.rs | 279 +++++++++++++++++++++++++++++-------------- src/macros.rs | 132 ++++++++++++++------ 2 files changed, 287 insertions(+), 124 deletions(-) diff --git a/src/__private_api.rs b/src/__private_api.rs index 359deebbc..7815b71d6 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -1,89 +1,190 @@ -//! WARNING: this is not part of the crate's public API and is subject to change at any time - -use self::sealed::KVs; -use crate::{Level, Metadata, Record}; -use std::fmt::Arguments; -pub use std::{file, format_args, line, module_path, stringify}; - -#[cfg(feature = "kv_unstable")] -pub type Value<'a> = dyn crate::kv::value::ToValue + 'a; - -#[cfg(not(feature = "kv_unstable"))] -pub type Value<'a> = str; - -mod sealed { - /// Types for the `kv` argument. - pub trait KVs<'a> { - fn into_kvs(self) -> Option<&'a [(&'a str, &'a super::Value<'a>)]>; - } -} - -// Types for the `kv` argument. - -impl<'a> KVs<'a> for &'a [(&'a str, &'a Value<'a>)] { - #[inline] - fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { - Some(self) - } -} - -impl<'a> KVs<'a> for () { - #[inline] - fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { - None - } -} - -// Log implementation. - -fn log_impl( - args: Arguments, - level: Level, - &(target, module_path, file): &(&str, &'static str, &'static str), - line: u32, - kvs: Option<&[(&str, &Value)]>, -) { - #[cfg(not(feature = "kv_unstable"))] - if kvs.is_some() { - panic!( - "key-value support is experimental and must be enabled using the `kv_unstable` feature" - ) - } - - let mut builder = Record::builder(); - - builder - .args(args) - .level(level) - .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)); - - #[cfg(feature = "kv_unstable")] - builder.key_values(&kvs); - - crate::logger().log(&builder.build()); -} - -pub fn log<'a, K>( - args: Arguments, - level: Level, - target_module_path_and_file: &(&str, &'static str, &'static str), - line: u32, - kvs: K, -) where - K: KVs<'a>, -{ - log_impl( - args, - level, - target_module_path_and_file, - line, - kvs.into_kvs(), - ) -} - -pub fn enabled(level: Level, target: &str) -> bool { - crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) -} +//! WARNING: this is not part of the crate's public API and is subject to change at any time + +use self::sealed::{KVs, Level as LevelTrait, Target}; +use crate::{Level, LevelFilter, Metadata, Record}; +use std::cmp::Ordering; +use std::fmt::Arguments; +pub use std::{file, format_args, line, module_path, stringify}; + +#[cfg(feature = "kv_unstable")] +pub type Value<'a> = dyn crate::kv::value::ToValue + 'a; + +#[cfg(not(feature = "kv_unstable"))] +pub type Value<'a> = str; + +mod sealed { + /// Types for the `target` argument. + pub trait Target<'a>: Copy { + fn into_target(self, module_path: &'static str) -> &'a str; + } + + /// Types for the `level` argument. + pub trait Level: Copy { + fn into_level(self) -> crate::Level; + } + + /// Types for the `kv` argument. + pub trait KVs<'a> { + fn into_kvs(self) -> Option<&'a [(&'a str, &'a super::Value<'a>)]>; + } +} + +// Types for the `target` argument. + +/// Caller specified target explicitly. +impl<'a> Target<'a> for &'a str { + #[inline] + fn into_target(self, _module_path: &'static str) -> &'a str { + self + } +} + +/// Caller did not specified target. +impl<'a> Target<'a> for () { + #[inline] + fn into_target(self, module_path: &'static str) -> &'a str { + module_path + } +} + +// Types for the `kvs` argument. + +/// Caller specified key-value data explicitly. +impl<'a> KVs<'a> for &'a [(&'a str, &'a Value<'a>)] { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { + Some(self) + } +} + +/// Caller did not specify key-value data. +impl<'a> KVs<'a> for () { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { + None + } +} + +// Types for the `level` argument. + +/// The log level is dynamically determined. +impl LevelTrait for Level { + #[inline] + fn into_level(self) -> Level { + self + } +} + +macro_rules! define_static_levels { + ($(($name:ident, $level:ident),)*) => {$( + #[derive(Clone, Copy, Debug)] + pub struct $name; + + /// The log level is statically determined. + impl LevelTrait for $name { + #[inline] + fn into_level(self) -> Level { + Level::$level + } + } + + impl PartialEq for $name { + #[inline] + fn eq(&self, other: &LevelFilter) -> bool { + self.into_level().eq(other) + } + } + + impl PartialOrd for $name { + #[inline] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + self.into_level().partial_cmp(other) + } + } + )*}; +} + +define_static_levels![ + (LevelError, Error), + (LevelWarn, Warn), + (LevelInfo, Info), + (LevelDebug, Debug), + (LevelTrace, Trace), +]; + +// Log implementation. + +/// Group arguments that can be shared by different log calls together in order to reduce binary size. +#[derive(Debug)] +pub struct Meta<'a> { + pub level: Level, + pub target: &'a str, + pub module_path: &'static str, + pub file: &'static str, +} + +fn log_impl( + args: Arguments, + &Meta { + level, + target, + module_path, + file, + }: &Meta, + line: u32, + kvs: Option<&[(&str, &Value)]>, +) { + #[cfg(not(feature = "kv_unstable"))] + if kvs.is_some() { + panic!( + "key-value support is experimental and must be enabled using the `kv_unstable` feature" + ) + } + + let mut builder = Record::builder(); + + builder + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(file)) + .line(Some(line)); + + #[cfg(feature = "kv_unstable")] + builder.key_values(&kvs); + + crate::logger().log(&builder.build()); +} + +/// Group arguments that can be shared by different log calls together in order to reduce binary size. +#[derive(Debug)] +pub struct GenericMeta { + pub level: L, + pub target: T, + pub module_path: &'static str, + pub file: &'static str, +} + +pub fn log<'a, 'b, T, L, K>(args: Arguments, meta: &GenericMeta, line: u32, kvs: K) +where + T: Target<'a>, + L: LevelTrait, + K: KVs<'b>, +{ + log_impl( + args, + &Meta { + level: meta.level.into_level(), + target: meta.target.into_target(meta.module_path), + module_path: meta.module_path, + file: meta.file, + }, + line, + kvs.into_kvs(), + ) +} + +pub fn enabled(level: Level, target: &str) -> bool { + crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) +} diff --git a/src/macros.rs b/src/macros.rs index 30d7c10a0..deef782ed 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,6 +8,76 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[doc(hidden)] +#[macro_export] +macro_rules! __private_api_log_impl { + ( + @impl + $target:expr => $target_ty:ty, + $lvl:expr => $lvl_ty:ty, + $kvs:expr => $kvs_ty:ty, + $($arg:tt)+ + ) => {{ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api::log::<$target_ty, $lvl_ty, $kvs_ty>( + $crate::__private_api::format_args!($($arg)+), + &$crate::__private_api::GenericMeta { + level: $lvl, + target: $target, + module_path: $crate::__private_api::module_path!(), + file: $crate::__private_api::file!(), + }, + $crate::__private_api::line!(), + $kvs, + ); + } + }}; + + // Parse key value data. + + (@parse_kvs $target:expr => $target_ty:ty, $lvl:expr => $lvl_ty:ty, $($key:tt = $value:expr),+; $($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @impl + $target => $target_ty, + $lvl => $lvl_ty, + &[$(($crate::__log_key!($key), &$value)),+] => &_, + $($arg)+ + ) + ); + + (@parse_kvs $target:expr => $target_ty:ty, $lvl:expr => $lvl_ty:ty, $($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @impl + $target => $target_ty, + $lvl => $lvl_ty, + () => (), + $($arg)+ + ) + ); + + // Parse level. + + (@parse_level $target:expr => $target_ty:ty, $lvl:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(@parse_kvs $target => $target_ty, $lvl => $crate::Level, $($arg)+) + ); + + (@parse_level $target:expr => $target_ty:ty, @static $lvl:ident, $($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @parse_kvs + $target => $target_ty, + $crate::__private_api::$lvl => $crate::__private_api::$lvl, + $($arg)+ + ) + ); + + // Parse target. + + (target: $target:expr, $($arg:tt)+) => ($crate::__private_api_log_impl!(@parse_level $target => &_, $($arg)+)); + + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@parse_level () => (), $($arg)+)); +} + /// The standard logging macro. /// /// This macro will generically log with the specified `Level` and `format!` @@ -30,35 +100,17 @@ #[macro_export] macro_rules! log { // log!(target: "my_target", Level::Info, key1 = 42, key2 = true; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({ - let lvl = $lvl; - if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api::log::<&_>( - $crate::__private_api::format_args!($($arg)+), - lvl, - &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), - $crate::__private_api::line!(), - &[$(($crate::__log_key!($key), &$value)),+] - ); - } - }); + (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, $lvl, $($key = $value),+; $($arg)+) + ); // log!(target: "my_target", Level::Info, "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ - let lvl = $lvl; - if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api::log( - $crate::__private_api::format_args!($($arg)+), - lvl, - &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), - $crate::__private_api::line!(), - (), - ); - } - }); + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, $lvl, $($arg)+) + ); // log!(Level::Info, "a log event") - ($lvl:expr, $($arg:tt)+) => ($crate::log!(target: $crate::__private_api::module_path!(), $lvl, $($arg)+)); + ($lvl:expr, $($arg:tt)+) => ($crate::__private_api_log_impl!($lvl, $($arg)+)); } /// Logs a message at the error level. @@ -79,10 +131,12 @@ macro_rules! log { macro_rules! error { // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // error!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Error, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, @static LevelError, $($arg)+) + ); // error!("a {} event", "log") - ($($arg:tt)+) => ($crate::log!($crate::Level::Error, $($arg)+)) + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@static LevelError, $($arg)+)); } /// Logs a message at the warn level. @@ -103,10 +157,12 @@ macro_rules! error { macro_rules! warn { // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // warn!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Warn, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, @static LevelWarn, $($arg)+) + ); // warn!("a {} event", "log") - ($($arg:tt)+) => ($crate::log!($crate::Level::Warn, $($arg)+)) + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@static LevelWarn, $($arg)+)); } /// Logs a message at the info level. @@ -129,10 +185,12 @@ macro_rules! warn { macro_rules! info { // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // info!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Info, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, @static LevelInfo, $($arg)+) + ); // info!("a {} event", "log") - ($($arg:tt)+) => ($crate::log!($crate::Level::Info, $($arg)+)) + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@static LevelInfo, $($arg)+)); } /// Logs a message at the debug level. @@ -154,10 +212,12 @@ macro_rules! info { macro_rules! debug { // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // debug!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Debug, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, @static LevelDebug, $($arg)+) + ); // debug!("a {} event", "log") - ($($arg:tt)+) => ($crate::log!($crate::Level::Debug, $($arg)+)) + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@static LevelDebug, $($arg)+)); } /// Logs a message at the trace level. @@ -181,10 +241,12 @@ macro_rules! debug { macro_rules! trace { // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") // trace!(target: "my_target", "a {} event", "log") - (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Trace, $($arg)+)); + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!(target: $target, @static LevelTrace, $($arg)+) + ); // trace!("a {} event", "log") - ($($arg:tt)+) => ($crate::log!($crate::Level::Trace, $($arg)+)) + ($($arg:tt)+) => ($crate::__private_api_log_impl!(@static LevelTrace, $($arg)+)); } /// Determines if a message logged at the specified level in that module will