use super::{callsite, field};
use crate::stdlib::{
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, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Level(LevelInner);
#[repr(transparent)]
#[derive(Clone, Eq, PartialEq)]
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,
}
}
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()
}
}
impl<'a> fmt::Debug for Metadata<'a> {
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()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum KindInner {
Event,
Span,
}
impl Kind {
pub const EVENT: Kind = Kind(KindInner::Event);
pub const SPAN: Kind = Kind(KindInner::Span);
pub fn is_span(&self) -> bool {
match self {
Kind(KindInner::Span) => true,
_ => false,
}
}
pub fn is_event(&self) -> bool {
match self {
Kind(KindInner::Event) => true,
_ => false,
}
}
}
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);
}
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 crate::stdlib::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 Into<Option<Level>> for LevelFilter {
#[inline]
fn into(self) -> Option<Level> {
self.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 {
crate::stdlib::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_else(|| 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 crate::stdlib::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.clone().into_level(), *level);
match level {
Some(level) => {
let actual: LevelFilter = level.clone().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 &(ref filter, expected) in &mapping {
let repr = unsafe {
mem::transmute::<_, usize>(filter.clone())
};
assert_eq!(expected, repr, "repr changed for {:?}", filter)
}
}
}