Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Specialize target and level arguments that can be statically determined
Browse files Browse the repository at this point in the history
EFanZh committed Dec 3, 2023
1 parent eabf70b commit 859a93f
Showing 2 changed files with 238 additions and 61 deletions.
140 changes: 114 additions & 26 deletions src/__private_api.rs
Original file line number Diff line number Diff line change
@@ -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<LevelFilter> for $name {
#[inline]
fn eq(&self, other: &LevelFilter) -> bool {
self.into_level().eq(other)
}
}

impl PartialOrd<LevelFilter> for $name {
#[inline]
fn partial_cmp(&self, other: &LevelFilter) -> Option<Ordering> {
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,33 +140,34 @@ 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);

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(),
)
}
159 changes: 124 additions & 35 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 859a93f

Please sign in to comment.