From 5187828df8f00716b1b0b8fc3d531c88575e2768 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Mon, 4 Dec 2023 00:17:38 +0800 Subject: [PATCH] Specialize target and level arguments that can be statically determined --- src/__private_api.rs | 140 ++++++++++++++++++++++++++++++------- src/macros.rs | 159 +++++++++++++++++++++++++++++++++---------- 2 files changed, 238 insertions(+), 61 deletions(-) diff --git a/src/__private_api.rs b/src/__private_api.rs index 92bd15656..bc97db4ef 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -1,7 +1,8 @@ //! 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 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}; @@ -12,36 +13,122 @@ pub type Value<'a> = dyn crate::kv::value::ToValue + 'a; pub type Value<'a> = str; mod sealed { + /// Types for the `target` argument. + pub trait Target<'a>: Copy { + fn into_target(self, module_path: &'a &'a str) -> &'a &'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>)]>; + fn into_kvs(self) -> Option<&'a &'a [(&'a str, &'a super::Value<'a>)]>; + } +} + +// Types for the `target` argument. + +/// Caller specified target explicitly. +impl<'a> Target<'a> for &'a &'a str { + #[inline] + fn into_target(self, _module_path: &'a &'a str) -> &'a &'a str { + self } } -// Types for the `kv` argument. +/// Caller did not specified target. +impl<'a> Target<'a> for () { + #[inline] + fn into_target(self, module_path: &'a &'a str) -> &'a &'a str { + module_path + } +} + +// Types for the `kvs` argument. -impl<'a> KVs<'a> for &'a [(&'a str, &'a Value<'a>)] { +/// Caller specified key-value data explicitly. +impl<'a> KVs<'a> for &'a &'a [(&'a str, &'a Value<'a>)] { #[inline] - fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { + fn into_kvs(self) -> Option<&'a &'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>)]> { + fn into_kvs(self) -> Option<&'a &'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. +/// Log arguments that are not generic. +#[derive(Debug)] +pub struct NonGenericArgs<'a> { + pub module_path_and_file: &'static (&'static str, &'static str), + pub line: u32, + pub args: Arguments<'a>, +} + +// Note that all argument types are selected to have sizes less than or equal to a single pointer size, which allows +// tail call optimizations to be applied. fn log_impl( - args: Arguments, + non_generic_args: &NonGenericArgs, + target: &&str, level: Level, - &(target, module_path, file): &(&str, &'static str, &'static str), - line: u32, - kvs: Option<&[(&str, &Value)]>, + kvs: Option<&&[(&str, &Value)]>, ) { #[cfg(not(feature = "kv_unstable"))] if kvs.is_some() { @@ -53,12 +140,12 @@ fn log_impl( let mut builder = Record::builder(); builder - .args(args) + .args(non_generic_args.args) .level(level) .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)); + .module_path_static(Some(non_generic_args.module_path_and_file.0)) + .file_static(Some(non_generic_args.module_path_and_file.1)) + .line(Some(non_generic_args.line)); #[cfg(feature = "kv_unstable")] builder.key_values(&kvs); @@ -66,20 +153,21 @@ fn log_impl( 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 +// `#[inline(never)]` is used to prevent compiler from inlining this function so that the binary size could be kept as +// small as possible. Also, the argument types are carefully selected so that the performance cost of this function +// could be kept as low as possible. +#[inline(never)] +pub fn log<'a, T, L, K>(non_generic_args: &NonGenericArgs, target: T, level: L, kvs: K) +where + T: Target<'a>, + L: LevelTrait, K: KVs<'a>, { + // For all possible generic arguments combinations, tail call optimization can be applied to this function call. log_impl( - args, - level, - target_module_path_and_file, - line, + non_generic_args, + target.into_target(&non_generic_args.module_path_and_file.0), + level.into_level(), kvs.into_kvs(), ) } diff --git a/src/macros.rs b/src/macros.rs index 44945f0d9..56886ea2d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,6 +8,103 @@ // 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 { + // The real log macro implementation. + ( + @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::NonGenericArgs { + module_path_and_file: &( + $crate::__private_api::module_path!(), + $crate::__private_api::file!(), + ), + line: $crate::__private_api::line!(), + args: $crate::__private_api::format_args!($($arg)+), + }, + $target, + $lvl, + $kvs, + ); + } + }}; + + // Parse key value data. + + // The key value data is specified explicitly. + (@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, + // Stores the parsed key value data. + &(&[$(($crate::__log_key!($key), (&$value as &$crate::__private_api::Value))),+] as &[_]) => &_, + $($arg)+ + ) + ); + + // The key value data is not specified. + (@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, + () => (), // Stores the parsed key value data. + $($arg)+ + ) + ); + + // Parse level. + + // The level is specified at runtime with the `log` macro. + (@parse_level $target:expr => $target_ty:ty, $lvl:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @parse_kvs + $target => $target_ty, + $lvl => $crate::Level, // Stores the parsed level. + $($arg)+ + ) + ); + + // The level is specified statically with the individual log macros (`info`, `warn`, ...). + (@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, // Stores the parsed level. + $($arg)+ + ) + ); + + // Parse target. + + // The target is specified explicitly. + (target: $target:expr, $($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @parse_level + &$target => &_, // Stores the parsed target. + $($arg)+ + ) + ); + + // The target is not specified explicitly. + ($($arg:tt)+) => ( + $crate::__private_api_log_impl!( + @parse_level + () => (), // Stores the parsed target. + $($arg)+ + ) + ); +} + /// The standard logging macro. /// /// This macro will generically log with the specified `Level` and `format!` @@ -30,35 +127,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 +158,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 +184,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 +212,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 +239,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 +268,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