Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialize target and level arguments that can be statically determined #603

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
106 changes: 96 additions & 10 deletions 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};

Expand All @@ -12,35 +13,113 @@ 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 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 `kv` argument.
// Types for the `target` argument.

/// Caller specified target explicitly.
impl<'a> Target<'a> for &'a str {
#[inline]
fn into_target(self, _module_path: &'a str) -> &'a str {
self
}
}

/// Caller did not specified target.
impl<'a> Target<'a> for () {
#[inline]
fn into_target(self, module_path: &'a 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<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.

fn log_impl(
args: Arguments,
level: Level,
&(target, module_path, file): &(&str, &'static str, &'static str),
&(module_path, file): &'static (&'static str, &'static str),
line: u32,
target: &str,
level: Level,
kvs: Option<&[(&str, &Value)]>,
) {
#[cfg(not(feature = "kv_unstable"))]
Expand All @@ -66,20 +145,27 @@ fn log_impl(
crate::logger().log(&builder.build());
}

pub fn log<'a, K>(
// `#[inline(never)]` is used to prevent compiler from inlining this function so that the binary size could be kept as
// small as possible.
#[inline(never)]
pub fn log<'a, T, L, K>(
args: Arguments,
level: Level,
target_module_path_and_file: &(&str, &'static str, &'static str),
module_path_and_file: &'static (&'static str, &'static str),
line: u32,
target: T,
level: L,
kvs: K,
) where
T: Target<'a>,
L: LevelTrait,
K: KVs<'a>,
{
log_impl(
args,
level,
target_module_path_and_file,
module_path_and_file,
line,
target.into_target(module_path_and_file.0),
level.into_level(),
kvs.into_kvs(),
)
}
Expand Down
156 changes: 121 additions & 35 deletions src/macros.rs
Expand Up @@ -8,6 +8,100 @@
// 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::format_args!($($arg)+),
&(
$crate::__private_api::module_path!(),
$crate::__private_api::file!(),
),
$crate::__private_api::line!(),
$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,
&[$(($crate::__log_key!($key), &$value)),+] => &_, // Stores the parsed key value data.
$($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!`
Expand All @@ -30,35 +124,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.
Expand All @@ -79,10 +155,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.
Expand All @@ -103,10 +181,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.
Expand All @@ -129,10 +209,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.
Expand All @@ -154,10 +236,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.
Expand All @@ -181,10 +265,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
Expand Down