use super::{callsite, field};
use core::{
cmp, fmt,
str::FromStr,
sync::atomic::{AtomicUsize, Ordering},
};
pub struct Metadata<'a> {
name: &'static str,
target: &'a str,
level: Level,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u32>,
fields: field::FieldSet,
kind: Kind,
}
#[derive(Clone, Eq, PartialEq)]
pub struct Kind(u8);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Level(LevelInner);
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct LevelFilter(Option<Level>);
#[derive(Clone, Debug)]
pub struct ParseLevelFilterError(());
static MAX_LEVEL: AtomicUsize = AtomicUsize::new(LevelFilter::OFF_USIZE);
impl<'a> Metadata<'a> {
pub const fn new(
name: &'static str,
target: &'a str,
level: Level,
file: Option<&'a str>,
line: Option<u32>,
module_path: Option<&'a str>,
fields: field::FieldSet,
kind: Kind,
) -> Self {
Metadata {
name,
target,
level,
module_path,
file,
line,
fields,
kind,
}
}
#[inline]
pub fn fields(&self) -> &field::FieldSet {
&self.fields
}
pub fn level(&self) -> &Level {
&self.level
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn target(&self) -> &'a str {
self.target
}
pub fn module_path(&self) -> Option<&'a str> {
self.module_path
}
pub fn file(&self) -> Option<&'a str> {
self.file
}
pub fn line(&self) -> Option<u32> {
self.line
}
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
pub fn is_event(&self) -> bool {
self.kind.is_event()
}
pub fn is_span(&self) -> bool {
self.kind.is_span()
}
#[doc(hidden)]
pub const fn private_fake_field(&self) -> field::Field {
self.fields.fake_field()
}
}
impl fmt::Debug for Metadata<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut meta = f.debug_struct("Metadata");
meta.field("name", &self.name)
.field("target", &self.target)
.field("level", &self.level);
if let Some(path) = self.module_path() {
meta.field("module_path", &path);
}
match (self.file(), self.line()) {
(Some(file), Some(line)) => {
meta.field("location", &format_args!("{}:{}", file, line));
}
(Some(file), None) => {
meta.field("file", &format_args!("{}", file));
}
(None, Some(line)) => {
meta.field("line", &line);
}
(None, None) => {}
};
meta.field("fields", &format_args!("{}", self.fields))
.field("callsite", &self.callsite())
.field("kind", &self.kind)
.finish()
}
}
impl Kind {
const EVENT_BIT: u8 = 1 << 0;
const SPAN_BIT: u8 = 1 << 1;
const HINT_BIT: u8 = 1 << 2;
pub const EVENT: Kind = Kind(Self::EVENT_BIT);
pub const SPAN: Kind = Kind(Self::SPAN_BIT);
pub const HINT: Kind = Kind(Self::HINT_BIT);
pub fn is_span(&self) -> bool {
self.0 & Self::SPAN_BIT == Self::SPAN_BIT
}
pub fn is_event(&self) -> bool {
self.0 & Self::EVENT_BIT == Self::EVENT_BIT
}
pub fn is_hint(&self) -> bool {
self.0 & Self::HINT_BIT == Self::HINT_BIT
}
pub const fn hint(self) -> Self {
Self(self.0 | Self::HINT_BIT)
}
}
impl fmt::Debug for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Kind(")?;
let mut has_bits = false;
let mut write_bit = |name: &str| {
if has_bits {
f.write_str(" | ")?;
}
f.write_str(name)?;
has_bits = true;
Ok(())
};
if self.is_event() {
write_bit("EVENT")?;
}
if self.is_span() {
write_bit("SPAN")?;
}
if self.is_hint() {
write_bit("HINT")?;
}
if !has_bits {
write!(f, "{:#b}", self.0)?;
}
f.write_str(")")
}
}
impl Eq for Metadata<'_> {}
impl PartialEq for Metadata<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if core::ptr::eq(self, other) {
true
} else if cfg!(not(debug_assertions)) {
self.callsite() == other.callsite()
} else {
let Metadata {
name: lhs_name,
target: lhs_target,
level: lhs_level,
module_path: lhs_module_path,
file: lhs_file,
line: lhs_line,
fields: lhs_fields,
kind: lhs_kind,
} = self;
let Metadata {
name: rhs_name,
target: rhs_target,
level: rhs_level,
module_path: rhs_module_path,
file: rhs_file,
line: rhs_line,
fields: rhs_fields,
kind: rhs_kind,
} = &other;
self.callsite() == other.callsite()
&& lhs_name == rhs_name
&& lhs_target == rhs_target
&& lhs_level == rhs_level
&& lhs_module_path == rhs_module_path
&& lhs_file == rhs_file
&& lhs_line == rhs_line
&& lhs_fields == rhs_fields
&& lhs_kind == rhs_kind
}
}
}
impl Level {
pub const ERROR: Level = Level(LevelInner::Error);
pub const WARN: Level = Level(LevelInner::Warn);
pub const INFO: Level = Level(LevelInner::Info);
pub const DEBUG: Level = Level(LevelInner::Debug);
pub const TRACE: Level = Level(LevelInner::Trace);
pub fn as_str(&self) -> &'static str {
match *self {
Level::TRACE => "TRACE",
Level::DEBUG => "DEBUG",
Level::INFO => "INFO",
Level::WARN => "WARN",
Level::ERROR => "ERROR",
}
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Level::TRACE => f.pad("TRACE"),
Level::DEBUG => f.pad("DEBUG"),
Level::INFO => f.pad("INFO"),
Level::WARN => f.pad("WARN"),
Level::ERROR => f.pad("ERROR"),
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for ParseLevelError {}
impl FromStr for Level {
type Err = ParseLevelError;
fn from_str(s: &str) -> Result<Self, ParseLevelError> {
s.parse::<usize>()
.map_err(|_| ParseLevelError { _p: () })
.and_then(|num| match num {
1 => Ok(Level::ERROR),
2 => Ok(Level::WARN),
3 => Ok(Level::INFO),
4 => Ok(Level::DEBUG),
5 => Ok(Level::TRACE),
_ => Err(ParseLevelError { _p: () }),
})
.or_else(|_| match s {
s if s.eq_ignore_ascii_case("error") => Ok(Level::ERROR),
s if s.eq_ignore_ascii_case("warn") => Ok(Level::WARN),
s if s.eq_ignore_ascii_case("info") => Ok(Level::INFO),
s if s.eq_ignore_ascii_case("debug") => Ok(Level::DEBUG),
s if s.eq_ignore_ascii_case("trace") => Ok(Level::TRACE),
_ => Err(ParseLevelError { _p: () }),
})
}
}
#[repr(usize)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
enum LevelInner {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
}
impl From<Level> for LevelFilter {
#[inline]
fn from(level: Level) -> Self {
Self::from_level(level)
}
}
impl From<Option<Level>> for LevelFilter {
#[inline]
fn from(level: Option<Level>) -> Self {
Self(level)
}
}
impl From<LevelFilter> for Option<Level> {
#[inline]
fn from(filter: LevelFilter) -> Self {
filter.into_level()
}
}
impl LevelFilter {
pub const OFF: LevelFilter = LevelFilter(None);
pub const ERROR: LevelFilter = LevelFilter::from_level(Level::ERROR);
pub const WARN: LevelFilter = LevelFilter::from_level(Level::WARN);
pub const INFO: LevelFilter = LevelFilter::from_level(Level::INFO);
pub const DEBUG: LevelFilter = LevelFilter::from_level(Level::DEBUG);
pub const TRACE: LevelFilter = LevelFilter(Some(Level::TRACE));
pub const fn from_level(level: Level) -> Self {
Self(Some(level))
}
pub const fn into_level(self) -> Option<Level> {
self.0
}
const ERROR_USIZE: usize = LevelInner::Error as usize;
const WARN_USIZE: usize = LevelInner::Warn as usize;
const INFO_USIZE: usize = LevelInner::Info as usize;
const DEBUG_USIZE: usize = LevelInner::Debug as usize;
const TRACE_USIZE: usize = LevelInner::Trace as usize;
const OFF_USIZE: usize = LevelInner::Error as usize + 1;
#[inline(always)]
pub fn current() -> Self {
match MAX_LEVEL.load(Ordering::Relaxed) {
Self::ERROR_USIZE => Self::ERROR,
Self::WARN_USIZE => Self::WARN,
Self::INFO_USIZE => Self::INFO,
Self::DEBUG_USIZE => Self::DEBUG,
Self::TRACE_USIZE => Self::TRACE,
Self::OFF_USIZE => Self::OFF,
#[cfg(debug_assertions)]
unknown => unreachable!(
"/!\\ `LevelFilter` representation seems to have changed! /!\\ \n\
This is a bug (and it's pretty bad). Please contact the `tracing` \
maintainers. Thank you and I'm sorry.\n \
The offending repr was: {:?}",
unknown,
),
#[cfg(not(debug_assertions))]
_ => unsafe {
core::hint::unreachable_unchecked()
},
}
}
pub(crate) fn set_max(LevelFilter(level): LevelFilter) {
let val = match level {
Some(Level(level)) => level as usize,
None => Self::OFF_USIZE,
};
MAX_LEVEL.swap(val, Ordering::AcqRel);
}
}
impl fmt::Display for LevelFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LevelFilter::OFF => f.pad("off"),
LevelFilter::ERROR => f.pad("error"),
LevelFilter::WARN => f.pad("warn"),
LevelFilter::INFO => f.pad("info"),
LevelFilter::DEBUG => f.pad("debug"),
LevelFilter::TRACE => f.pad("trace"),
}
}
}
impl fmt::Debug for LevelFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LevelFilter::OFF => f.pad("LevelFilter::OFF"),
LevelFilter::ERROR => f.pad("LevelFilter::ERROR"),
LevelFilter::WARN => f.pad("LevelFilter::WARN"),
LevelFilter::INFO => f.pad("LevelFilter::INFO"),
LevelFilter::DEBUG => f.pad("LevelFilter::DEBUG"),
LevelFilter::TRACE => f.pad("LevelFilter::TRACE"),
}
}
}
impl FromStr for LevelFilter {
type Err = ParseLevelFilterError;
fn from_str(from: &str) -> Result<Self, Self::Err> {
from.parse::<usize>()
.ok()
.and_then(|num| match num {
0 => Some(LevelFilter::OFF),
1 => Some(LevelFilter::ERROR),
2 => Some(LevelFilter::WARN),
3 => Some(LevelFilter::INFO),
4 => Some(LevelFilter::DEBUG),
5 => Some(LevelFilter::TRACE),
_ => None,
})
.or_else(|| match from {
"" => Some(LevelFilter::ERROR),
s if s.eq_ignore_ascii_case("error") => Some(LevelFilter::ERROR),
s if s.eq_ignore_ascii_case("warn") => Some(LevelFilter::WARN),
s if s.eq_ignore_ascii_case("info") => Some(LevelFilter::INFO),
s if s.eq_ignore_ascii_case("debug") => Some(LevelFilter::DEBUG),
s if s.eq_ignore_ascii_case("trace") => Some(LevelFilter::TRACE),
s if s.eq_ignore_ascii_case("off") => Some(LevelFilter::OFF),
_ => None,
})
.ok_or(ParseLevelFilterError(()))
}
}
#[derive(Debug)]
pub struct ParseLevelError {
_p: (),
}
impl fmt::Display for ParseLevelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(
"error parsing level: expected one of \"error\", \"warn\", \
\"info\", \"debug\", \"trace\", or a number 1-5",
)
}
}
impl fmt::Display for ParseLevelFilterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(
"error parsing level filter: expected one of \"off\", \"error\", \
\"warn\", \"info\", \"debug\", \"trace\", or a number 0-5",
)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseLevelFilterError {}
impl PartialEq<LevelFilter> for Level {
#[inline(always)]
fn eq(&self, other: &LevelFilter) -> bool {
self.0 as usize == filter_as_usize(&other.0)
}
}
impl PartialOrd for Level {
#[inline(always)]
fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &Level) -> bool {
(other.0 as usize) < (self.0 as usize)
}
#[inline(always)]
fn le(&self, other: &Level) -> bool {
(other.0 as usize) <= (self.0 as usize)
}
#[inline(always)]
fn gt(&self, other: &Level) -> bool {
(other.0 as usize) > (self.0 as usize)
}
#[inline(always)]
fn ge(&self, other: &Level) -> bool {
(other.0 as usize) >= (self.0 as usize)
}
}
impl Ord for Level {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(other.0 as usize).cmp(&(self.0 as usize))
}
}
impl PartialOrd<LevelFilter> for Level {
#[inline(always)]
fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
Some(filter_as_usize(&other.0).cmp(&(self.0 as usize)))
}
#[inline(always)]
fn lt(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) < (self.0 as usize)
}
#[inline(always)]
fn le(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) <= (self.0 as usize)
}
#[inline(always)]
fn gt(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) > (self.0 as usize)
}
#[inline(always)]
fn ge(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) >= (self.0 as usize)
}
}
#[inline(always)]
fn filter_as_usize(x: &Option<Level>) -> usize {
match x {
Some(Level(f)) => *f as usize,
None => LevelFilter::OFF_USIZE,
}
}
impl PartialEq<Level> for LevelFilter {
#[inline(always)]
fn eq(&self, other: &Level) -> bool {
filter_as_usize(&self.0) == other.0 as usize
}
}
impl PartialOrd for LevelFilter {
#[inline(always)]
fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) < filter_as_usize(&self.0)
}
#[inline(always)]
fn le(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) <= filter_as_usize(&self.0)
}
#[inline(always)]
fn gt(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) > filter_as_usize(&self.0)
}
#[inline(always)]
fn ge(&self, other: &LevelFilter) -> bool {
filter_as_usize(&other.0) >= filter_as_usize(&self.0)
}
}
impl Ord for LevelFilter {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
filter_as_usize(&other.0).cmp(&filter_as_usize(&self.0))
}
}
impl PartialOrd<Level> for LevelFilter {
#[inline(always)]
fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
Some((other.0 as usize).cmp(&filter_as_usize(&self.0)))
}
#[inline(always)]
fn lt(&self, other: &Level) -> bool {
(other.0 as usize) < filter_as_usize(&self.0)
}
#[inline(always)]
fn le(&self, other: &Level) -> bool {
(other.0 as usize) <= filter_as_usize(&self.0)
}
#[inline(always)]
fn gt(&self, other: &Level) -> bool {
(other.0 as usize) > filter_as_usize(&self.0)
}
#[inline(always)]
fn ge(&self, other: &Level) -> bool {
(other.0 as usize) >= filter_as_usize(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem;
#[test]
fn level_from_str() {
assert_eq!("error".parse::<Level>().unwrap(), Level::ERROR);
assert_eq!("4".parse::<Level>().unwrap(), Level::DEBUG);
assert!("0".parse::<Level>().is_err())
}
#[test]
fn filter_level_conversion() {
let mapping = [
(LevelFilter::OFF, None),
(LevelFilter::ERROR, Some(Level::ERROR)),
(LevelFilter::WARN, Some(Level::WARN)),
(LevelFilter::INFO, Some(Level::INFO)),
(LevelFilter::DEBUG, Some(Level::DEBUG)),
(LevelFilter::TRACE, Some(Level::TRACE)),
];
for (filter, level) in mapping.iter() {
assert_eq!(filter.into_level(), *level);
match level {
Some(level) => {
let actual: LevelFilter = (*level).into();
assert_eq!(actual, *filter);
}
None => {
let actual: LevelFilter = None.into();
assert_eq!(actual, *filter);
}
}
}
}
#[test]
fn level_filter_is_usize_sized() {
assert_eq!(
mem::size_of::<LevelFilter>(),
mem::size_of::<usize>(),
"`LevelFilter` is no longer `usize`-sized! global MAX_LEVEL may now be invalid!"
)
}
#[test]
fn level_filter_reprs() {
let mapping = [
(LevelFilter::OFF, LevelInner::Error as usize + 1),
(LevelFilter::ERROR, LevelInner::Error as usize),
(LevelFilter::WARN, LevelInner::Warn as usize),
(LevelFilter::INFO, LevelInner::Info as usize),
(LevelFilter::DEBUG, LevelInner::Debug as usize),
(LevelFilter::TRACE, LevelInner::Trace as usize),
];
for &(filter, expected) in &mapping {
let repr = unsafe {
mem::transmute::<LevelFilter, usize>(filter)
};
assert_eq!(expected, repr, "repr changed for {:?}", filter)
}
}
}