Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
//! `MakeVisitor` wrappers for working with `fmt::Debug` fields.
use super::{MakeVisitor, VisitFmt, VisitOutput};
use tracing_core::field::{Field, Visit};
use core::fmt;
/// A visitor wrapper that ensures any `fmt::Debug` fields are formatted using
/// the alternate (`:#`) formatter.
#[derive(Debug, Clone)]
pub struct Alt<V>(V);
// TODO(eliza): When `error` as a primitive type is stable, add a
// `DisplayErrors` wrapper...
// === impl Alt ===
//
impl<V> Alt<V> {
/// Wraps the provided visitor so that any `fmt::Debug` fields are formatted
/// using the alternative (`:#`) formatter.
pub fn new(inner: V) -> Self {
Alt(inner)
}
}
impl<T, V> MakeVisitor<T> for Alt<V>
where
V: MakeVisitor<T>,
{
type Visitor = Alt<V::Visitor>;
#[inline]
fn make_visitor(&self, target: T) -> Self::Visitor {
Alt(self.0.make_visitor(target))
}
}
impl<V> Visit for Alt<V>
where
V: Visit,
{
#[inline]
fn record_f64(&mut self, field: &Field, value: f64) {
self.0.record_f64(field, value)
}
#[inline]
fn record_i64(&mut self, field: &Field, value: i64) {
self.0.record_i64(field, value)
}
#[inline]
fn record_u64(&mut self, field: &Field, value: u64) {
self.0.record_u64(field, value)
}
#[inline]
fn record_bool(&mut self, field: &Field, value: bool) {
self.0.record_bool(field, value)
}
/// Visit a string value.
fn record_str(&mut self, field: &Field, value: &str) {
self.0.record_str(field, value)
}
// TODO(eliza): add RecordError when stable
// fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
// self.record_debug(field, &format_args!("{}", value))
// }
#[inline]
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
self.0.record_debug(field, &format_args!("{:#?}", value))
}
}
impl<V, O> VisitOutput<O> for Alt<V>
where
V: VisitOutput<O>,
{
#[inline]
fn finish(self) -> O {
self.0.finish()
}
}
feature! {
#![feature = "std"]
use super::VisitWrite;
use std::io;
impl<V> VisitWrite for Alt<V>
where
V: VisitWrite,
{
#[inline]
fn writer(&mut self) -> &mut dyn io::Write {
self.0.writer()
}
}
}
impl<V> VisitFmt for Alt<V>
where
V: VisitFmt,
{
#[inline]
fn writer(&mut self) -> &mut dyn fmt::Write {
self.0.writer()
}
}

View File

@@ -0,0 +1,184 @@
//! A `MakeVisitor` wrapper that separates formatted fields with a delimiter.
use super::{MakeVisitor, VisitFmt, VisitOutput};
use core::fmt;
use tracing_core::field::{Field, Visit};
/// A `MakeVisitor` wrapper that wraps a visitor that writes formatted output so
/// that a delimiter is inserted between writing formatted field values.
#[derive(Debug, Clone)]
pub struct Delimited<D, V> {
delimiter: D,
inner: V,
}
/// A visitor wrapper that inserts a delimiter after the wrapped visitor formats
/// a field value.
#[derive(Debug)]
pub struct VisitDelimited<D, V> {
delimiter: D,
seen: bool,
inner: V,
err: fmt::Result,
}
// === impl Delimited ===
impl<D, V, T> MakeVisitor<T> for Delimited<D, V>
where
D: AsRef<str> + Clone,
V: MakeVisitor<T>,
V::Visitor: VisitFmt,
{
type Visitor = VisitDelimited<D, V::Visitor>;
fn make_visitor(&self, target: T) -> Self::Visitor {
let inner = self.inner.make_visitor(target);
VisitDelimited::new(self.delimiter.clone(), inner)
}
}
impl<D, V> Delimited<D, V> {
/// Returns a new [`MakeVisitor`] implementation that wraps `inner` so that
/// it will format each visited field separated by the provided `delimiter`.
///
/// [`MakeVisitor`]: super::MakeVisitor
pub fn new(delimiter: D, inner: V) -> Self {
Self { delimiter, inner }
}
}
// === impl VisitDelimited ===
impl<D, V> VisitDelimited<D, V> {
/// Returns a new [`Visit`] implementation that wraps `inner` so that
/// each formatted field is separated by the provided `delimiter`.
///
/// [`Visit`]: tracing_core::field::Visit
pub fn new(delimiter: D, inner: V) -> Self {
Self {
delimiter,
inner,
seen: false,
err: Ok(()),
}
}
fn delimit(&mut self)
where
V: VisitFmt,
D: AsRef<str>,
{
if self.err.is_err() {
return;
}
if self.seen {
self.err = self.inner.writer().write_str(self.delimiter.as_ref());
}
self.seen = true;
}
}
impl<D, V> Visit for VisitDelimited<D, V>
where
V: VisitFmt,
D: AsRef<str>,
{
fn record_i64(&mut self, field: &Field, value: i64) {
self.delimit();
self.inner.record_i64(field, value);
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.delimit();
self.inner.record_u64(field, value);
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.delimit();
self.inner.record_bool(field, value);
}
fn record_str(&mut self, field: &Field, value: &str) {
self.delimit();
self.inner.record_str(field, value);
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
self.delimit();
self.inner.record_debug(field, value);
}
}
impl<D, V> VisitOutput<fmt::Result> for VisitDelimited<D, V>
where
V: VisitFmt,
D: AsRef<str>,
{
fn finish(self) -> fmt::Result {
self.err?;
self.inner.finish()
}
}
impl<D, V> VisitFmt for VisitDelimited<D, V>
where
V: VisitFmt,
D: AsRef<str>,
{
fn writer(&mut self) -> &mut dyn fmt::Write {
self.inner.writer()
}
}
#[cfg(test)]
#[cfg(all(test, feature = "alloc"))]
mod test {
use super::*;
use crate::field::test_util::*;
#[test]
fn delimited_visitor() {
let mut s = String::new();
let visitor = DebugVisitor::new(&mut s);
let mut visitor = VisitDelimited::new(", ", visitor);
TestAttrs1::with(|attrs| attrs.record(&mut visitor));
visitor.finish().unwrap();
assert_eq!(
s.as_str(),
"question=\"life, the universe, and everything\", tricky=true, can_you_do_it=true"
);
}
#[test]
fn delimited_new_visitor() {
let make = Delimited::new("; ", MakeDebug);
TestAttrs1::with(|attrs| {
let mut s = String::new();
{
let mut v = make.make_visitor(&mut s);
attrs.record(&mut v);
}
assert_eq!(
s.as_str(),
"question=\"life, the universe, and everything\"; tricky=true; can_you_do_it=true"
);
});
TestAttrs2::with(|attrs| {
let mut s = String::new();
{
let mut v = make.make_visitor(&mut s);
attrs.record(&mut v);
}
assert_eq!(
s.as_str(),
"question=None; question.answer=42; tricky=true; can_you_do_it=false"
);
});
}
}

View File

@@ -0,0 +1,117 @@
//! `MakeVisitor` wrappers for working with `fmt::Display` fields.
use super::{MakeVisitor, VisitFmt, VisitOutput};
use tracing_core::field::{Field, Visit};
use core::fmt;
/// A visitor wrapper that ensures any strings named "message" are formatted
/// using `fmt::Display`
#[derive(Debug, Clone)]
pub struct Messages<V>(V);
// TODO(eliza): When `error` as a primitive type is stable, add a
// `DisplayErrors` wrapper...
// === impl Messages ===
//
impl<V> Messages<V> {
/// Returns a new [`MakeVisitor`] implementation that will wrap `inner` so
/// that any strings named `message` are formatted using `fmt::Display`.
///
/// [`MakeVisitor`]: super::MakeVisitor
pub fn new(inner: V) -> Self {
Messages(inner)
}
}
impl<T, V> MakeVisitor<T> for Messages<V>
where
V: MakeVisitor<T>,
{
type Visitor = Messages<V::Visitor>;
#[inline]
fn make_visitor(&self, target: T) -> Self::Visitor {
Messages(self.0.make_visitor(target))
}
}
impl<V> Visit for Messages<V>
where
V: Visit,
{
#[inline]
fn record_f64(&mut self, field: &Field, value: f64) {
self.0.record_f64(field, value)
}
#[inline]
fn record_i64(&mut self, field: &Field, value: i64) {
self.0.record_i64(field, value)
}
#[inline]
fn record_u64(&mut self, field: &Field, value: u64) {
self.0.record_u64(field, value)
}
#[inline]
fn record_bool(&mut self, field: &Field, value: bool) {
self.0.record_bool(field, value)
}
/// Visit a string value.
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "message" {
self.0.record_debug(field, &format_args!("{}", value))
} else {
self.0.record_str(field, value)
}
}
// TODO(eliza): add RecordError when stable
// fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
// self.record_debug(field, &format_args!("{}", value))
// }
#[inline]
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
self.0.record_debug(field, value)
}
}
impl<V, O> VisitOutput<O> for Messages<V>
where
V: VisitOutput<O>,
{
#[inline]
fn finish(self) -> O {
self.0.finish()
}
}
feature! {
#![feature = "std"]
use super::VisitWrite;
use std::io;
impl<V> VisitWrite for Messages<V>
where
V: VisitWrite,
{
#[inline]
fn writer(&mut self) -> &mut dyn io::Write {
self.0.writer()
}
}
}
impl<V> VisitFmt for Messages<V>
where
V: VisitFmt,
{
#[inline]
fn writer(&mut self) -> &mut dyn fmt::Write {
self.0.writer()
}
}

View File

@@ -0,0 +1,365 @@
//! Utilities for working with [fields] and [field visitors].
//!
//! [fields]: tracing_core::field
//! [field visitors]: tracing_core::field::Visit
use core::{fmt, marker::PhantomData};
pub use tracing_core::field::Visit;
use tracing_core::{
span::{Attributes, Record},
Event,
};
pub mod debug;
pub mod delimited;
pub mod display;
/// Creates new [visitors].
///
/// A type implementing `MakeVisitor` represents a composable factory for types
/// implementing the [`Visit` trait][visitors]. The `MakeVisitor` trait defines
/// a single function, `make_visitor`, which takes in a `T`-typed `target` and
/// returns a type implementing `Visit` configured for that target. A target may
/// be a string, output stream, or data structure that the visitor will record
/// data to, configuration variables that determine the visitor's behavior, or
/// `()` when no input is required to produce a visitor.
///
/// [visitors]: tracing_core::field::Visit
pub trait MakeVisitor<T> {
/// The visitor type produced by this `MakeVisitor`.
type Visitor: Visit;
/// Make a new visitor for the provided `target`.
fn make_visitor(&self, target: T) -> Self::Visitor;
}
/// A [visitor] that produces output once it has visited a set of fields.
///
/// [visitor]: tracing_core::field::Visit
pub trait VisitOutput<Out>: Visit {
/// Completes the visitor, returning any output.
///
/// This is called once a full set of fields has been visited.
fn finish(self) -> Out;
/// Visit a set of fields, and return the output of finishing the visitor
/// once the fields have been visited.
fn visit<R>(mut self, fields: &R) -> Out
where
R: RecordFields,
Self: Sized,
{
fields.record(&mut self);
self.finish()
}
}
/// Extension trait implemented by types which can be recorded by a [visitor].
///
/// This allows writing code that is generic over `tracing_core`'s
/// [`span::Attributes`][attr], [`span::Record`][rec], and [`Event`]
/// types. These types all provide inherent `record` methods that allow a
/// visitor to record their fields, but there is no common trait representing this.
///
/// With `RecordFields`, we can write code like this:
/// ```
/// use tracing_core::field::Visit;
/// # use tracing_core::field::Field;
/// use tracing_subscriber::field::RecordFields;
///
/// struct MyVisitor {
/// // ...
/// }
/// # impl MyVisitor { fn new() -> Self { Self{} } }
/// impl Visit for MyVisitor {
/// // ...
/// # fn record_debug(&mut self, _: &Field, _: &dyn std::fmt::Debug) {}
/// }
///
/// fn record_with_my_visitor<R>(r: R)
/// where
/// R: RecordFields,
/// {
/// let mut visitor = MyVisitor::new();
/// r.record(&mut visitor);
/// }
/// ```
/// [visitor]: tracing_core::field::Visit
/// [attr]: tracing_core::span::Attributes
/// [rec]: tracing_core::span::Record
pub trait RecordFields: crate::sealed::Sealed<RecordFieldsMarker> {
/// Record all the fields in `self` with the provided `visitor`.
fn record(&self, visitor: &mut dyn Visit);
}
/// Extension trait implemented for all `MakeVisitor` implementations that
/// produce a visitor implementing `VisitOutput`.
pub trait MakeOutput<T, Out>
where
Self: MakeVisitor<T> + crate::sealed::Sealed<(T, Out)>,
Self::Visitor: VisitOutput<Out>,
{
/// Visits all fields in `fields` with a new visitor constructed from
/// `target`.
fn visit_with<F>(&self, target: T, fields: &F) -> Out
where
F: RecordFields,
{
self.make_visitor(target).visit(fields)
}
}
feature! {
#![feature = "std"]
use std::io;
/// Extension trait implemented by visitors to indicate that they write to an
/// `io::Write` instance, and allow access to that writer.
pub trait VisitWrite: VisitOutput<Result<(), io::Error>> {
/// Returns the writer that this visitor writes to.
fn writer(&mut self) -> &mut dyn io::Write;
}
}
/// Extension trait implemented by visitors to indicate that they write to a
/// `fmt::Write` instance, and allow access to that writer.
pub trait VisitFmt: VisitOutput<fmt::Result> {
/// Returns the formatter that this visitor writes to.
fn writer(&mut self) -> &mut dyn fmt::Write;
}
/// Extension trait providing `MakeVisitor` combinators.
pub trait MakeExt<T>
where
Self: MakeVisitor<T> + Sized,
Self: crate::sealed::Sealed<MakeExtMarker<T>>,
{
/// Wraps `self` so that any `fmt::Debug` fields are recorded using the
/// alternate formatter (`{:#?}`).
fn debug_alt(self) -> debug::Alt<Self> {
debug::Alt::new(self)
}
/// Wraps `self` so that any string fields named "message" are recorded
/// using `fmt::Display`.
fn display_messages(self) -> display::Messages<Self> {
display::Messages::new(self)
}
/// Wraps `self` so that when fields are formatted to a writer, they are
/// separated by the provided `delimiter`.
fn delimited<D>(self, delimiter: D) -> delimited::Delimited<D, Self>
where
D: AsRef<str> + Clone,
Self::Visitor: VisitFmt,
{
delimited::Delimited::new(delimiter, self)
}
}
// === impl RecordFields ===
impl crate::sealed::Sealed<RecordFieldsMarker> for Event<'_> {}
impl RecordFields for Event<'_> {
fn record(&self, visitor: &mut dyn Visit) {
Event::record(self, visitor)
}
}
impl crate::sealed::Sealed<RecordFieldsMarker> for Attributes<'_> {}
impl RecordFields for Attributes<'_> {
fn record(&self, visitor: &mut dyn Visit) {
Attributes::record(self, visitor)
}
}
impl crate::sealed::Sealed<RecordFieldsMarker> for Record<'_> {}
impl RecordFields for Record<'_> {
fn record(&self, visitor: &mut dyn Visit) {
Record::record(self, visitor)
}
}
impl<F> crate::sealed::Sealed<RecordFieldsMarker> for &F where F: RecordFields {}
impl<F> RecordFields for &F
where
F: RecordFields,
{
fn record(&self, visitor: &mut dyn Visit) {
F::record(*self, visitor)
}
}
// === blanket impls ===
impl<T, V, F> MakeVisitor<T> for F
where
F: Fn(T) -> V,
V: Visit,
{
type Visitor = V;
fn make_visitor(&self, target: T) -> Self::Visitor {
(self)(target)
}
}
impl<T, Out, M> crate::sealed::Sealed<(T, Out)> for M
where
M: MakeVisitor<T>,
M::Visitor: VisitOutput<Out>,
{
}
impl<T, Out, M> MakeOutput<T, Out> for M
where
M: MakeVisitor<T>,
M::Visitor: VisitOutput<Out>,
{
}
impl<T, M> crate::sealed::Sealed<MakeExtMarker<T>> for M where M: MakeVisitor<T> + Sized {}
impl<T, M> MakeExt<T> for M
where
M: MakeVisitor<T> + Sized,
M: crate::sealed::Sealed<MakeExtMarker<T>>,
{
}
#[derive(Debug)]
#[doc(hidden)]
pub struct MakeExtMarker<T> {
_p: PhantomData<T>,
}
#[derive(Debug)]
#[doc(hidden)]
pub struct RecordFieldsMarker {
_p: (),
}
#[cfg(all(test, feature = "alloc"))]
#[macro_use]
pub(in crate::field) mod test_util {
use super::*;
pub(in crate::field) use alloc::string::String;
use tracing_core::{
callsite::Callsite,
field::{Field, Value},
metadata::{Kind, Level, Metadata},
};
pub(crate) struct TestAttrs1;
pub(crate) struct TestAttrs2;
impl TestAttrs1 {
pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T {
let fieldset = TEST_META_1.fields();
let values = &[
(
&fieldset.field("question").unwrap(),
Some(&"life, the universe, and everything" as &dyn Value),
),
(&fieldset.field("question.answer").unwrap(), None),
(
&fieldset.field("tricky").unwrap(),
Some(&true as &dyn Value),
),
(
&fieldset.field("can_you_do_it").unwrap(),
Some(&true as &dyn Value),
),
];
let valueset = fieldset.value_set(values);
let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset);
f(attrs)
}
}
impl TestAttrs2 {
pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T {
let fieldset = TEST_META_1.fields();
let none = tracing_core::field::debug(&Option::<&str>::None);
let values = &[
(
&fieldset.field("question").unwrap(),
Some(&none as &dyn Value),
),
(
&fieldset.field("question.answer").unwrap(),
Some(&42 as &dyn Value),
),
(
&fieldset.field("tricky").unwrap(),
Some(&true as &dyn Value),
),
(
&fieldset.field("can_you_do_it").unwrap(),
Some(&false as &dyn Value),
),
];
let valueset = fieldset.value_set(values);
let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset);
f(attrs)
}
}
struct TestCallsite1;
static TEST_CALLSITE_1: &'static dyn Callsite = &TestCallsite1;
static TEST_META_1: Metadata<'static> = tracing_core::metadata! {
name: "field_test1",
target: module_path!(),
level: Level::INFO,
fields: &["question", "question.answer", "tricky", "can_you_do_it"],
callsite: TEST_CALLSITE_1,
kind: Kind::SPAN,
};
impl Callsite for TestCallsite1 {
fn set_interest(&self, _: tracing_core::subscriber::Interest) {
unimplemented!()
}
fn metadata(&self) -> &Metadata<'_> {
&TEST_META_1
}
}
pub(crate) struct MakeDebug;
pub(crate) struct DebugVisitor<'a> {
writer: &'a mut dyn fmt::Write,
err: fmt::Result,
}
impl<'a> DebugVisitor<'a> {
pub(crate) fn new(writer: &'a mut dyn fmt::Write) -> Self {
Self {
writer,
err: Ok(()),
}
}
}
impl Visit for DebugVisitor<'_> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
write!(self.writer, "{}={:?}", field, value).unwrap();
}
}
impl VisitOutput<fmt::Result> for DebugVisitor<'_> {
fn finish(self) -> fmt::Result {
self.err
}
}
impl VisitFmt for DebugVisitor<'_> {
fn writer(&mut self) -> &mut dyn fmt::Write {
self.writer
}
}
impl<'a> MakeVisitor<&'a mut dyn fmt::Write> for MakeDebug {
type Visitor = DebugVisitor<'a>;
fn make_visitor(&self, w: &'a mut dyn fmt::Write) -> DebugVisitor<'a> {
DebugVisitor::new(w)
}
}
}

View File

@@ -0,0 +1,457 @@
use crate::filter::level::{self, LevelFilter};
#[cfg(not(feature = "smallvec"))]
use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr};
use tracing_core::{Level, Metadata};
/// Indicates that a string could not be parsed as a filtering directive.
#[derive(Debug)]
pub struct ParseError {
kind: ParseErrorKind,
}
/// A directive which will statically enable or disable a given callsite.
///
/// Unlike a dynamic directive, this can be cached by the callsite.
#[derive(Debug, PartialEq, Eq, Clone)]
pub(crate) struct StaticDirective {
pub(in crate::filter) target: Option<String>,
pub(in crate::filter) field_names: Vec<String>,
pub(in crate::filter) level: LevelFilter,
}
#[cfg(feature = "smallvec")]
pub(crate) type FilterVec<T> = smallvec::SmallVec<[T; 8]>;
#[cfg(not(feature = "smallvec"))]
pub(crate) type FilterVec<T> = Vec<T>;
#[derive(Debug, PartialEq, Clone)]
pub(in crate::filter) struct DirectiveSet<T> {
directives: FilterVec<T>,
pub(in crate::filter) max_level: LevelFilter,
}
pub(in crate::filter) trait Match {
fn cares_about(&self, meta: &Metadata<'_>) -> bool;
fn level(&self) -> &LevelFilter;
}
#[derive(Debug)]
enum ParseErrorKind {
#[cfg(feature = "std")]
Field(Box<dyn std::error::Error + Send + Sync>),
Level(level::ParseError),
Other(Option<&'static str>),
}
// === impl DirectiveSet ===
impl<T> DirectiveSet<T> {
// this is only used by `env-filter`.
#[cfg(all(feature = "std", feature = "env-filter"))]
pub(crate) fn is_empty(&self) -> bool {
self.directives.is_empty()
}
pub(crate) fn iter(&self) -> slice::Iter<'_, T> {
self.directives.iter()
}
}
impl<T: Ord> Default for DirectiveSet<T> {
fn default() -> Self {
Self {
directives: FilterVec::new(),
max_level: LevelFilter::OFF,
}
}
}
impl<T: Match + Ord> DirectiveSet<T> {
pub(crate) fn directives(&self) -> impl Iterator<Item = &T> {
self.directives.iter()
}
pub(crate) fn directives_for<'a>(
&'a self,
metadata: &'a Metadata<'a>,
) -> impl Iterator<Item = &'a T> + 'a {
self.directives().filter(move |d| d.cares_about(metadata))
}
pub(crate) fn add(&mut self, directive: T) {
// does this directive enable a more verbose level than the current
// max? if so, update the max level.
let level = *directive.level();
if level > self.max_level {
self.max_level = level;
}
// insert the directive into the vec of directives, ordered by
// specificity (length of target + number of field filters). this
// ensures that, when finding a directive to match a span or event, we
// search the directive set in most specific first order.
match self.directives.binary_search(&directive) {
Ok(i) => self.directives[i] = directive,
Err(i) => self.directives.insert(i, directive),
}
}
#[cfg(test)]
pub(in crate::filter) fn into_vec(self) -> FilterVec<T> {
self.directives
}
}
impl<T: Match + Ord> FromIterator<T> for DirectiveSet<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut this = Self::default();
this.extend(iter);
this
}
}
impl<T: Match + Ord> Extend<T> for DirectiveSet<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for directive in iter.into_iter() {
self.add(directive);
}
}
}
impl<T> IntoIterator for DirectiveSet<T> {
type Item = T;
#[cfg(feature = "smallvec")]
type IntoIter = smallvec::IntoIter<[T; 8]>;
#[cfg(not(feature = "smallvec"))]
type IntoIter = vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.directives.into_iter()
}
}
// === impl Statics ===
impl DirectiveSet<StaticDirective> {
pub(crate) fn enabled(&self, meta: &Metadata<'_>) -> bool {
let level = meta.level();
match self.directives_for(meta).next() {
Some(d) => d.level >= *level,
None => false,
}
}
/// Same as `enabled` above, but skips `Directive`'s with fields.
pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool {
match self.directives_for_target(target).next() {
Some(d) => d.level >= *level,
None => false,
}
}
pub(crate) fn directives_for_target<'a>(
&'a self,
target: &'a str,
) -> impl Iterator<Item = &'a StaticDirective> + 'a {
self.directives()
.filter(move |d| d.cares_about_target(target))
}
}
// === impl StaticDirective ===
impl StaticDirective {
pub(in crate::filter) fn new(
target: Option<String>,
field_names: Vec<String>,
level: LevelFilter,
) -> Self {
Self {
target,
field_names,
level,
}
}
pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool {
// Does this directive have a target filter, and does it match the
// metadata's target?
if let Some(ref target) = self.target {
if !to_check.starts_with(&target[..]) {
return false;
}
}
if !self.field_names.is_empty() {
return false;
}
true
}
}
impl Ord for StaticDirective {
fn cmp(&self, other: &StaticDirective) -> Ordering {
// We attempt to order directives by how "specific" they are. This
// ensures that we try the most specific directives first when
// attempting to match a piece of metadata.
// First, we compare based on whether a target is specified, and the
// lengths of those targets if both have targets.
let ordering = self
.target
.as_ref()
.map(String::len)
.cmp(&other.target.as_ref().map(String::len))
// Then we compare how many field names are matched by each directive.
.then_with(|| self.field_names.len().cmp(&other.field_names.len()))
// Finally, we fall back to lexicographical ordering if the directives are
// equally specific. Although this is no longer semantically important,
// we need to define a total ordering to determine the directive's place
// in the BTreeMap.
.then_with(|| {
self.target
.cmp(&other.target)
.then_with(|| self.field_names[..].cmp(&other.field_names[..]))
})
.reverse();
#[cfg(debug_assertions)]
{
if ordering == Ordering::Equal {
debug_assert_eq!(
self.target, other.target,
"invariant violated: Ordering::Equal must imply a.target == b.target"
);
debug_assert_eq!(
self.field_names, other.field_names,
"invariant violated: Ordering::Equal must imply a.field_names == b.field_names"
);
}
}
ordering
}
}
impl PartialOrd for StaticDirective {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Match for StaticDirective {
fn cares_about(&self, meta: &Metadata<'_>) -> bool {
// Does this directive have a target filter, and does it match the
// metadata's target?
if let Some(ref target) = self.target {
if !meta.target().starts_with(&target[..]) {
return false;
}
}
if meta.is_event() && !self.field_names.is_empty() {
let fields = meta.fields();
for name in &self.field_names {
if fields.field(name).is_none() {
return false;
}
}
}
true
}
fn level(&self) -> &LevelFilter {
&self.level
}
}
impl Default for StaticDirective {
fn default() -> Self {
StaticDirective {
target: None,
field_names: Vec::new(),
level: LevelFilter::ERROR,
}
}
}
impl fmt::Display for StaticDirective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut wrote_any = false;
if let Some(ref target) = self.target {
fmt::Display::fmt(target, f)?;
wrote_any = true;
}
if !self.field_names.is_empty() {
f.write_str("[")?;
let mut fields = self.field_names.iter();
if let Some(field) = fields.next() {
write!(f, "{{{}", field)?;
for field in fields {
write!(f, ",{}", field)?;
}
f.write_str("}")?;
}
f.write_str("]")?;
wrote_any = true;
}
if wrote_any {
f.write_str("=")?;
}
fmt::Display::fmt(&self.level, f)
}
}
impl FromStr for StaticDirective {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// This method parses a filtering directive in one of the following
// forms:
//
// * `foo=trace` (TARGET=LEVEL)
// * `foo[{bar,baz}]=info` (TARGET[{FIELD,+}]=LEVEL)
// * `trace` (bare LEVEL)
// * `foo` (bare TARGET)
let mut split = s.split('=');
let part0 = split
.next()
.ok_or_else(|| ParseError::msg("string must not be empty"))?;
// Directive includes an `=`:
// * `foo=trace`
// * `foo[{bar}]=trace`
// * `foo[{bar,baz}]=trace`
if let Some(part1) = split.next() {
if split.next().is_some() {
return Err(ParseError::msg(
"too many '=' in filter directive, expected 0 or 1",
));
}
let mut split = part0.split("[{");
let target = split.next().map(String::from);
let mut field_names = Vec::new();
// Directive includes fields:
// * `foo[{bar}]=trace`
// * `foo[{bar,baz}]=trace`
if let Some(maybe_fields) = split.next() {
if split.next().is_some() {
return Err(ParseError::msg(
"too many '[{' in filter directive, expected 0 or 1",
));
}
if !maybe_fields.ends_with("}]") {
return Err(ParseError::msg("expected fields list to end with '}]'"));
}
let fields = maybe_fields
.trim_end_matches("}]")
.split(',')
.filter_map(|s| {
if s.is_empty() {
None
} else {
Some(String::from(s))
}
});
field_names.extend(fields);
};
let level = part1.parse()?;
return Ok(Self {
level,
field_names,
target,
});
}
// Okay, the part after the `=` was empty, the directive is either a
// bare level or a bare target.
// * `foo`
// * `info`
Ok(match part0.parse::<LevelFilter>() {
Ok(level) => Self {
level,
target: None,
field_names: Vec::new(),
},
Err(_) => Self {
target: Some(String::from(part0)),
level: LevelFilter::TRACE,
field_names: Vec::new(),
},
})
}
}
// === impl ParseError ===
impl ParseError {
#[cfg(all(feature = "std", feature = "env-filter"))]
pub(crate) fn new() -> Self {
ParseError {
kind: ParseErrorKind::Other(None),
}
}
pub(crate) fn msg(s: &'static str) -> Self {
ParseError {
kind: ParseErrorKind::Other(Some(s)),
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ParseErrorKind::Other(None) => f.pad("invalid filter directive"),
ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg),
ParseErrorKind::Level(ref l) => l.fmt(f),
#[cfg(feature = "std")]
ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseError {
fn description(&self) -> &str {
"invalid filter directive"
}
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.kind {
ParseErrorKind::Other(_) => None,
ParseErrorKind::Level(ref l) => Some(l),
ParseErrorKind::Field(ref n) => Some(n.as_ref()),
}
}
}
#[cfg(feature = "std")]
impl From<Box<dyn std::error::Error + Send + Sync>> for ParseError {
fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
Self {
kind: ParseErrorKind::Field(e),
}
}
}
impl From<level::ParseError> for ParseError {
fn from(l: level::ParseError) -> Self {
Self {
kind: ParseErrorKind::Level(l),
}
}
}

View File

@@ -0,0 +1,351 @@
use super::{
directive::{self, Directive},
EnvFilter, FromEnvError,
};
use crate::sync::RwLock;
use std::env;
use thread_local::ThreadLocal;
use tracing::level_filters::STATIC_MAX_LEVEL;
/// A [builder] for constructing new [`EnvFilter`]s.
///
/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
#[derive(Debug, Clone)]
#[must_use]
pub struct Builder {
regex: bool,
env: Option<String>,
default_directive: Option<Directive>,
}
impl Builder {
/// Sets whether span field values can be matched with regular expressions.
///
/// If this is `true`, field filter directives will be interpreted as
/// regular expressions if they are not able to be interpreted as a `bool`,
/// `i64`, `u64`, or `f64` literal. If this is `false,` those field values
/// will be interpreted as literal [`std::fmt::Debug`] output instead.
///
/// By default, regular expressions are enabled.
///
/// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs,
/// disabling regular expressions is strongly encouraged.
pub fn with_regex(self, regex: bool) -> Self {
Self { regex, ..self }
}
/// Sets a default [filtering directive] that will be added to the filter if
/// the parsed string or environment variable contains no filter directives.
///
/// By default, there is no default directive.
///
/// # Examples
///
/// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are
/// called with an empty string or environment variable, the default
/// directive is used instead:
///
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
///
/// let filter = EnvFilter::builder()
/// .with_default_directive(LevelFilter::INFO.into())
/// .parse("")?;
///
/// assert_eq!(format!("{}", filter), "info");
/// # Ok(()) }
/// ```
///
/// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`])
/// will ignore any invalid directives. If all directives in a filter
/// string or environment variable are invalid, those methods will also use
/// the default directive:
///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
///
/// let filter = EnvFilter::builder()
/// .with_default_directive(LevelFilter::INFO.into())
/// .parse_lossy("some_target=fake level,foo::bar=lolwut");
///
/// assert_eq!(format!("{}", filter), "info");
/// ```
///
///
/// If the string or environment variable contains valid filtering
/// directives, the default directive is not used:
///
/// ```rust
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
///
/// let filter = EnvFilter::builder()
/// .with_default_directive(LevelFilter::INFO.into())
/// .parse_lossy("foo=trace");
///
/// // The default directive is *not* used:
/// assert_eq!(format!("{}", filter), "foo=trace");
/// ```
///
/// Parsing a more complex default directive from a string:
///
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
///
/// let default = "myapp=debug".parse()
/// .expect("hard-coded default directive should be valid");
///
/// let filter = EnvFilter::builder()
/// .with_default_directive(default)
/// .parse("")?;
///
/// assert_eq!(format!("{}", filter), "myapp=debug");
/// # Ok(()) }
/// ```
///
/// [`parse_lossy`]: Self::parse_lossy
/// [`from_env_lossy`]: Self::from_env_lossy
/// [`parse`]: Self::parse
/// [`from_env`]: Self::from_env
pub fn with_default_directive(self, default_directive: Directive) -> Self {
Self {
default_directive: Some(default_directive),
..self
}
}
/// Sets the name of the environment variable used by the [`from_env`],
/// [`from_env_lossy`], and [`try_from_env`] methods.
///
/// By default, this is the value of [`EnvFilter::DEFAULT_ENV`]
/// (`RUST_LOG`).
///
/// [`from_env`]: Self::from_env
/// [`from_env_lossy`]: Self::from_env_lossy
/// [`try_from_env`]: Self::try_from_env
pub fn with_env_var(self, var: impl ToString) -> Self {
Self {
env: Some(var.to_string()),
..self
}
}
/// Returns a new [`EnvFilter`] from the directives in the given string,
/// *ignoring* any that are invalid.
///
/// If `parse_lossy` is called with an empty string, then the
/// [default directive] is used instead.
///
/// [default directive]: Self::with_default_directive
pub fn parse_lossy<S: AsRef<str>>(&self, dirs: S) -> EnvFilter {
let directives = dirs
.as_ref()
.split(',')
.filter(|s| !s.is_empty())
.filter_map(|s| match Directive::parse(s, self.regex) {
Ok(d) => Some(d),
Err(err) => {
eprintln!("ignoring `{}`: {}", s, err);
None
}
});
self.from_directives(directives)
}
/// Returns a new [`EnvFilter`] from the directives in the given string,
/// or an error if any are invalid.
///
/// If `parse` is called with an empty string, then the [default directive]
/// is used instead.
///
/// [default directive]: Self::with_default_directive
pub fn parse<S: AsRef<str>>(&self, dirs: S) -> Result<EnvFilter, directive::ParseError> {
let dirs = dirs.as_ref();
if dirs.is_empty() {
return Ok(self.from_directives(std::iter::empty()));
}
let directives = dirs
.split(',')
.filter(|s| !s.is_empty())
.map(|s| Directive::parse(s, self.regex))
.collect::<Result<Vec<_>, _>>()?;
Ok(self.from_directives(directives))
}
/// Returns a new [`EnvFilter`] from the directives in the configured
/// environment variable, ignoring any directives that are invalid.
///
/// If the environment variable is empty, then the [default directive]
/// is used instead.
///
/// [default directive]: Self::with_default_directive
pub fn from_env_lossy(&self) -> EnvFilter {
let var = env::var(self.env_var_name()).unwrap_or_default();
self.parse_lossy(var)
}
/// Returns a new [`EnvFilter`] from the directives in the configured
/// environment variable. If the environment variable is unset, no directive is added.
///
/// An error is returned if the environment contains invalid directives.
///
/// If the environment variable is empty, then the [default directive]
/// is used instead.
///
/// [default directive]: Self::with_default_directive
pub fn from_env(&self) -> Result<EnvFilter, FromEnvError> {
let var = env::var(self.env_var_name()).unwrap_or_default();
self.parse(var).map_err(Into::into)
}
/// Returns a new [`EnvFilter`] from the directives in the configured
/// environment variable, or an error if the environment variable is not set
/// or contains invalid directives.
///
/// If the environment variable is empty, then the [default directive]
/// is used instead.
///
/// [default directive]: Self::with_default_directive
pub fn try_from_env(&self) -> Result<EnvFilter, FromEnvError> {
let var = env::var(self.env_var_name())?;
self.parse(var).map_err(Into::into)
}
// TODO(eliza): consider making this a public API?
// Clippy doesn't love this naming, because it suggests that `from_` methods
// should not take a `Self`...but in this case, it's the `EnvFilter` that is
// being constructed "from" the directives, rather than the builder itself.
#[allow(clippy::wrong_self_convention)]
pub(super) fn from_directives(
&self,
directives: impl IntoIterator<Item = Directive>,
) -> EnvFilter {
use tracing::Level;
let mut directives: Vec<_> = directives.into_iter().collect();
let mut disabled = Vec::new();
for directive in &mut directives {
if directive.level > STATIC_MAX_LEVEL {
disabled.push(directive.clone());
}
if !self.regex {
directive.deregexify();
}
}
if !disabled.is_empty() {
#[cfg(feature = "nu-ansi-term")]
use nu_ansi_term::{Color, Style};
// NOTE: We can't use a configured `MakeWriter` because the EnvFilter
// has no knowledge of any underlying subscriber or subscriber, which
// may or may not use a `MakeWriter`.
let warn = |msg: &str| {
#[cfg(not(feature = "nu-ansi-term"))]
let msg = format!("warning: {}", msg);
#[cfg(feature = "nu-ansi-term")]
let msg = {
let bold = Style::new().bold();
let mut warning = Color::Yellow.paint("warning");
warning.style_ref_mut().is_bold = true;
format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg))
};
eprintln!("{}", msg);
};
let ctx_prefixed = |prefix: &str, msg: &str| {
#[cfg(not(feature = "nu-ansi-term"))]
let msg = format!("{} {}", prefix, msg);
#[cfg(feature = "nu-ansi-term")]
let msg = {
let mut equal = Color::Fixed(21).paint("="); // dark blue
equal.style_ref_mut().is_bold = true;
format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg)
};
eprintln!("{}", msg);
};
let ctx_help = |msg| ctx_prefixed("help:", msg);
let ctx_note = |msg| ctx_prefixed("note:", msg);
let ctx = |msg: &str| {
#[cfg(not(feature = "nu-ansi-term"))]
let msg = format!("note: {}", msg);
#[cfg(feature = "nu-ansi-term")]
let msg = {
let mut pipe = Color::Fixed(21).paint("|");
pipe.style_ref_mut().is_bold = true;
format!(" {} {}", pipe, msg)
};
eprintln!("{}", msg);
};
warn("some trace filter directives would enable traces that are disabled statically");
for directive in disabled {
let target = if let Some(target) = &directive.target {
format!("the `{}` target", target)
} else {
"all targets".into()
};
let level = directive
.level
.into_level()
.expect("=off would not have enabled any filters");
ctx(&format!(
"`{}` would enable the {} level for {}",
directive, level, target
));
}
ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL));
let help_msg = || {
let (feature, filter) = match STATIC_MAX_LEVEL.into_level() {
Some(Level::TRACE) => unreachable!(
"if the max level is trace, no static filtering features are enabled"
),
Some(Level::DEBUG) => ("max_level_debug", Level::TRACE),
Some(Level::INFO) => ("max_level_info", Level::DEBUG),
Some(Level::WARN) => ("max_level_warn", Level::INFO),
Some(Level::ERROR) => ("max_level_error", Level::WARN),
None => return ("max_level_off", String::new()),
};
(feature, format!("{} ", filter))
};
let (feature, earlier_level) = help_msg();
ctx_help(&format!(
"to enable {}logging, remove the `{}` feature from the `tracing` crate",
earlier_level, feature
));
}
let (dynamics, statics) = Directive::make_tables(directives);
let has_dynamics = !dynamics.is_empty();
let mut filter = EnvFilter {
statics,
dynamics,
has_dynamics,
by_id: RwLock::new(Default::default()),
by_cs: RwLock::new(Default::default()),
scope: ThreadLocal::new(),
regex: self.regex,
};
if !has_dynamics && filter.statics.is_empty() {
if let Some(ref default) = self.default_directive {
filter = filter.add_directive(default.clone());
}
}
filter
}
fn env_var_name(&self) -> &str {
self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV)
}
}
impl Default for Builder {
fn default() -> Self {
Self {
regex: true,
env: None,
default_directive: None,
}
}
}

View File

@@ -0,0 +1,888 @@
pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective};
use crate::filter::{
directive::{DirectiveSet, Match},
env::{field, FieldMap},
level::LevelFilter,
};
use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
use tracing_core::{span, Level, Metadata};
/// A single filtering directive.
// TODO(eliza): add a builder for programmatically constructing directives?
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
pub struct Directive {
in_span: Option<String>,
fields: Vec<field::Match>,
pub(crate) target: Option<String>,
pub(crate) level: LevelFilter,
}
/// A set of dynamic filtering directives.
pub(super) type Dynamics = DirectiveSet<Directive>;
/// A set of static filtering directives.
pub(super) type Statics = DirectiveSet<StaticDirective>;
pub(crate) type CallsiteMatcher = MatchSet<field::CallsiteMatch>;
pub(crate) type SpanMatcher = MatchSet<field::SpanMatch>;
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct MatchSet<T> {
field_matches: FilterVec<T>,
base_level: LevelFilter,
}
impl Directive {
pub(super) fn has_name(&self) -> bool {
self.in_span.is_some()
}
pub(super) fn has_fields(&self) -> bool {
!self.fields.is_empty()
}
pub(super) fn to_static(&self) -> Option<StaticDirective> {
if !self.is_static() {
return None;
}
// TODO(eliza): these strings are all immutable; we should consider
// `Arc`ing them to make this more efficient...
let field_names = self.fields.iter().map(field::Match::name).collect();
Some(StaticDirective::new(
self.target.clone(),
field_names,
self.level,
))
}
fn is_static(&self) -> bool {
!self.has_name() && !self.fields.iter().any(field::Match::has_value)
}
pub(super) fn is_dynamic(&self) -> bool {
self.has_name() || self.has_fields()
}
pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch> {
let fieldset = meta.fields();
let fields = self
.fields
.iter()
.filter_map(
|field::Match {
ref name,
ref value,
}| {
if let Some(field) = fieldset.field(name) {
let value = value.as_ref().cloned()?;
Some(Ok((field, value)))
} else {
Some(Err(()))
}
},
)
.collect::<Result<FieldMap<_>, ()>>()
.ok()?;
Some(field::CallsiteMatch {
fields,
level: self.level,
})
}
pub(super) fn make_tables(
directives: impl IntoIterator<Item = Directive>,
) -> (Dynamics, Statics) {
// TODO(eliza): this could be made more efficient...
let (dyns, stats): (Vec<Directive>, Vec<Directive>) =
directives.into_iter().partition(Directive::is_dynamic);
let statics = stats
.into_iter()
.filter_map(|d| d.to_static())
.chain(dyns.iter().filter_map(Directive::to_static))
.collect();
(Dynamics::from_iter(dyns), statics)
}
pub(super) fn deregexify(&mut self) {
for field in &mut self.fields {
field.value = match field.value.take() {
Some(field::ValueMatch::Pat(pat)) => {
Some(field::ValueMatch::Debug(pat.into_debug_match()))
}
x => x,
}
}
}
pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
let mut cur = Self {
level: LevelFilter::TRACE,
target: None,
in_span: None,
fields: Vec::new(),
};
#[derive(Debug)]
enum ParseState {
Start,
LevelOrTarget { start: usize },
Span { span_start: usize },
Field { field_start: usize },
Fields,
Target,
Level { level_start: usize },
Complete,
}
use ParseState::*;
let mut state = Start;
for (i, c) in from.trim().char_indices() {
state = match (state, c) {
(Start, '[') => Span { span_start: i + 1 },
(Start, c) if !['-', ':', '_'].contains(&c) && !c.is_alphanumeric() => {
return Err(ParseError::new())
}
(Start, _) => LevelOrTarget { start: i },
(LevelOrTarget { start }, '=') => {
cur.target = Some(from[start..i].to_owned());
Level { level_start: i + 1 }
}
(LevelOrTarget { start }, '[') => {
cur.target = Some(from[start..i].to_owned());
Span { span_start: i + 1 }
}
(LevelOrTarget { start }, ',') => {
let (level, target) = match &from[start..] {
"" => (LevelFilter::TRACE, None),
level_or_target => match LevelFilter::from_str(level_or_target) {
Ok(level) => (level, None),
Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
},
};
cur.level = level;
cur.target = target;
Complete
}
(state @ LevelOrTarget { .. }, _) => state,
(Target, '=') => Level { level_start: i + 1 },
(Span { span_start }, ']') => {
cur.in_span = Some(from[span_start..i].to_owned());
Target
}
(Span { span_start }, '{') => {
cur.in_span = match &from[span_start..i] {
"" => None,
_ => Some(from[span_start..i].to_owned()),
};
Field { field_start: i + 1 }
}
(state @ Span { .. }, _) => state,
(Field { field_start }, '}') => {
cur.fields.push(match &from[field_start..i] {
"" => return Err(ParseError::new()),
field => field::Match::parse(field, regex)?,
});
Fields
}
(Field { field_start }, ',') => {
cur.fields.push(match &from[field_start..i] {
"" => return Err(ParseError::new()),
field => field::Match::parse(field, regex)?,
});
Field { field_start: i + 1 }
}
(state @ Field { .. }, _) => state,
(Fields, ']') => Target,
(Level { level_start }, ',') => {
cur.level = match &from[level_start..i] {
"" => LevelFilter::TRACE,
level => LevelFilter::from_str(level)?,
};
Complete
}
(state @ Level { .. }, _) => state,
_ => return Err(ParseError::new()),
};
}
match state {
LevelOrTarget { start } => {
let (level, target) = match &from[start..] {
"" => (LevelFilter::TRACE, None),
level_or_target => match LevelFilter::from_str(level_or_target) {
Ok(level) => (level, None),
// Setting the target without the level enables every level for that target
Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
},
};
cur.level = level;
cur.target = target;
}
Level { level_start } => {
cur.level = match &from[level_start..] {
"" => LevelFilter::TRACE,
level => LevelFilter::from_str(level)?,
};
}
Target | Complete => {}
_ => return Err(ParseError::new()),
};
Ok(cur)
}
}
impl Match for Directive {
fn cares_about(&self, meta: &Metadata<'_>) -> bool {
// Does this directive have a target filter, and does it match the
// metadata's target?
if let Some(ref target) = self.target {
if !meta.target().starts_with(&target[..]) {
return false;
}
}
// Do we have a name filter, and does it match the metadata's name?
// TODO(eliza): put name globbing here?
if let Some(ref name) = self.in_span {
if name != meta.name() {
return false;
}
}
// Does the metadata define all the fields that this directive cares about?
let actual_fields = meta.fields();
for expected_field in &self.fields {
// Does the actual field set (from the metadata) contain this field?
if actual_fields.field(&expected_field.name).is_none() {
return false;
}
}
true
}
fn level(&self) -> &LevelFilter {
&self.level
}
}
impl FromStr for Directive {
type Err = ParseError;
fn from_str(from: &str) -> Result<Self, Self::Err> {
Directive::parse(from, true)
}
}
impl Default for Directive {
fn default() -> Self {
Directive {
level: LevelFilter::OFF,
target: None,
in_span: None,
fields: Vec::new(),
}
}
}
impl PartialOrd for Directive {
fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Directive {
fn cmp(&self, other: &Directive) -> Ordering {
// We attempt to order directives by how "specific" they are. This
// ensures that we try the most specific directives first when
// attempting to match a piece of metadata.
// First, we compare based on whether a target is specified, and the
// lengths of those targets if both have targets.
let ordering = self
.target
.as_ref()
.map(String::len)
.cmp(&other.target.as_ref().map(String::len))
// Next compare based on the presence of span names.
.then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some()))
// Then we compare how many fields are defined by each
// directive.
.then_with(|| self.fields.len().cmp(&other.fields.len()))
// Finally, we fall back to lexicographical ordering if the directives are
// equally specific. Although this is no longer semantically important,
// we need to define a total ordering to determine the directive's place
// in the BTreeMap.
.then_with(|| {
self.target
.cmp(&other.target)
.then_with(|| self.in_span.cmp(&other.in_span))
.then_with(|| self.fields[..].cmp(&other.fields[..]))
})
.reverse();
#[cfg(debug_assertions)]
{
if ordering == Ordering::Equal {
debug_assert_eq!(
self.target, other.target,
"invariant violated: Ordering::Equal must imply a.target == b.target"
);
debug_assert_eq!(
self.in_span, other.in_span,
"invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
);
debug_assert_eq!(
self.fields, other.fields,
"invariant violated: Ordering::Equal must imply a.fields == b.fields"
);
}
}
ordering
}
}
impl fmt::Display for Directive {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut wrote_any = false;
if let Some(ref target) = self.target {
fmt::Display::fmt(target, f)?;
wrote_any = true;
}
if self.in_span.is_some() || !self.fields.is_empty() {
f.write_str("[")?;
if let Some(ref span) = self.in_span {
fmt::Display::fmt(span, f)?;
}
let mut fields = self.fields.iter();
if let Some(field) = fields.next() {
write!(f, "{{{}", field)?;
for field in fields {
write!(f, ",{}", field)?;
}
f.write_str("}")?;
}
f.write_str("]")?;
wrote_any = true;
}
if wrote_any {
f.write_str("=")?;
}
fmt::Display::fmt(&self.level, f)
}
}
impl From<LevelFilter> for Directive {
fn from(level: LevelFilter) -> Self {
Self {
level,
..Self::default()
}
}
}
impl From<Level> for Directive {
fn from(level: Level) -> Self {
LevelFilter::from_level(level).into()
}
}
// === impl Dynamics ===
impl Dynamics {
pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher> {
let mut base_level = None;
let field_matches = self
.directives_for(metadata)
.filter_map(|d| {
if let Some(f) = d.field_matcher(metadata) {
return Some(f);
}
match base_level {
Some(ref b) if d.level > *b => base_level = Some(d.level),
None => base_level = Some(d.level),
_ => {}
}
None
})
.collect();
if let Some(base_level) = base_level {
Some(CallsiteMatcher {
field_matches,
base_level,
})
} else if !field_matches.is_empty() {
Some(CallsiteMatcher {
field_matches,
base_level: base_level.unwrap_or(LevelFilter::OFF),
})
} else {
None
}
}
pub(crate) fn has_value_filters(&self) -> bool {
self.directives()
.any(|d| d.fields.iter().any(|f| f.value.is_some()))
}
}
// ===== impl DynamicMatch =====
impl CallsiteMatcher {
/// Create a new `SpanMatch` for a given instance of the matched callsite.
pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher {
let field_matches = self
.field_matches
.iter()
.map(|m| {
let m = m.to_span_match();
attrs.record(&mut m.visitor());
m
})
.collect();
SpanMatcher {
field_matches,
base_level: self.base_level,
}
}
}
impl SpanMatcher {
/// Returns the level currently enabled for this callsite.
pub(crate) fn level(&self) -> LevelFilter {
self.field_matches
.iter()
.filter_map(field::SpanMatch::filter)
.max()
.unwrap_or(self.base_level)
}
pub(crate) fn record_update(&self, record: &span::Record<'_>) {
for m in &self.field_matches {
record.record(&mut m.visitor())
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> {
dirs.as_ref()
.split(',')
.filter_map(|s| s.parse().ok())
.collect()
}
fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> {
dirs.as_ref()
.split(',')
.map(|s| {
s.parse()
.unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err))
})
.collect()
}
#[test]
fn directive_ordering_by_target_len() {
// TODO(eliza): it would be nice to have a property-based test for this
// instead.
let mut dirs = expect_parse(
"foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
);
dirs.sort_unstable();
let expected = vec![
"a_really_long_name_with_no_colons",
"foo::bar::baz",
"foo::bar",
"foo",
];
let sorted = dirs
.iter()
.map(|d| d.target.as_ref().unwrap())
.collect::<Vec<_>>();
assert_eq!(expected, sorted);
}
#[test]
fn directive_ordering_by_span() {
// TODO(eliza): it would be nice to have a property-based test for this
// instead.
let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
dirs.sort_unstable();
let expected = vec!["baz::quux", "bar", "foo", "a"];
let sorted = dirs
.iter()
.map(|d| d.target.as_ref().unwrap())
.collect::<Vec<_>>();
assert_eq!(expected, sorted);
}
#[test]
fn directive_ordering_uses_lexicographic_when_equal() {
// TODO(eliza): it would be nice to have a property-based test for this
// instead.
let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
dirs.sort_unstable();
let expected = vec![
("span", Some("b")),
("span", Some("a")),
("c", None),
("b", None),
("a", None),
];
let sorted = dirs
.iter()
.map(|d| {
(
d.target.as_ref().unwrap().as_ref(),
d.in_span.as_ref().map(String::as_ref),
)
})
.collect::<Vec<_>>();
assert_eq!(expected, sorted);
}
// TODO: this test requires the parser to support directives with multiple
// fields, which it currently can't handle. We should enable this test when
// that's implemented.
#[test]
#[ignore]
fn directive_ordering_by_field_num() {
// TODO(eliza): it would be nice to have a property-based test for this
// instead.
let mut dirs = expect_parse(
"b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info"
);
dirs.sort_unstable();
let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"];
let sorted = dirs
.iter()
.map(|d| d.target.as_ref().unwrap())
.collect::<Vec<_>>();
assert_eq!(expected, sorted);
}
#[test]
fn parse_directives_ralith() {
let dirs = parse_directives("common=trace,server=trace");
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("common".to_string()));
assert_eq!(dirs[0].level, LevelFilter::TRACE);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("server".to_string()));
assert_eq!(dirs[1].level, LevelFilter::TRACE);
assert_eq!(dirs[1].in_span, None);
}
#[test]
fn parse_directives_ralith_uc() {
let dirs = parse_directives("common=INFO,server=DEBUG");
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("common".to_string()));
assert_eq!(dirs[0].level, LevelFilter::INFO);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("server".to_string()));
assert_eq!(dirs[1].level, LevelFilter::DEBUG);
assert_eq!(dirs[1].in_span, None);
}
#[test]
fn parse_directives_ralith_mixed() {
let dirs = parse_directives("common=iNfo,server=dEbUg");
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("common".to_string()));
assert_eq!(dirs[0].level, LevelFilter::INFO);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("server".to_string()));
assert_eq!(dirs[1].level, LevelFilter::DEBUG);
assert_eq!(dirs[1].in_span, None);
}
#[test]
fn parse_directives_valid() {
let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::ERROR);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::TRACE);
assert_eq!(dirs[1].in_span, None);
assert_eq!(dirs[2].target, Some("crate2".to_string()));
assert_eq!(dirs[2].level, LevelFilter::DEBUG);
assert_eq!(dirs[2].in_span, None);
assert_eq!(dirs[3].target, Some("crate3".to_string()));
assert_eq!(dirs[3].level, LevelFilter::OFF);
assert_eq!(dirs[3].in_span, None);
}
#[test]
fn parse_level_directives() {
let dirs = parse_directives(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
);
assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::ERROR);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::WARN);
assert_eq!(dirs[1].in_span, None);
assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
assert_eq!(dirs[2].level, LevelFilter::INFO);
assert_eq!(dirs[2].in_span, None);
assert_eq!(dirs[3].target, Some("crate2".to_string()));
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
assert_eq!(dirs[3].in_span, None);
assert_eq!(dirs[4].target, Some("crate3".to_string()));
assert_eq!(dirs[4].level, LevelFilter::TRACE);
assert_eq!(dirs[4].in_span, None);
assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
assert_eq!(dirs[5].level, LevelFilter::OFF);
assert_eq!(dirs[5].in_span, None);
}
#[test]
fn parse_uppercase_level_directives() {
let dirs = parse_directives(
"crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
);
assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::ERROR);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::WARN);
assert_eq!(dirs[1].in_span, None);
assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
assert_eq!(dirs[2].level, LevelFilter::INFO);
assert_eq!(dirs[2].in_span, None);
assert_eq!(dirs[3].target, Some("crate2".to_string()));
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
assert_eq!(dirs[3].in_span, None);
assert_eq!(dirs[4].target, Some("crate3".to_string()));
assert_eq!(dirs[4].level, LevelFilter::TRACE);
assert_eq!(dirs[4].in_span, None);
assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
assert_eq!(dirs[5].level, LevelFilter::OFF);
assert_eq!(dirs[5].in_span, None);
}
#[test]
fn parse_numeric_level_directives() {
let dirs = parse_directives(
"crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
crate3=5,crate3::mod2::mod1=0",
);
assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::ERROR);
assert_eq!(dirs[0].in_span, None);
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::WARN);
assert_eq!(dirs[1].in_span, None);
assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
assert_eq!(dirs[2].level, LevelFilter::INFO);
assert_eq!(dirs[2].in_span, None);
assert_eq!(dirs[3].target, Some("crate2".to_string()));
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
assert_eq!(dirs[3].in_span, None);
assert_eq!(dirs[4].target, Some("crate3".to_string()));
assert_eq!(dirs[4].level, LevelFilter::TRACE);
assert_eq!(dirs[4].in_span, None);
assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
assert_eq!(dirs[5].level, LevelFilter::OFF);
assert_eq!(dirs[5].in_span, None);
}
#[test]
fn parse_directives_invalid_crate() {
// test parse_directives with multiple = in specification
let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::DEBUG);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_invalid_level() {
// test parse_directives with 'noNumber' as log level
let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::DEBUG);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_string_level() {
// test parse_directives with 'warn' as log level
let dirs = parse_directives("crate1::mod1=wrong,crate2=warn");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::WARN);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_empty_level() {
// test parse_directives with '' as log level
let dirs = parse_directives("crate1::mod1=wrong,crate2=");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::TRACE);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_global() {
// test parse_directives with no crate
let dirs = parse_directives("warn,crate2=debug");
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, None);
assert_eq!(dirs[0].level, LevelFilter::WARN);
assert_eq!(dirs[1].in_span, None);
assert_eq!(dirs[1].target, Some("crate2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::DEBUG);
assert_eq!(dirs[1].in_span, None);
}
// helper function for tests below
fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) {
let dirs = parse_directives(directive_to_test);
assert_eq!(
dirs.len(),
1,
"\ninput: \"{}\"; parsed: {:#?}",
directive_to_test,
dirs
);
assert_eq!(dirs[0].target, None);
assert_eq!(dirs[0].level, level_expected);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_global_bare_warn_lc() {
// test parse_directives with no crate, in isolation, all lowercase
test_parse_bare_level("warn", LevelFilter::WARN);
}
#[test]
fn parse_directives_global_bare_warn_uc() {
// test parse_directives with no crate, in isolation, all uppercase
test_parse_bare_level("WARN", LevelFilter::WARN);
}
#[test]
fn parse_directives_global_bare_warn_mixed() {
// test parse_directives with no crate, in isolation, mixed case
test_parse_bare_level("wArN", LevelFilter::WARN);
}
#[test]
fn parse_directives_valid_with_spans() {
let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug");
assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::ERROR);
assert_eq!(dirs[0].in_span, Some("foo".to_string()));
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[1].level, LevelFilter::TRACE);
assert_eq!(dirs[1].in_span, Some("bar".to_string()));
assert_eq!(dirs[2].target, Some("crate2".to_string()));
assert_eq!(dirs[2].level, LevelFilter::DEBUG);
assert_eq!(dirs[2].in_span, Some("baz".to_string()));
}
#[test]
fn parse_directives_with_dash_in_target_name() {
let dirs = parse_directives("target-name=info");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("target-name".to_string()));
assert_eq!(dirs[0].level, LevelFilter::INFO);
assert_eq!(dirs[0].in_span, None);
}
#[test]
fn parse_directives_with_dash_in_span_name() {
// Reproduces https://github.com/tokio-rs/tracing/issues/1367
let dirs = parse_directives("target[span-name]=info");
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("target".to_string()));
assert_eq!(dirs[0].level, LevelFilter::INFO);
assert_eq!(dirs[0].in_span, Some("span-name".to_string()));
}
#[test]
fn parse_directives_with_special_characters_in_span_name() {
let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
let dirs = parse_directives(format!("target[{}]=info", span_name));
assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("target".to_string()));
assert_eq!(dirs[0].level, LevelFilter::INFO);
assert_eq!(dirs[0].in_span, Some(span_name.to_string()));
}
#[test]
fn parse_directives_with_invalid_span_chars() {
let invalid_span_name = "]{";
let dirs = parse_directives(format!("target[{}]=info", invalid_span_name));
assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs);
}
}

View File

@@ -0,0 +1,627 @@
use matchers::Pattern;
use std::{
cmp::Ordering,
error::Error,
fmt::{self, Write},
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering::*},
Arc,
},
};
use super::{FieldMap, LevelFilter};
use tracing_core::field::{Field, Visit};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Match {
pub(crate) name: String, // TODO: allow match patterns for names?
pub(crate) value: Option<ValueMatch>,
}
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct CallsiteMatch {
pub(crate) fields: FieldMap<ValueMatch>,
pub(crate) level: LevelFilter,
}
#[derive(Debug)]
pub(crate) struct SpanMatch {
fields: FieldMap<(ValueMatch, AtomicBool)>,
level: LevelFilter,
has_matched: AtomicBool,
}
pub(crate) struct MatchVisitor<'a> {
inner: &'a SpanMatch,
}
#[derive(Debug, Clone)]
pub(crate) enum ValueMatch {
/// Matches a specific `bool` value.
Bool(bool),
/// Matches a specific `f64` value.
F64(f64),
/// Matches a specific `u64` value.
U64(u64),
/// Matches a specific `i64` value.
I64(i64),
/// Matches any `NaN` `f64` value.
NaN,
/// Matches any field whose `fmt::Debug` output is equal to a fixed string.
Debug(MatchDebug),
/// Matches any field whose `fmt::Debug` output matches a regular expression
/// pattern.
Pat(Box<MatchPattern>),
}
impl Eq for ValueMatch {}
impl PartialEq for ValueMatch {
fn eq(&self, other: &Self) -> bool {
use ValueMatch::*;
match (self, other) {
(Bool(a), Bool(b)) => a.eq(b),
(F64(a), F64(b)) => {
debug_assert!(!a.is_nan());
debug_assert!(!b.is_nan());
a.eq(b)
}
(U64(a), U64(b)) => a.eq(b),
(I64(a), I64(b)) => a.eq(b),
(NaN, NaN) => true,
(Pat(a), Pat(b)) => a.eq(b),
_ => false,
}
}
}
impl Ord for ValueMatch {
fn cmp(&self, other: &Self) -> Ordering {
use ValueMatch::*;
match (self, other) {
(Bool(this), Bool(that)) => this.cmp(that),
(Bool(_), _) => Ordering::Less,
(F64(this), F64(that)) => this
.partial_cmp(that)
.expect("`ValueMatch::F64` may not contain `NaN` values"),
(F64(_), Bool(_)) => Ordering::Greater,
(F64(_), _) => Ordering::Less,
(NaN, NaN) => Ordering::Equal,
(NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater,
(NaN, _) => Ordering::Less,
(U64(this), U64(that)) => this.cmp(that),
(U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater,
(U64(_), _) => Ordering::Less,
(I64(this), I64(that)) => this.cmp(that),
(I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => {
Ordering::Greater
}
(I64(_), _) => Ordering::Less,
(Pat(this), Pat(that)) => this.cmp(that),
(Pat(_), _) => Ordering::Greater,
(Debug(this), Debug(that)) => this.cmp(that),
(Debug(_), _) => Ordering::Greater,
}
}
}
impl PartialOrd for ValueMatch {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// Matches a field's `fmt::Debug` output against a regular expression pattern.
///
/// This is used for matching all non-literal field value filters when regular
/// expressions are enabled.
#[derive(Debug, Clone)]
pub(crate) struct MatchPattern {
pub(crate) matcher: Pattern,
pattern: Arc<str>,
}
/// Matches a field's `fmt::Debug` output against a fixed string pattern.
///
/// This is used for matching all non-literal field value filters when regular
/// expressions are disabled.
#[derive(Debug, Clone)]
pub(crate) struct MatchDebug {
pattern: Arc<str>,
}
/// Indicates that a field name specified in a filter directive was invalid.
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
pub struct BadName {
name: String,
}
// === impl Match ===
impl Match {
pub(crate) fn has_value(&self) -> bool {
self.value.is_some()
}
// TODO: reference count these strings?
pub(crate) fn name(&self) -> String {
self.name.clone()
}
pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> {
let mut parts = s.split('=');
let name = parts
.next()
.ok_or_else(|| BadName {
name: "".to_string(),
})?
// TODO: validate field name
.to_string();
let value = parts
.next()
.map(|part| match regex {
true => ValueMatch::parse_regex(part),
false => Ok(ValueMatch::parse_non_regex(part)),
})
.transpose()?;
Ok(Match { name, value })
}
}
impl fmt::Display for Match {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.name, f)?;
if let Some(ref value) = self.value {
write!(f, "={}", value)?;
}
Ok(())
}
}
impl Ord for Match {
fn cmp(&self, other: &Self) -> Ordering {
// Ordering for `Match` directives is based first on _whether_ a value
// is matched or not. This is semantically meaningful --- we would
// prefer to check directives that match values first as they are more
// specific.
let has_value = match (self.value.as_ref(), other.value.as_ref()) {
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
_ => Ordering::Equal,
};
// If both directives match a value, we fall back to the field names in
// length + lexicographic ordering, and if these are equal as well, we
// compare the match directives.
//
// This ordering is no longer semantically meaningful but is necessary
// so that the directives can be stored in the `BTreeMap` in a defined
// order.
has_value
.then_with(|| self.name.cmp(&other.name))
.then_with(|| self.value.cmp(&other.value))
}
}
impl PartialOrd for Match {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// === impl ValueMatch ===
fn value_match_f64(v: f64) -> ValueMatch {
if v.is_nan() {
ValueMatch::NaN
} else {
ValueMatch::F64(v)
}
}
impl ValueMatch {
/// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular
/// expressions.
///
/// This returns an error if the string didn't contain a valid `bool`,
/// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular
/// expression.
#[allow(clippy::result_large_err)]
fn parse_regex(s: &str) -> Result<Self, matchers::BuildError> {
s.parse::<bool>()
.map(ValueMatch::Bool)
.or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
.or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
.or_else(|_| s.parse::<f64>().map(value_match_f64))
.or_else(|_| {
s.parse::<MatchPattern>()
.map(|p| ValueMatch::Pat(Box::new(p)))
})
}
/// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed
/// string.
///
/// This does *not* return an error, because any string that isn't a valid
/// `bool`, `u64`, `i64`, or `f64` literal is treated as expected
/// `fmt::Debug` output.
fn parse_non_regex(s: &str) -> Self {
s.parse::<bool>()
.map(ValueMatch::Bool)
.or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
.or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
.or_else(|_| s.parse::<f64>().map(value_match_f64))
.unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
}
}
impl fmt::Display for ValueMatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
}
}
}
// === impl MatchPattern ===
impl FromStr for MatchPattern {
type Err = matchers::BuildError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let matcher = s.parse::<Pattern>()?;
Ok(Self {
matcher,
pattern: s.to_owned().into(),
})
}
}
impl fmt::Display for MatchPattern {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&*self.pattern, f)
}
}
impl AsRef<str> for MatchPattern {
#[inline]
fn as_ref(&self) -> &str {
self.pattern.as_ref()
}
}
impl MatchPattern {
#[inline]
fn str_matches(&self, s: &impl AsRef<str>) -> bool {
self.matcher.matches(s)
}
#[inline]
fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
self.matcher.debug_matches(d)
}
pub(super) fn into_debug_match(self) -> MatchDebug {
MatchDebug {
pattern: self.pattern,
}
}
}
impl PartialEq for MatchPattern {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.pattern == other.pattern
}
}
impl Eq for MatchPattern {}
impl PartialOrd for MatchPattern {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MatchPattern {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.pattern.cmp(&other.pattern)
}
}
// === impl MatchDebug ===
impl MatchDebug {
fn new(s: &str) -> Self {
Self {
pattern: s.to_owned().into(),
}
}
#[inline]
fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
// Naively, we would probably match a value's `fmt::Debug` output by
// formatting it to a string, and then checking if the string is equal
// to the expected pattern. However, this would require allocating every
// time we want to match a field value against a `Debug` matcher, which
// can be avoided.
//
// Instead, we implement `fmt::Write` for a type that, rather than
// actually _writing_ the strings to something, matches them against the
// expected pattern, and returns an error if the pattern does not match.
struct Matcher<'a> {
pattern: &'a str,
}
impl fmt::Write for Matcher<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
// If the string is longer than the remaining expected string,
// we know it won't match, so bail.
if s.len() > self.pattern.len() {
return Err(fmt::Error);
}
// If the expected string begins with the string that was
// written, we are still potentially a match. Advance the
// position in the expected pattern to chop off the matched
// output, and continue.
if self.pattern.starts_with(s) {
self.pattern = &self.pattern[s.len()..];
return Ok(());
}
// Otherwise, the expected string doesn't include the string
// that was written at the current position, so the `fmt::Debug`
// output doesn't match! Return an error signalling that this
// doesn't match.
Err(fmt::Error)
}
}
let mut matcher = Matcher {
pattern: &self.pattern,
};
// Try to "write" the value's `fmt::Debug` output to a `Matcher`. This
// returns an error if the `fmt::Debug` implementation wrote any
// characters that did not match the expected pattern.
write!(matcher, "{:?}", d).is_ok()
}
}
impl fmt::Display for MatchDebug {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&*self.pattern, f)
}
}
impl AsRef<str> for MatchDebug {
#[inline]
fn as_ref(&self) -> &str {
self.pattern.as_ref()
}
}
impl PartialEq for MatchDebug {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.pattern == other.pattern
}
}
impl Eq for MatchDebug {}
impl PartialOrd for MatchDebug {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MatchDebug {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.pattern.cmp(&other.pattern)
}
}
// === impl BadName ===
impl Error for BadName {}
impl fmt::Display for BadName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid field name `{}`", self.name)
}
}
impl CallsiteMatch {
pub(crate) fn to_span_match(&self) -> SpanMatch {
let fields = self
.fields
.iter()
.map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false))))
.collect();
SpanMatch {
fields,
level: self.level,
has_matched: AtomicBool::new(false),
}
}
}
impl SpanMatch {
pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
MatchVisitor { inner: self }
}
#[inline]
pub(crate) fn is_matched(&self) -> bool {
if self.has_matched.load(Acquire) {
return true;
}
self.is_matched_slow()
}
#[inline(never)]
fn is_matched_slow(&self) -> bool {
let matched = self
.fields
.values()
.all(|(_, matched)| matched.load(Acquire));
if matched {
self.has_matched.store(true, Release);
}
matched
}
#[inline]
pub(crate) fn filter(&self) -> Option<LevelFilter> {
if self.is_matched() {
Some(self.level)
} else {
None
}
}
}
impl Visit for MatchVisitor<'_> {
fn record_f64(&mut self, field: &Field, value: f64) {
match self.inner.fields.get(field) {
Some((ValueMatch::NaN, ref matched)) if value.is_nan() => {
matched.store(true, Release);
}
Some((ValueMatch::F64(ref e), ref matched))
if (value - *e).abs() < f64::EPSILON =>
{
matched.store(true, Release);
}
_ => {}
}
}
fn record_i64(&mut self, field: &Field, value: i64) {
use std::convert::TryInto;
match self.inner.fields.get(field) {
Some((ValueMatch::I64(ref e), ref matched)) if value == *e => {
matched.store(true, Release);
}
Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => {
matched.store(true, Release);
}
_ => {}
}
}
fn record_u64(&mut self, field: &Field, value: u64) {
match self.inner.fields.get(field) {
Some((ValueMatch::U64(ref e), ref matched)) if value == *e => {
matched.store(true, Release);
}
_ => {}
}
}
fn record_bool(&mut self, field: &Field, value: bool) {
match self.inner.fields.get(field) {
Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => {
matched.store(true, Release);
}
_ => {}
}
}
fn record_str(&mut self, field: &Field, value: &str) {
match self.inner.fields.get(field) {
Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
matched.store(true, Release);
}
Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
matched.store(true, Release)
}
_ => {}
}
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
match self.inner.fields.get(field) {
Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
matched.store(true, Release);
}
Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
matched.store(true, Release)
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
#[allow(dead_code)]
struct MyStruct {
answer: usize,
question: &'static str,
}
#[test]
fn debug_struct_match() {
let my_struct = MyStruct {
answer: 42,
question: "life, the universe, and everything",
};
let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
assert_eq!(
format!("{:?}", my_struct),
pattern,
"`MyStruct`'s `Debug` impl doesn't output the expected string"
);
let matcher = MatchDebug {
pattern: pattern.into(),
};
assert!(matcher.debug_matches(&my_struct))
}
#[test]
fn debug_struct_not_match() {
let my_struct = MyStruct {
answer: 42,
question: "what shall we have for lunch?",
};
let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
assert_eq!(
format!("{:?}", my_struct),
"MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
"`MyStruct`'s `Debug` impl doesn't output the expected string"
);
let matcher = MatchDebug {
pattern: pattern.into(),
};
assert!(!matcher.debug_matches(&my_struct))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,749 @@
use crate::{
filter::LevelFilter,
layer::{Context, Layer},
};
use core::{any::type_name, fmt, marker::PhantomData};
use tracing_core::{Interest, Metadata, Subscriber};
/// A filter implemented by a closure or function pointer that
/// determines whether a given span or event is enabled, based on its
/// [`Metadata`].
///
/// This type can be used for both [per-layer filtering][plf] (using its
/// [`Filter`] implementation) and [global filtering][global] (using its
/// [`Layer`] implementation).
///
/// See the [documentation on filtering with layers][filtering] for details.
///
/// [`Metadata`]: tracing_core::Metadata
/// [`Filter`]: crate::layer::Filter
/// [`Layer`]: crate::layer::Layer
/// [plf]: crate::layer#per-layer-filtering
/// [global]: crate::layer#global-filtering
/// [filtering]: crate::layer#filtering-with-layers
#[derive(Clone)]
pub struct FilterFn<F = fn(&Metadata<'_>) -> bool> {
enabled: F,
max_level_hint: Option<LevelFilter>,
}
/// A filter implemented by a closure or function pointer that
/// determines whether a given span or event is enabled _dynamically_,
/// potentially based on the current [span context].
///
/// This type can be used for both [per-layer filtering][plf] (using its
/// [`Filter`] implementation) and [global filtering][global] (using its
/// [`Layer`] implementation).
///
/// See the [documentation on filtering with layers][filtering] for details.
///
/// [span context]: crate::layer::Context
/// [`Filter`]: crate::layer::Filter
/// [`Layer`]: crate::layer::Layer
/// [plf]: crate::layer#per-layer-filtering
/// [global]: crate::layer#global-filtering
/// [filtering]: crate::layer#filtering-with-layers
pub struct DynFilterFn<
S,
// TODO(eliza): should these just be boxed functions?
F = fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R = fn(&'static Metadata<'static>) -> Interest,
> {
enabled: F,
register_callsite: Option<R>,
max_level_hint: Option<LevelFilter>,
_s: PhantomData<fn(S)>,
}
// === impl FilterFn ===
/// Constructs a [`FilterFn`], from a function or closure that returns `true` if
/// a span or event should be enabled, based on its [`Metadata`].
///
/// The returned [`FilterFn`] can be used for both [per-layer filtering][plf]
/// (using its [`Filter`] implementation) and [global filtering][global] (using
/// its [`Layer`] implementation).
///
/// See the [documentation on filtering with layers][filtering] for details.
///
/// This is equivalent to calling [`FilterFn::new`].
///
/// [`Metadata`]: tracing_core::Metadata
/// [`Filter`]: crate::layer::Filter
/// [`Layer`]: crate::layer::Layer
/// [plf]: crate::layer#per-layer-filtering
/// [global]: crate::layer#global-filtering
/// [filtering]: crate::layer#filtering-with-layers
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter,
/// util::SubscriberInitExt,
/// };
///
/// let my_filter = filter::filter_fn(|metadata| {
/// // Only enable spans or events with the target "interesting_things"
/// metadata.target() == "interesting_things"
/// });
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
///
/// // This event will not be enabled.
/// tracing::warn!("something important but uninteresting happened!");
///
/// // This event will be enabled.
/// tracing::debug!(target: "interesting_things", "an interesting minor detail...");
/// ```
pub fn filter_fn<F>(f: F) -> FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
FilterFn::new(f)
}
/// Constructs a [`DynFilterFn`] from a function or closure that returns `true`
/// if a span or event should be enabled within a particular [span context][`Context`].
///
/// This is equivalent to calling [`DynFilterFn::new`].
///
/// Unlike [`filter_fn`], this function takes a closure or function pointer
/// taking the [`Metadata`] for a span or event *and* the current [`Context`].
/// This means that a [`DynFilterFn`] can choose whether to enable spans or
/// events based on information about the _current_ span (or its parents).
///
/// If this is *not* necessary, use [`filter_fn`] instead.
///
/// The returned [`DynFilterFn`] can be used for both [per-layer filtering][plf]
/// (using its [`Filter`] implementation) and [global filtering][global] (using
/// its [`Layer`] implementation).
///
/// See the [documentation on filtering with layers][filtering] for details.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter,
/// util::SubscriberInitExt,
/// };
///
/// // Only enable spans or events within a span named "interesting_span".
/// let my_filter = filter::dynamic_filter_fn(|metadata, cx| {
/// // If this *is* "interesting_span", make sure to enable it.
/// if metadata.is_span() && metadata.name() == "interesting_span" {
/// return true;
/// }
///
/// // Otherwise, are we in an interesting span?
/// if let Some(current_span) = cx.lookup_current() {
/// return current_span.name() == "interesting_span";
/// }
///
/// false
/// });
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
///
/// // This event will not be enabled.
/// tracing::info!("something happened");
///
/// tracing::info_span!("interesting_span").in_scope(|| {
/// // This event will be enabled.
/// tracing::debug!("something else happened");
/// });
/// ```
///
/// [`Filter`]: crate::layer::Filter
/// [`Layer`]: crate::layer::Layer
/// [plf]: crate::layer#per-layer-filtering
/// [global]: crate::layer#global-filtering
/// [filtering]: crate::layer#filtering-with-layers
/// [`Context`]: crate::layer::Context
/// [`Metadata`]: tracing_core::Metadata
pub fn dynamic_filter_fn<S, F>(f: F) -> DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
DynFilterFn::new(f)
}
impl<F> FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
/// Constructs a [`FilterFn`] from a function or closure that returns `true`
/// if a span or event should be enabled, based on its [`Metadata`].
///
/// If determining whether a span or event should be enabled also requires
/// information about the current span context, use [`DynFilterFn`] instead.
///
/// See the [documentation on per-layer filtering][plf] for details on using
/// [`Filter`]s.
///
/// [`Filter`]: crate::layer::Filter
/// [plf]: crate::layer#per-layer-filtering
/// [`Metadata`]: tracing_core::Metadata
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter::FilterFn,
/// util::SubscriberInitExt,
/// };
///
/// let my_filter = FilterFn::new(|metadata| {
/// // Only enable spans or events with the target "interesting_things"
/// metadata.target() == "interesting_things"
/// });
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
///
/// // This event will not be enabled.
/// tracing::warn!("something important but uninteresting happened!");
///
/// // This event will be enabled.
/// tracing::debug!(target: "interesting_things", "an interesting minor detail...");
/// ```
pub fn new(enabled: F) -> Self {
Self {
enabled,
max_level_hint: None,
}
}
/// Sets the highest verbosity [`Level`] the filter function will enable.
///
/// The value passed to this method will be returned by this `FilterFn`'s
/// [`Filter::max_level_hint`] method.
///
/// If the provided function will not enable all levels, it is recommended
/// to call this method to configure it with the most verbose level it will
/// enable.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter::{filter_fn, LevelFilter},
/// util::SubscriberInitExt,
/// };
/// use tracing_core::Level;
///
/// let my_filter = filter_fn(|metadata| {
/// // Only enable spans or events with targets starting with `my_crate`
/// // and levels at or below `INFO`.
/// metadata.level() <= &Level::INFO && metadata.target().starts_with("my_crate")
/// })
/// // Since the filter closure will only enable the `INFO` level and
/// // below, set the max level hint
/// .with_max_level_hint(LevelFilter::INFO);
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
/// ```
///
/// [`Level`]: tracing_core::Level
/// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint
pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self {
Self {
max_level_hint: Some(max_level_hint.into()),
..self
}
}
#[inline]
pub(in crate::filter) fn is_enabled(&self, metadata: &Metadata<'_>) -> bool {
let enabled = (self.enabled)(metadata);
debug_assert!(
!enabled || self.is_below_max_level(metadata),
"FilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
enabled
}
#[inline]
pub(in crate::filter) fn is_callsite_enabled(
&self,
metadata: &'static Metadata<'static>,
) -> Interest {
// Because `self.enabled` takes a `Metadata` only (and no `Context`
// parameter), we can reasonably assume its results are cacheable, and
// just return `Interest::always`/`Interest::never`.
if (self.enabled)(metadata) {
debug_assert!(
self.is_below_max_level(metadata),
"FilterFn<{}> claimed it was only interested in {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
return Interest::always();
}
Interest::never()
}
fn is_below_max_level(&self, metadata: &Metadata<'_>) -> bool {
self.max_level_hint
.as_ref()
.map(|hint| metadata.level() <= hint)
.unwrap_or(true)
}
}
impl<S, F> Layer<S> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool + 'static,
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool {
self.is_enabled(metadata)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<F> From<F> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
fn from(enabled: F) -> Self {
Self::new(enabled)
}
}
impl<F> fmt::Debug for FilterFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FilterFn")
.field("enabled", &format_args!("{}", type_name::<F>()))
.field("max_level_hint", &self.max_level_hint)
.finish()
}
}
// === impl DynFilterFn ==
impl<S, F> DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
/// Constructs a [`Filter`] from a function or closure that returns `true`
/// if a span or event should be enabled in the current [span
/// context][`Context`].
///
/// Unlike [`FilterFn`], a `DynFilterFn` is constructed from a closure or
/// function pointer that takes both the [`Metadata`] for a span or event
/// *and* the current [`Context`]. This means that a [`DynFilterFn`] can
/// choose whether to enable spans or events based on information about the
/// _current_ span (or its parents).
///
/// If this is *not* necessary, use [`FilterFn`] instead.
///
/// See the [documentation on per-layer filtering][plf] for details on using
/// [`Filter`]s.
///
/// [`Filter`]: crate::layer::Filter
/// [plf]: crate::layer#per-layer-filtering
/// [`Context`]: crate::layer::Context
/// [`Metadata`]: tracing_core::Metadata
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter::DynFilterFn,
/// util::SubscriberInitExt,
/// };
///
/// // Only enable spans or events within a span named "interesting_span".
/// let my_filter = DynFilterFn::new(|metadata, cx| {
/// // If this *is* "interesting_span", make sure to enable it.
/// if metadata.is_span() && metadata.name() == "interesting_span" {
/// return true;
/// }
///
/// // Otherwise, are we in an interesting span?
/// if let Some(current_span) = cx.lookup_current() {
/// return current_span.name() == "interesting_span";
/// }
///
/// false
/// });
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
///
/// // This event will not be enabled.
/// tracing::info!("something happened");
///
/// tracing::info_span!("interesting_span").in_scope(|| {
/// // This event will be enabled.
/// tracing::debug!("something else happened");
/// });
/// ```
pub fn new(enabled: F) -> Self {
Self {
enabled,
register_callsite: None,
max_level_hint: None,
_s: PhantomData,
}
}
}
impl<S, F, R> DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
/// Sets the highest verbosity [`Level`] the filter function will enable.
///
/// The value passed to this method will be returned by this `DynFilterFn`'s
/// [`Filter::max_level_hint`] method.
///
/// If the provided function will not enable all levels, it is recommended
/// to call this method to configure it with the most verbose level it will
/// enable.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter::{DynFilterFn, LevelFilter},
/// util::SubscriberInitExt,
/// };
/// use tracing_core::Level;
///
/// // Only enable spans or events with levels at or below `INFO`, if
/// // we are inside a span called "interesting_span".
/// let my_filter = DynFilterFn::new(|metadata, cx| {
/// // If the level is greater than INFO, disable it.
/// if metadata.level() > &Level::INFO {
/// return false;
/// }
///
/// // If any span in the current scope is named "interesting_span",
/// // enable this span or event.
/// for span in cx.lookup_current().iter().flat_map(|span| span.scope()) {
/// if span.name() == "interesting_span" {
/// return true;
/// }
/// }
///
/// // Otherwise, disable it.
/// false
/// })
/// // Since the filter closure will only enable the `INFO` level and
/// // below, set the max level hint
/// .with_max_level_hint(LevelFilter::INFO);
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
/// ```
///
/// [`Level`]: tracing_core::Level
/// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint
pub fn with_max_level_hint(self, max_level_hint: impl Into<LevelFilter>) -> Self {
Self {
max_level_hint: Some(max_level_hint.into()),
..self
}
}
/// Adds a function for filtering callsites to this filter.
///
/// When this filter's [`Filter::callsite_enabled`][cse] method is called,
/// the provided function will be used rather than the default.
///
/// By default, `DynFilterFn` assumes that, because the filter _may_ depend
/// dynamically on the current [span context], its result should never be
/// cached. However, some filtering strategies may require dynamic information
/// from the current span context in *some* cases, but are able to make
/// static filtering decisions from [`Metadata`] alone in others.
///
/// For example, consider the filter given in the example for
/// [`DynFilterFn::new`]. That filter enables all spans named
/// "interesting_span", and any events and spans that occur inside of an
/// interesting span. Since the span's name is part of its static
/// [`Metadata`], the "interesting_span" can be enabled in
/// [`callsite_enabled`][cse]:
///
/// ```
/// use tracing_subscriber::{
/// layer::{Layer, SubscriberExt},
/// filter::DynFilterFn,
/// util::SubscriberInitExt,
/// };
/// use tracing_core::subscriber::Interest;
///
/// // Only enable spans or events within a span named "interesting_span".
/// let my_filter = DynFilterFn::new(|metadata, cx| {
/// // If this *is* "interesting_span", make sure to enable it.
/// if metadata.is_span() && metadata.name() == "interesting_span" {
/// return true;
/// }
///
/// // Otherwise, are we in an interesting span?
/// if let Some(current_span) = cx.lookup_current() {
/// return current_span.name() == "interesting_span";
/// }
///
/// false
/// }).with_callsite_filter(|metadata| {
/// // If this is an "interesting_span", we know we will always
/// // enable it.
/// if metadata.is_span() && metadata.name() == "interesting_span" {
/// return Interest::always();
/// }
///
/// // Otherwise, it depends on whether or not we're in an interesting
/// // span. You'll have to ask us again for each span/event!
/// Interest::sometimes()
/// });
///
/// let my_layer = tracing_subscriber::fmt::layer();
///
/// tracing_subscriber::registry()
/// .with(my_layer.with_filter(my_filter))
/// .init();
/// ```
///
/// [cse]: crate::layer::Filter::callsite_enabled
/// [`enabled`]: crate::layer::Filter::enabled
/// [`Metadata`]: tracing_core::Metadata
/// [span context]: crate::layer::Context
pub fn with_callsite_filter<R2>(self, callsite_enabled: R2) -> DynFilterFn<S, F, R2>
where
R2: Fn(&'static Metadata<'static>) -> Interest,
{
let register_callsite = Some(callsite_enabled);
let DynFilterFn {
enabled,
max_level_hint,
_s,
..
} = self;
DynFilterFn {
enabled,
register_callsite,
max_level_hint,
_s,
}
}
fn default_callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest {
// If it's below the configured max level, assume that `enabled` will
// never enable it...
if !is_below_max_level(&self.max_level_hint, metadata) {
debug_assert!(
!(self.enabled)(metadata, &Context::none()),
"DynFilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
return Interest::never();
}
// Otherwise, since this `enabled` function is dynamic and depends on
// the current context, we don't know whether this span or event will be
// enabled or not. Ask again every time it's recorded!
Interest::sometimes()
}
}
impl<S, F, R> DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R: Fn(&'static Metadata<'static>) -> Interest,
{
#[inline]
fn is_enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
let enabled = (self.enabled)(metadata, cx);
debug_assert!(
!enabled || is_below_max_level(&self.max_level_hint, metadata),
"DynFilterFn<{}> claimed it would only enable {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
enabled
}
#[inline]
fn is_callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
let interest = self
.register_callsite
.as_ref()
.map(|callsite_enabled| callsite_enabled(metadata))
.unwrap_or_else(|| self.default_callsite_enabled(metadata));
debug_assert!(
interest.is_never() || is_below_max_level(&self.max_level_hint, metadata),
"DynFilterFn<{}, {}> claimed it was only interested in {:?} and below, \
but it enabled metadata with the {:?} level\nmetadata={:#?}",
type_name::<F>(),
type_name::<R>(),
self.max_level_hint.unwrap(),
metadata.level(),
metadata,
);
interest
}
}
impl<S, F, R> Layer<S> for DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static,
R: Fn(&'static Metadata<'static>) -> Interest + 'static,
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool {
self.is_enabled(metadata, &cx)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<S, F, R> fmt::Debug for DynFilterFn<S, F, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("DynFilterFn");
s.field("enabled", &format_args!("{}", type_name::<F>()));
if self.register_callsite.is_some() {
s.field(
"register_callsite",
&format_args!("Some({})", type_name::<R>()),
);
} else {
s.field("register_callsite", &format_args!("None"));
}
s.field("max_level_hint", &self.max_level_hint).finish()
}
}
impl<S, F, R> Clone for DynFilterFn<S, F, R>
where
F: Clone,
R: Clone,
{
fn clone(&self) -> Self {
Self {
enabled: self.enabled.clone(),
register_callsite: self.register_callsite.clone(),
max_level_hint: self.max_level_hint,
_s: PhantomData,
}
}
}
impl<F, S> From<F> for DynFilterFn<S, F>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
{
fn from(f: F) -> Self {
Self::new(f)
}
}
// === PLF impls ===
feature! {
#![all(feature = "registry", feature = "std")]
use crate::layer::Filter;
impl<S, F> Filter<S> for FilterFn<F>
where
F: Fn(&Metadata<'_>) -> bool,
{
fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> bool {
self.is_enabled(metadata)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
impl<S, F, R> Filter<S> for DynFilterFn<S, F, R>
where
F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool,
R: Fn(&'static Metadata<'static>) -> Interest,
{
fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
self.is_enabled(metadata, cx)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.is_callsite_enabled(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.max_level_hint
}
}
}
fn is_below_max_level(hint: &Option<LevelFilter>, metadata: &Metadata<'_>) -> bool {
hint.as_ref()
.map(|hint| metadata.level() <= hint)
.unwrap_or(true)
}

View File

@@ -0,0 +1,542 @@
//! Filter combinators
use crate::layer::{Context, Filter};
use std::{cmp, fmt, marker::PhantomData};
use tracing_core::{
span::{Attributes, Id, Record},
subscriber::Interest,
LevelFilter, Metadata,
};
/// Combines two [`Filter`]s so that spans and events are enabled if and only if
/// *both* filters return `true`.
///
/// This type is typically returned by the [`FilterExt::and`] method. See that
/// method's documentation for details.
///
/// [`Filter`]: crate::layer::Filter
/// [`FilterExt::and`]: crate::filter::FilterExt::and
pub struct And<A, B, S> {
a: A,
b: B,
_s: PhantomData<fn(S)>,
}
/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
/// returns `true`.
///
/// This type is typically returned by the [`FilterExt::or`] method. See that
/// method's documentation for details.
///
/// [`Filter`]: crate::layer::Filter
/// [`FilterExt::or`]: crate::filter::FilterExt::or
pub struct Or<A, B, S> {
a: A,
b: B,
_s: PhantomData<fn(S)>,
}
/// Inverts the result of a [`Filter`].
///
/// If the wrapped filter would enable a span or event, it will be disabled. If
/// it would disable a span or event, that span or event will be enabled.
///
/// This type is typically returned by the [`FilterExt::not`] method. See that
/// method's documentation for details.
///
/// [`Filter`]: crate::layer::Filter
/// [`FilterExt::not`]: crate::filter::FilterExt::not
pub struct Not<A, S> {
a: A,
_s: PhantomData<fn(S)>,
}
// === impl And ===
impl<A, B, S> And<A, B, S>
where
A: Filter<S>,
B: Filter<S>,
{
/// Combines two [`Filter`]s so that spans and events are enabled if and only if
/// *both* filters return `true`.
///
/// # Examples
///
/// Enabling spans or events if they have both a particular target *and* are
/// above a certain level:
///
/// ```ignore
/// use tracing_subscriber::{
/// filter::{filter_fn, LevelFilter, combinator::And},
/// prelude::*,
/// };
///
/// // Enables spans and events with targets starting with `interesting_target`:
/// let target_filter = filter_fn(|meta| {
/// meta.target().starts_with("interesting_target")
/// });
///
/// // Enables spans and events with levels `INFO` and below:
/// let level_filter = LevelFilter::INFO;
///
/// // Combine the two filters together so that a span or event is only enabled
/// // if *both* filters would enable it:
/// let filter = And::new(level_filter, target_filter);
///
/// tracing_subscriber::registry()
/// .with(tracing_subscriber::fmt::layer().with_filter(filter))
/// .init();
///
/// // This event will *not* be enabled:
/// tracing::info!("an event with an uninteresting target");
///
/// // This event *will* be enabled:
/// tracing::info!(target: "interesting_target", "a very interesting event");
///
/// // This event will *not* be enabled:
/// tracing::debug!(target: "interesting_target", "interesting debug event...");
/// ```
///
/// [`Filter`]: crate::layer::Filter
pub(crate) fn new(a: A, b: B) -> Self {
Self {
a,
b,
_s: PhantomData,
}
}
}
impl<A, B, S> Filter<S> for And<A, B, S>
where
A: Filter<S>,
B: Filter<S>,
{
#[inline]
fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
self.a.enabled(meta, cx) && self.b.enabled(meta, cx)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
let a = self.a.callsite_enabled(meta);
if a.is_never() {
return a;
}
let b = self.b.callsite_enabled(meta);
if !b.is_always() {
return b;
}
a
}
fn max_level_hint(&self) -> Option<LevelFilter> {
// If either hint is `None`, return `None`. Otherwise, return the most restrictive.
cmp::min(self.a.max_level_hint(), self.b.max_level_hint())
}
#[inline]
fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx)
}
#[inline]
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
self.a.on_new_span(attrs, id, ctx.clone());
self.b.on_new_span(attrs, id, ctx)
}
#[inline]
fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
self.a.on_record(id, values, ctx.clone());
self.b.on_record(id, values, ctx);
}
#[inline]
fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_enter(id, ctx.clone());
self.b.on_enter(id, ctx);
}
#[inline]
fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_exit(id, ctx.clone());
self.b.on_exit(id, ctx);
}
#[inline]
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
self.a.on_close(id.clone(), ctx.clone());
self.b.on_close(id, ctx);
}
}
impl<A, B, S> Clone for And<A, B, S>
where
A: Clone,
B: Clone,
{
fn clone(&self) -> Self {
Self {
a: self.a.clone(),
b: self.b.clone(),
_s: PhantomData,
}
}
}
impl<A, B, S> fmt::Debug for And<A, B, S>
where
A: fmt::Debug,
B: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("And")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
// === impl Or ===
impl<A, B, S> Or<A, B, S>
where
A: Filter<S>,
B: Filter<S>,
{
/// Combines two [`Filter`]s so that spans and events are enabled if *either* filter
/// returns `true`.
///
/// # Examples
///
/// Enabling spans and events at the `INFO` level and above, and all spans
/// and events with a particular target:
///
/// ```ignore
/// use tracing_subscriber::{
/// filter::{filter_fn, LevelFilter, combinator::Or},
/// prelude::*,
/// };
///
/// // Enables spans and events with targets starting with `interesting_target`:
/// let target_filter = filter_fn(|meta| {
/// meta.target().starts_with("interesting_target")
/// });
///
/// // Enables spans and events with levels `INFO` and below:
/// let level_filter = LevelFilter::INFO;
///
/// // Combine the two filters together so that a span or event is enabled
/// // if it is at INFO or lower, or if it has a target starting with
/// // `interesting_target`.
/// let filter = Or::new(level_filter, target_filter);
///
/// tracing_subscriber::registry()
/// .with(tracing_subscriber::fmt::layer().with_filter(filter))
/// .init();
///
/// // This event will *not* be enabled:
/// tracing::debug!("an uninteresting event");
///
/// // This event *will* be enabled:
/// tracing::info!("an uninteresting INFO event");
///
/// // This event *will* be enabled:
/// tracing::info!(target: "interesting_target", "a very interesting event");
///
/// // This event *will* be enabled:
/// tracing::debug!(target: "interesting_target", "interesting debug event...");
/// ```
///
/// Enabling a higher level for a particular target by using `Or` in
/// conjunction with the [`And`] combinator:
///
/// ```ignore
/// use tracing_subscriber::{
/// filter::{filter_fn, LevelFilter, combinator},
/// prelude::*,
/// };
///
/// // This filter will enable spans and events with targets beginning with
/// // `my_crate`:
/// let my_crate = filter_fn(|meta| {
/// meta.target().starts_with("my_crate")
/// });
///
/// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter
/// // that will enable the `INFO` level and lower for spans and events with
/// // `my_crate` targets:
/// let filter = combinator::And::new(my_crate, LevelFilter::INFO);
///
/// // If a span or event *doesn't* have a target beginning with
/// // `my_crate`, enable it if it has the `WARN` level or lower:
/// // let filter = combinator::Or::new(filter, LevelFilter::WARN);
///
/// tracing_subscriber::registry()
/// .with(tracing_subscriber::fmt::layer().with_filter(filter))
/// .init();
/// ```
///
/// [`Filter`]: crate::layer::Filter
pub(crate) fn new(a: A, b: B) -> Self {
Self {
a,
b,
_s: PhantomData,
}
}
}
impl<A, B, S> Filter<S> for Or<A, B, S>
where
A: Filter<S>,
B: Filter<S>,
{
#[inline]
fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
self.a.enabled(meta, cx) || self.b.enabled(meta, cx)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
let a = self.a.callsite_enabled(meta);
let b = self.b.callsite_enabled(meta);
// If either filter will always enable the span or event, return `always`.
if a.is_always() || b.is_always() {
return Interest::always();
}
// Okay, if either filter will sometimes enable the span or event,
// return `sometimes`.
if a.is_sometimes() || b.is_sometimes() {
return Interest::sometimes();
}
debug_assert!(
a.is_never() && b.is_never(),
"if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})",
a,
b,
);
Interest::never()
}
fn max_level_hint(&self) -> Option<LevelFilter> {
// If either hint is `None`, return `None`. Otherwise, return the less restrictive.
Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?))
}
#[inline]
fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx)
}
#[inline]
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
self.a.on_new_span(attrs, id, ctx.clone());
self.b.on_new_span(attrs, id, ctx)
}
#[inline]
fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
self.a.on_record(id, values, ctx.clone());
self.b.on_record(id, values, ctx);
}
#[inline]
fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_enter(id, ctx.clone());
self.b.on_enter(id, ctx);
}
#[inline]
fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_exit(id, ctx.clone());
self.b.on_exit(id, ctx);
}
#[inline]
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
self.a.on_close(id.clone(), ctx.clone());
self.b.on_close(id, ctx);
}
}
impl<A, B, S> Clone for Or<A, B, S>
where
A: Clone,
B: Clone,
{
fn clone(&self) -> Self {
Self {
a: self.a.clone(),
b: self.b.clone(),
_s: PhantomData,
}
}
}
impl<A, B, S> fmt::Debug for Or<A, B, S>
where
A: fmt::Debug,
B: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Or")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
// === impl Not ===
impl<A, S> Not<A, S>
where
A: Filter<S>,
{
/// Inverts the result of a [`Filter`].
///
/// If the wrapped filter would enable a span or event, it will be disabled. If
/// it would disable a span or event, that span or event will be enabled.
///
/// This inverts the values returned by the [`enabled`] and [`callsite_enabled`]
/// methods on the wrapped filter; it does *not* invert [`event_enabled`], as
/// filters which do not implement filtering on event field values will return
/// the default `true` even for events that their [`enabled`] method disables.
///
/// Consider a normal filter defined as:
///
/// ```ignore (pseudo-code)
/// // for spans
/// match callsite_enabled() {
/// ALWAYS => on_span(),
/// SOMETIMES => if enabled() { on_span() },
/// NEVER => (),
/// }
/// // for events
/// match callsite_enabled() {
/// ALWAYS => on_event(),
/// SOMETIMES => if enabled() && event_enabled() { on_event() },
/// NEVER => (),
/// }
/// ```
///
/// and an inverted filter defined as:
///
/// ```ignore (pseudo-code)
/// // for spans
/// match callsite_enabled() {
/// ALWAYS => (),
/// SOMETIMES => if !enabled() { on_span() },
/// NEVER => on_span(),
/// }
/// // for events
/// match callsite_enabled() {
/// ALWAYS => (),
/// SOMETIMES => if !enabled() { on_event() },
/// NEVER => on_event(),
/// }
/// ```
///
/// A proper inversion would do `!(enabled() && event_enabled())` (or
/// `!enabled() || !event_enabled()`), but because of the implicit `&&`
/// relation between `enabled` and `event_enabled`, it is difficult to
/// short circuit and not call the wrapped `event_enabled`.
///
/// A combinator which remembers the result of `enabled` in order to call
/// `event_enabled` only when `enabled() == true` is possible, but requires
/// additional thread-local mutable state to support a very niche use case.
//
// Also, it'd mean the wrapped layer's `enabled()` always gets called and
// globally applied to events where it doesn't today, since we can't know
// what `event_enabled` will say until we have the event to call it with.
///
/// [`Filter`]: crate::layer::Filter
/// [`enabled`]: crate::layer::Filter::enabled
/// [`event_enabled`]: crate::layer::Filter::event_enabled
/// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled
pub(crate) fn new(a: A) -> Self {
Self { a, _s: PhantomData }
}
}
impl<A, S> Filter<S> for Not<A, S>
where
A: Filter<S>,
{
#[inline]
fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool {
!self.a.enabled(meta, cx)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
match self.a.callsite_enabled(meta) {
i if i.is_always() => Interest::never(),
i if i.is_never() => Interest::always(),
_ => Interest::sometimes(),
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
// TODO(eliza): figure this out???
None
}
#[inline]
fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool {
// Never disable based on event_enabled; we "disabled" it in `enabled`,
// so the `not` has already been applied and filtered this not out.
let _ = (event, cx);
true
}
#[inline]
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
self.a.on_new_span(attrs, id, ctx);
}
#[inline]
fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
self.a.on_record(id, values, ctx.clone());
}
#[inline]
fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_enter(id, ctx);
}
#[inline]
fn on_exit(&self, id: &Id, ctx: Context<'_, S>) {
self.a.on_exit(id, ctx);
}
#[inline]
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
self.a.on_close(id, ctx);
}
}
impl<A, S> Clone for Not<A, S>
where
A: Clone,
{
fn clone(&self) -> Self {
Self {
a: self.a.clone(),
_s: PhantomData,
}
}
}
impl<A, S> fmt::Debug for Not<A, S>
where
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Not").field(&self.a).finish()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
use tracing_core::{
subscriber::{Interest, Subscriber},
Metadata,
};
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use tracing_core::metadata::{LevelFilter, ParseLevelFilterError as ParseError};
// === impl LevelFilter ===
impl<S: Subscriber> crate::Layer<S> for LevelFilter {
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
if self >= metadata.level() {
Interest::always()
} else {
Interest::never()
}
}
fn enabled(&self, metadata: &Metadata<'_>, _: crate::layer::Context<'_, S>) -> bool {
self >= metadata.level()
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(*self)
}
}

View File

@@ -0,0 +1,66 @@
//! [`Layer`]s that control which spans and events are enabled by the wrapped
//! subscriber.
//!
//! This module contains a number of types that provide implementations of
//! various strategies for filtering which spans and events are enabled. For
//! details on filtering spans and events using [`Layer`]s, see the
//! [`layer` module's documentation].
//!
//! [`layer` module's documentation]: crate::layer#filtering-with-layers
//! [`Layer`]: crate::layer
mod filter_fn;
feature! {
#![all(feature = "env-filter", feature = "std")]
mod env;
pub use self::env::*;
}
feature! {
#![all(feature = "registry", feature = "std")]
mod layer_filters;
pub use self::layer_filters::*;
}
mod level;
pub use self::filter_fn::*;
pub use self::level::{LevelFilter, ParseError as LevelParseError};
#[cfg(not(all(feature = "registry", feature = "std")))]
pub(crate) use self::has_plf_stubs::*;
feature! {
#![any(feature = "std", feature = "alloc")]
pub mod targets;
pub use self::targets::Targets;
mod directive;
pub use self::directive::ParseError;
}
/// Stub implementations of the per-layer-filter detection functions for when the
/// `registry` feature is disabled.
#[cfg(not(all(feature = "registry", feature = "std")))]
mod has_plf_stubs {
pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool {
false
}
/// Does a type implementing `Subscriber` contain any per-layer filters?
pub(crate) fn subscriber_has_plf<S>(_: &S) -> bool
where
S: tracing_core::Subscriber,
{
false
}
/// Does a type implementing `Layer` contain any per-layer filters?
pub(crate) fn layer_has_plf<L, S>(_: &L) -> bool
where
L: crate::Layer<S>,
S: tracing_core::Subscriber,
{
false
}
}

View File

@@ -0,0 +1,834 @@
//! A [filter] that enables or disables spans and events based on their [target] and [level].
//!
//! See [`Targets`] for details.
//!
//! [target]: tracing_core::Metadata::target
//! [level]: tracing_core::Level
//! [filter]: crate::layer#filtering-with-layers
use crate::{
filter::{
directive::{DirectiveSet, ParseError, StaticDirective},
LevelFilter,
},
layer,
};
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::{
fmt,
iter::{Extend, FilterMap, FromIterator},
slice,
str::FromStr,
};
use tracing_core::{Interest, Level, Metadata, Subscriber};
/// A filter that enables or disables spans and events based on their [target]
/// and [level].
///
/// Targets are typically equal to the Rust module path of the code where the
/// span or event was recorded, although they may be overridden.
///
/// This type can be used for both [per-layer filtering][plf] (using its
/// [`Filter`] implementation) and [global filtering][global] (using its
/// [`Layer`] implementation).
///
/// See the [documentation on filtering with layers][filtering] for details.
///
/// # Filtering With `Targets`
///
/// A `Targets` filter consists of one or more [target] prefixes, paired with
/// [`LevelFilter`]s. If a span or event's [target] begins with one of those
/// prefixes, and its [level] is at or below the [`LevelFilter`] enabled for
/// that prefix, then the span or event will be enabled.
///
/// This is similar to the behavior implemented by the [`env_logger` crate] in
/// the `log` ecosystem.
///
/// The [`EnvFilter`] type also provided by this crate is very similar to `Targets`,
/// but is capable of a more sophisticated form of filtering where events may
/// also be enabled or disabled based on the span they are recorded in.
/// `Targets` can be thought of as a lighter-weight form of [`EnvFilter`] that
/// can be used instead when this dynamic filtering is not required.
///
/// # Examples
///
/// A `Targets` filter can be constructed by programmatically adding targets and
/// levels to enable:
///
/// ```
/// use tracing_subscriber::{filter, prelude::*};
/// use tracing_core::Level;
///
/// let filter = filter::Targets::new()
/// // Enable the `INFO` level for anything in `my_crate`
/// .with_target("my_crate", Level::INFO)
/// // Enable the `DEBUG` level for a specific module.
/// .with_target("my_crate::interesting_module", Level::DEBUG);
///
/// // Build a new subscriber with the `fmt` layer using the `Targets`
/// // filter we constructed above.
/// tracing_subscriber::registry()
/// .with(tracing_subscriber::fmt::layer())
/// .with(filter)
/// .init();
/// ```
///
/// [`LevelFilter::OFF`] can be used to disable a particular target:
/// ```
/// use tracing_subscriber::filter::{Targets, LevelFilter};
/// use tracing_core::Level;
///
/// let filter = Targets::new()
/// .with_target("my_crate", Level::INFO)
/// // Disable all traces from `annoying_module`.
/// .with_target("my_crate::annoying_module", LevelFilter::OFF);
/// # drop(filter);
/// ```
///
/// Alternatively, `Targets` implements [`std::str::FromStr`], allowing it to be
/// parsed from a comma-delimited list of `target=level` pairs. For example:
///
/// ```rust
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tracing_subscriber::filter;
/// use tracing_core::Level;
///
/// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug"
/// .parse::<filter::Targets>()?;
///
/// // The parsed filter is identical to a filter constructed using `with_target`:
/// assert_eq!(
/// filter,
/// filter::Targets::new()
/// .with_target("my_crate", Level::INFO)
/// .with_target("my_crate::interesting_module", Level::TRACE)
/// .with_target("other_crate", Level::DEBUG)
/// );
/// # Ok(()) }
/// ```
///
/// This is particularly useful when the list of enabled targets is configurable
/// by the user at runtime.
///
/// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a
/// [global filter][global]:
///
/// ```rust
/// use tracing_subscriber::{
/// fmt,
/// filter::{Targets, LevelFilter},
/// prelude::*,
/// };
/// use tracing_core::Level;
/// use std::{sync::Arc, fs::File};
/// # fn docs() -> Result<(), Box<dyn std::error::Error>> {
///
/// // A layer that logs events to stdout using the human-readable "pretty"
/// // format.
/// let stdout_log = fmt::layer().pretty();
///
/// // A layer that logs events to a file, using the JSON format.
/// let file = File::create("debug_log.json")?;
/// let debug_log = fmt::layer()
/// .with_writer(Arc::new(file))
/// .json();
///
/// tracing_subscriber::registry()
/// // Only log INFO and above to stdout, unless the span or event
/// // has the `my_crate::cool_module` target prefix.
/// .with(stdout_log
/// .with_filter(
/// Targets::default()
/// .with_target("my_crate::cool_module", Level::DEBUG)
/// .with_default(Level::INFO)
/// )
/// )
/// // Log everything enabled by the global filter to `debug_log.json`.
/// .with(debug_log)
/// // Configure a global filter for the whole subscriber stack. This will
/// // control what spans and events are recorded by both the `debug_log`
/// // and the `stdout_log` layers, and `stdout_log` will *additionally* be
/// // filtered by its per-layer filter.
/// .with(
/// Targets::default()
/// .with_target("my_crate", Level::TRACE)
/// .with_target("other_crate", Level::INFO)
/// .with_target("other_crate::annoying_module", LevelFilter::OFF)
/// .with_target("third_crate", Level::DEBUG)
/// ).init();
/// # Ok(()) }
///```
///
/// [target]: tracing_core::Metadata::target
/// [level]: tracing_core::Level
/// [`Filter`]: crate::layer::Filter
/// [`Layer`]: crate::layer::Layer
/// [plf]: crate::layer#per-layer-filtering
/// [global]: crate::layer#global-filtering
/// [filtering]: crate::layer#filtering-with-layers
/// [`env_logger` crate]: https://docs.rs/env_logger/0.9.0/env_logger/index.html#enabling-logging
/// [`EnvFilter`]: crate::filter::EnvFilter
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Targets(DirectiveSet<StaticDirective>);
impl Targets {
/// Returns a new `Targets` filter.
///
/// This filter will enable no targets. Call [`with_target`] or [`with_targets`]
/// to add enabled targets, and [`with_default`] to change the default level
/// enabled for spans and events that didn't match any of the provided targets.
///
/// [`with_target`]: Targets::with_target
/// [`with_targets`]: Targets::with_targets
/// [`with_default`]: Targets::with_default
pub fn new() -> Self {
Self::default()
}
/// Enables spans and events with [target]s starting with the provided target
/// prefix if they are at or below the provided [`LevelFilter`].
///
/// # Examples
///
/// ```
/// use tracing_subscriber::filter;
/// use tracing_core::Level;
///
/// let filter = filter::Targets::new()
/// // Enable the `INFO` level for anything in `my_crate`
/// .with_target("my_crate", Level::INFO)
/// // Enable the `DEBUG` level for a specific module.
/// .with_target("my_crate::interesting_module", Level::DEBUG);
/// # drop(filter);
/// ```
///
/// [`LevelFilter::OFF`] can be used to disable a particular target:
/// ```
/// use tracing_subscriber::filter::{Targets, LevelFilter};
/// use tracing_core::Level;
///
/// let filter = Targets::new()
/// .with_target("my_crate", Level::INFO)
/// // Disable all traces from `annoying_module`.
/// .with_target("my_crate::interesting_module", LevelFilter::OFF);
/// # drop(filter);
/// ```
///
/// [target]: tracing_core::Metadata::target
pub fn with_target(mut self, target: impl Into<String>, level: impl Into<LevelFilter>) -> Self {
self.0.add(StaticDirective::new(
Some(target.into()),
Default::default(),
level.into(),
));
self
}
/// Adds [target]s from an iterator of [target]-[`LevelFilter`] pairs to this filter.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::filter;
/// use tracing_core::Level;
///
/// let filter = filter::Targets::new()
/// .with_targets(vec![
/// ("my_crate", Level::INFO),
/// ("my_crate::some_module", Level::DEBUG),
/// ("my_crate::other_module::cool_stuff", Level::TRACE),
/// ("other_crate", Level::WARN)
/// ]);
/// # drop(filter);
/// ```
///
/// [`LevelFilter::OFF`] can be used to disable a particular target:
/// ```
/// use tracing_subscriber::filter::{Targets, LevelFilter};
/// use tracing_core::Level;
///
/// let filter = Targets::new()
/// .with_target("my_crate", Level::INFO)
/// // Disable all traces from `annoying_module`.
/// .with_target("my_crate::interesting_module", LevelFilter::OFF);
/// # drop(filter);
/// ```
///
/// [target]: tracing_core::Metadata::target
pub fn with_targets<T, L>(mut self, targets: impl IntoIterator<Item = (T, L)>) -> Self
where
String: From<T>,
LevelFilter: From<L>,
{
self.extend(targets);
self
}
/// Sets the default level to enable for spans and events whose targets did
/// not match any of the configured prefixes.
///
/// By default, this is [`LevelFilter::OFF`]. This means that spans and
/// events will only be enabled if they match one of the configured target
/// prefixes. If this is changed to a different [`LevelFilter`], spans and
/// events with targets that did not match any of the configured prefixes
/// will be enabled if their level is at or below the provided level.
pub fn with_default(mut self, level: impl Into<LevelFilter>) -> Self {
self.0
.add(StaticDirective::new(None, Default::default(), level.into()));
self
}
/// Returns the default level for this filter, if one is set.
///
/// The default level is used to filter any spans or events with targets
/// that do not match any of the configured set of prefixes.
///
/// The default level can be set for a filter either by using
/// [`with_default`](Self::with_default) or when parsing from a filter string that includes a
/// level without a target (e.g. `"trace"`).
///
/// # Examples
///
/// ```
/// use tracing_subscriber::filter::{LevelFilter, Targets};
///
/// let filter = Targets::new().with_default(LevelFilter::INFO);
/// assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
///
/// let filter: Targets = "info".parse().unwrap();
/// assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
/// ```
///
/// The default level is `None` if no default is set:
///
/// ```
/// use tracing_subscriber::filter::Targets;
///
/// let filter = Targets::new();
/// assert_eq!(filter.default_level(), None);
///
/// let filter: Targets = "my_crate=info".parse().unwrap();
/// assert_eq!(filter.default_level(), None);
/// ```
///
/// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is
/// used, but it could also be set explicitly which may be useful to distinguish (such as when
/// merging multiple `Targets`).
///
/// ```
/// use tracing_subscriber::filter::{LevelFilter, Targets};
///
/// let filter = Targets::new().with_default(LevelFilter::OFF);
/// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
///
/// let filter: Targets = "off".parse().unwrap();
/// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
/// ```
pub fn default_level(&self) -> Option<LevelFilter> {
self.0.directives().find_map(|d| {
if d.target.is_none() {
Some(d.level)
} else {
None
}
})
}
/// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter.
///
/// The order of iteration is undefined.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::filter::{Targets, LevelFilter};
/// use tracing_core::Level;
///
/// let filter = Targets::new()
/// .with_target("my_crate", Level::INFO)
/// .with_target("my_crate::interesting_module", Level::DEBUG);
///
/// let mut targets: Vec<_> = filter.iter().collect();
/// targets.sort();
///
/// assert_eq!(targets, vec![
/// ("my_crate", LevelFilter::INFO),
/// ("my_crate::interesting_module", LevelFilter::DEBUG),
/// ]);
/// ```
///
/// [target]: tracing_core::Metadata::target
pub fn iter(&self) -> Iter<'_> {
self.into_iter()
}
#[inline]
fn interested(&self, metadata: &'static Metadata<'static>) -> Interest {
if self.0.enabled(metadata) {
Interest::always()
} else {
Interest::never()
}
}
/// Returns whether a [target]-[`Level`] pair would be enabled
/// by this `Targets`.
///
/// This method can be used with [`module_path!`] from `std` as the target
/// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`]
/// macros.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::filter::{Targets, LevelFilter};
/// use tracing_core::Level;
///
/// let filter = Targets::new()
/// .with_target("my_crate", Level::INFO)
/// .with_target("my_crate::interesting_module", Level::DEBUG);
///
/// assert!(filter.would_enable("my_crate", &Level::INFO));
/// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE));
/// ```
///
/// [target]: tracing_core::Metadata::target
/// [`module_path!`]: std::module_path!
pub fn would_enable(&self, target: &str, level: &Level) -> bool {
// "Correct" to call because `Targets` only produces `StaticDirective`'s with NO
// fields
self.0.target_enabled(target, level)
}
}
impl<T, L> Extend<(T, L)> for Targets
where
T: Into<String>,
L: Into<LevelFilter>,
{
fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) {
let iter = iter.into_iter().map(|(target, level)| {
StaticDirective::new(Some(target.into()), Default::default(), level.into())
});
self.0.extend(iter);
}
}
impl<T, L> FromIterator<(T, L)> for Targets
where
T: Into<String>,
L: Into<LevelFilter>,
{
fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self {
let mut this = Self::default();
this.extend(iter);
this
}
}
impl FromStr for Targets {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.split(',')
.map(StaticDirective::from_str)
.collect::<Result<_, _>>()
.map(Self)
}
}
impl<S> layer::Layer<S> for Targets
where
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool {
self.0.enabled(metadata)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.interested(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(self.0.max_level)
}
}
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
impl<S> layer::Filter<S> for Targets {
fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool {
self.0.enabled(metadata)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.interested(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(self.0.max_level)
}
}
impl IntoIterator for Targets {
type Item = (String, LevelFilter);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
impl<'a> IntoIterator for &'a Targets {
type Item = (&'a str, LevelFilter);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
Iter::new(self)
}
}
impl fmt::Display for Targets {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut directives = self.0.directives();
if let Some(directive) = directives.next() {
write!(f, "{}", directive)?;
for directive in directives {
write!(f, ",{}", directive)?;
}
}
Ok(())
}
}
/// An owning iterator over the [target]-[level] pairs of a `Targets` filter.
///
/// This struct is created by the `IntoIterator` trait implementation of [`Targets`].
///
/// # Examples
///
/// Merge the targets from one `Targets` with another:
///
/// ```
/// use tracing_subscriber::filter::Targets;
/// use tracing_core::Level;
///
/// let mut filter = Targets::new().with_target("my_crate", Level::INFO);
/// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG);
///
/// filter.extend(overrides);
/// # drop(filter);
/// ```
///
/// [target]: tracing_core::Metadata::target
/// [level]: tracing_core::Level
#[derive(Debug)]
pub struct IntoIter(
#[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing
FilterMap<
<DirectiveSet<StaticDirective> as IntoIterator>::IntoIter,
fn(StaticDirective) -> Option<(String, LevelFilter)>,
>,
);
impl IntoIter {
fn new(targets: Targets) -> Self {
Self(targets.0.into_iter().filter_map(|directive| {
let level = directive.level;
directive.target.map(|target| (target, level))
}))
}
}
impl Iterator for IntoIter {
type Item = (String, LevelFilter);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
/// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter.
///
/// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator`
/// implementation for `&Targets`.
///
/// [target]: tracing_core::Metadata::target
/// [level]: tracing_core::Level
/// [`iter`]: Targets::iter
#[derive(Debug)]
pub struct Iter<'a>(
FilterMap<
slice::Iter<'a, StaticDirective>,
fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>,
>,
);
impl<'a> Iter<'a> {
fn new(targets: &'a Targets) -> Self {
Self(targets.0.iter().filter_map(|directive| {
directive
.target
.as_deref()
.map(|target| (target, directive.level))
}))
}
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a str, LevelFilter);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(test)]
mod tests {
use super::*;
feature! {
#![not(feature = "std")]
use alloc::{vec, vec::Vec, string::ToString};
// `dbg!` is only available with `libstd`; just nop it out when testing
// with alloc only.
macro_rules! dbg {
($x:expr) => { $x }
}
}
fn expect_parse(s: &str) -> Targets {
match dbg!(s).parse::<Targets>() {
Err(e) => panic!("string {:?} did not parse successfully: {}", s, e),
Ok(e) => e,
}
}
fn expect_parse_ralith(s: &str) {
let dirs = expect_parse(s).0.into_vec();
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("server".to_string()));
assert_eq!(dirs[0].level, LevelFilter::DEBUG);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("common".to_string()));
assert_eq!(dirs[1].level, LevelFilter::INFO);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
}
fn expect_parse_level_directives(s: &str) {
let dirs = expect_parse(s).0.into_vec();
assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::OFF);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string()));
assert_eq!(dirs[1].level, LevelFilter::INFO);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
assert_eq!(dirs[2].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[2].level, LevelFilter::WARN);
assert_eq!(dirs[2].field_names, Vec::<String>::new());
assert_eq!(dirs[3].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[3].level, LevelFilter::ERROR);
assert_eq!(dirs[3].field_names, Vec::<String>::new());
assert_eq!(dirs[4].target, Some("crate3".to_string()));
assert_eq!(dirs[4].level, LevelFilter::TRACE);
assert_eq!(dirs[4].field_names, Vec::<String>::new());
assert_eq!(dirs[5].target, Some("crate2".to_string()));
assert_eq!(dirs[5].level, LevelFilter::DEBUG);
assert_eq!(dirs[5].field_names, Vec::<String>::new());
}
#[test]
fn parse_ralith() {
expect_parse_ralith("common=info,server=debug");
}
#[test]
fn parse_ralith_uc() {
expect_parse_ralith("common=INFO,server=DEBUG");
}
#[test]
fn parse_ralith_mixed() {
expect_parse("common=iNfo,server=dEbUg");
}
#[test]
fn expect_parse_valid() {
let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.0
.into_vec();
assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::TRACE);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[1].level, LevelFilter::ERROR);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
assert_eq!(dirs[2].target, Some("crate3".to_string()));
assert_eq!(dirs[2].level, LevelFilter::OFF);
assert_eq!(dirs[2].field_names, Vec::<String>::new());
assert_eq!(dirs[3].target, Some("crate2".to_string()));
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
assert_eq!(dirs[3].field_names, Vec::<String>::new());
}
#[test]
fn parse_level_directives() {
expect_parse_level_directives(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
)
}
#[test]
fn parse_uppercase_level_directives() {
expect_parse_level_directives(
"crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
)
}
#[test]
fn parse_numeric_level_directives() {
expect_parse_level_directives(
"crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
crate3=5,crate3::mod2::mod1=0",
)
}
#[test]
fn targets_iter() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::WARN);
let mut targets: Vec<_> = filter.iter().collect();
targets.sort();
assert_eq!(
targets,
vec![
("crate1::mod1", LevelFilter::ERROR),
("crate1::mod2", LevelFilter::TRACE),
("crate2", LevelFilter::DEBUG),
("crate3", LevelFilter::OFF),
]
);
}
#[test]
fn targets_into_iter() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::WARN);
let mut targets: Vec<_> = filter.into_iter().collect();
targets.sort();
assert_eq!(
targets,
vec![
("crate1::mod1".to_string(), LevelFilter::ERROR),
("crate1::mod2".to_string(), LevelFilter::TRACE),
("crate2".to_string(), LevelFilter::DEBUG),
("crate3".to_string(), LevelFilter::OFF),
]
);
}
#[test]
fn targets_default_level() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
assert_eq!(filter.default_level(), None);
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::OFF);
assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::OFF)
.with_default(LevelFilter::INFO);
assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
}
#[test]
// `println!` is only available with `libstd`.
#[cfg(feature = "std")]
fn size_of_filters() {
fn print_sz(s: &str) {
let filter = s.parse::<Targets>().expect("filter should parse");
println!(
"size_of_val({:?})\n -> {}B",
s,
std::mem::size_of_val(&filter)
);
}
print_sz("info");
print_sz("foo=debug");
print_sz(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
);
}
/// Test that the `fmt::Display` implementation for `Targets` emits a string
/// that can itself be parsed as a `Targets`, and that the parsed `Targets`
/// is equivalent to the original one.
#[test]
fn display_roundtrips() {
fn test_roundtrip(s: &str) {
let filter = expect_parse(s);
// we don't assert that the display output is equivalent to the
// original parsed filter string, because the `Display` impl always
// uses lowercase level names and doesn't use the
// target-without-level shorthand syntax. while they may not be
// textually equivalent, though, they should still *parse* to the
// same filter.
let formatted = filter.to_string();
let filter2 = match dbg!(&formatted).parse::<Targets>() {
Ok(filter) => filter,
Err(e) => panic!(
"failed to parse formatted filter string {:?}: {}",
formatted, e
),
};
assert_eq!(filter, filter2);
}
test_roundtrip("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
test_roundtrip(
"crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
);
test_roundtrip(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
);
test_roundtrip("crate1::mod1,crate1::mod2,info");
test_roundtrip("crate1");
test_roundtrip("info");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
//! ANSI escape sequence sanitization to prevent terminal injection attacks.
use std::fmt::{self, Write};
/// A wrapper that implements `fmt::Debug` and `fmt::Display` and escapes ANSI sequences on-the-fly.
/// This avoids creating intermediate strings while providing security against terminal injection.
pub(super) struct Escape<T>(pub(super) T);
/// Helper struct that escapes ANSI sequences as characters are written
struct EscapingWriter<'a, 'b> {
inner: &'a mut fmt::Formatter<'b>,
}
impl<'a, 'b> fmt::Write for EscapingWriter<'a, 'b> {
fn write_str(&mut self, s: &str) -> fmt::Result {
// Stream the string character by character, escaping ANSI and C1 control sequences
for ch in s.chars() {
match ch {
// C0 control characters that can be used in terminal escape sequences
'\x1b' => self.inner.write_str("\\x1b")?, // ESC
'\x07' => self.inner.write_str("\\x07")?, // BEL
'\x08' => self.inner.write_str("\\x08")?, // BS
'\x0c' => self.inner.write_str("\\x0c")?, // FF
'\x7f' => self.inner.write_str("\\x7f")?, // DEL
// C1 control characters (\x80-\x9f) - 8-bit control codes
// These can be used as alternative escape sequences in some terminals
ch if ch as u32 >= 0x80 && ch as u32 <= 0x9f => {
write!(self.inner, "\\u{{{:x}}}", ch as u32)?
},
_ => self.inner.write_char(ch)?,
}
}
Ok(())
}
}
impl<T: fmt::Debug> fmt::Debug for Escape<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut escaping_writer = EscapingWriter { inner: f };
write!(escaping_writer, "{:?}", self.0)
}
}
impl<T: fmt::Display> fmt::Display for Escape<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut escaping_writer = EscapingWriter { inner: f };
write!(escaping_writer, "{}", self.0)
}
}

View File

@@ -0,0 +1,896 @@
use super::{Format, FormatEvent, FormatFields, FormatTime, Writer};
use crate::{
field::{RecordFields, VisitOutput},
fmt::{
fmt_layer::{FmtContext, FormattedFields},
writer::WriteAdaptor,
},
registry::LookupSpan,
};
use serde::ser::{SerializeMap, Serializer as _};
use serde_json::Serializer;
use std::{
collections::BTreeMap,
fmt::{self, Write},
};
use tracing_core::{
field::{self, Field},
span::Record,
Event, Subscriber,
};
use tracing_serde::AsSerde;
#[cfg(feature = "tracing-log")]
use tracing_log::NormalizeEvent;
/// Marker for [`Format`] that indicates that the newline-delimited JSON log
/// format should be used.
///
/// This formatter is intended for production use with systems where structured
/// logs are consumed as JSON by analysis and viewing tools. The JSON output is
/// not optimized for human readability; instead, it should be pretty-printed
/// using external JSON tools such as `jq`, or using a JSON log viewer.
///
/// # Example Output
///
/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-json
/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-json`
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821315Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;preparing to shave yaks&quot;,&quot;number_of_yaks&quot;:3},&quot;target&quot;:&quot;fmt_json&quot;}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821422Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;shaving yaks&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821495Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:1,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821546Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:1,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821598Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:1,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821637Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:1},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821684Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:2,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821727Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaved successfully&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:2,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821773Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:2,&quot;shaved&quot;:true},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821806Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821909Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;hello! I&apos;m gonna shave a yak&quot;,&quot;excitement&quot;:&quot;yay!&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:3,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.821956Z&quot;,&quot;level&quot;:&quot;WARN&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;could not locate yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;},{&quot;yak&quot;:3,&quot;name&quot;:&quot;shave&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822006Z&quot;,&quot;level&quot;:&quot;DEBUG&quot;,&quot;fields&quot;:{&quot;yak&quot;:3,&quot;shaved&quot;:false},&quot;target&quot;:&quot;yak_events&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822041Z&quot;,&quot;level&quot;:&quot;ERROR&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;failed to shave yak&quot;,&quot;yak&quot;:3,&quot;error&quot;:&quot;missing yak&quot;},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822079Z&quot;,&quot;level&quot;:&quot;TRACE&quot;,&quot;fields&quot;:{&quot;yaks_shaved&quot;:2},&quot;target&quot;:&quot;fmt_json::yak_shave&quot;,&quot;spans&quot;:[{&quot;yaks&quot;:3,&quot;name&quot;:&quot;shaving_yaks&quot;}]}
/// {&quot;timestamp&quot;:&quot;2022-02-15T18:47:10.822117Z&quot;,&quot;level&quot;:&quot;INFO&quot;,&quot;fields&quot;:{&quot;message&quot;:&quot;yak shaving completed&quot;,&quot;all_yaks_shaved&quot;:false},&quot;target&quot;:&quot;fmt_json&quot;}
/// </pre>
///
/// # Options
///
/// This formatter exposes additional options to configure the structure of the
/// output JSON objects:
///
/// - [`Json::flatten_event`] can be used to enable flattening event fields into
/// the root
/// - [`Json::with_current_span`] can be used to control logging of the current
/// span
/// - [`Json::with_span_list`] can be used to control logging of the span list
/// object.
///
/// By default, event fields are not flattened, and both current span and span
/// list are logged.
///
/// # Valuable Support
///
/// Experimental support is available for using the [`valuable`] crate to record
/// user-defined values as structured JSON. When the ["valuable" unstable
/// feature][unstable] is enabled, types implementing [`valuable::Valuable`] will
/// be recorded as structured JSON, rather than
/// using their [`std::fmt::Debug`] implementations.
///
/// **Note**: This is an experimental feature. [Unstable features][unstable]
/// must be enabled in order to use `valuable` support.
///
/// [`Json::flatten_event`]: Json::flatten_event()
/// [`Json::with_current_span`]: Json::with_current_span()
/// [`Json::with_span_list`]: Json::with_span_list()
/// [`valuable`]: https://crates.io/crates/valuable
/// [unstable]: crate#unstable-features
/// [`valuable::Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Json {
pub(crate) flatten_event: bool,
pub(crate) display_current_span: bool,
pub(crate) display_span_list: bool,
}
impl Json {
/// If set to `true` event metadata will be flattened into the root object.
pub fn flatten_event(&mut self, flatten_event: bool) {
self.flatten_event = flatten_event;
}
/// If set to `false`, formatted events won't contain a field for the current span.
pub fn with_current_span(&mut self, display_current_span: bool) {
self.display_current_span = display_current_span;
}
/// If set to `false`, formatted events won't contain a list of all currently
/// entered spans. Spans are logged in a list from root to leaf.
pub fn with_span_list(&mut self, display_span_list: bool) {
self.display_span_list = display_span_list;
}
}
struct SerializableContext<'a, 'b, Span, N>(
&'b crate::layer::Context<'a, Span>,
std::marker::PhantomData<N>,
)
where
Span: Subscriber + for<'lookup> crate::registry::LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static;
impl<Span, N> serde::ser::Serialize for SerializableContext<'_, '_, Span, N>
where
Span: Subscriber + for<'lookup> crate::registry::LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static,
{
fn serialize<Ser>(&self, serializer_o: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: serde::ser::Serializer,
{
use serde::ser::SerializeSeq;
let mut serializer = serializer_o.serialize_seq(None)?;
if let Some(leaf_span) = self.0.lookup_current() {
for span in leaf_span.scope().from_root() {
serializer.serialize_element(&SerializableSpan(&span, self.1))?;
}
}
serializer.end()
}
}
struct SerializableSpan<'a, 'b, Span, N>(
&'b crate::registry::SpanRef<'a, Span>,
std::marker::PhantomData<N>,
)
where
Span: for<'lookup> crate::registry::LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static;
impl<Span, N> serde::ser::Serialize for SerializableSpan<'_, '_, Span, N>
where
Span: for<'lookup> crate::registry::LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static,
{
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: serde::ser::Serializer,
{
let mut serializer = serializer.serialize_map(None)?;
let ext = self.0.extensions();
let data = ext
.get::<FormattedFields<N>>()
.expect("Unable to find FormattedFields in extensions; this is a bug");
// TODO: let's _not_ do this, but this resolves
// https://github.com/tokio-rs/tracing/issues/391.
// We should probably rework this to use a `serde_json::Value` or something
// similar in a JSON-specific layer, but I'd (david)
// rather have a uglier fix now rather than shipping broken JSON.
match serde_json::from_str::<serde_json::Value>(data) {
Ok(serde_json::Value::Object(fields)) => {
for field in fields {
serializer.serialize_entry(&field.0, &field.1)?;
}
}
// We have fields for this span which are valid JSON but not an object.
// This is probably a bug, so panic if we're in debug mode
Ok(_) if cfg!(debug_assertions) => panic!(
"span '{}' had malformed fields! this is a bug.\n error: invalid JSON object\n fields: {:?}",
self.0.metadata().name(),
data
),
// If we *aren't* in debug mode, it's probably best not to
// crash the program, let's log the field found but also an
// message saying it's type is invalid
Ok(value) => {
serializer.serialize_entry("field", &value)?;
serializer.serialize_entry("field_error", "field was no a valid object")?
}
// We have previously recorded fields for this span
// should be valid JSON. However, they appear to *not*
// be valid JSON. This is almost certainly a bug, so
// panic if we're in debug mode
Err(e) if cfg!(debug_assertions) => panic!(
"span '{}' had malformed fields! this is a bug.\n error: {}\n fields: {:?}",
self.0.metadata().name(),
e,
data
),
// If we *aren't* in debug mode, it's probably best not
// crash the program, but let's at least make sure it's clear
// that the fields are not supposed to be missing.
Err(e) => serializer.serialize_entry("field_error", &format!("{}", e))?,
};
serializer.serialize_entry("name", self.0.metadata().name())?;
serializer.end()
}
}
impl<S, N, T> FormatEvent<S, N> for Format<Json, T>
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
N: for<'writer> FormatFields<'writer> + 'static,
T: FormatTime,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let mut timestamp = String::new();
self.timer.format_time(&mut Writer::new(&mut timestamp))?;
#[cfg(feature = "tracing-log")]
let normalized_meta = event.normalized_metadata();
#[cfg(feature = "tracing-log")]
let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
#[cfg(not(feature = "tracing-log"))]
let meta = event.metadata();
let mut visit = || {
let mut serializer = Serializer::new(WriteAdaptor::new(&mut writer));
let mut serializer = serializer.serialize_map(None)?;
if self.display_timestamp {
serializer.serialize_entry("timestamp", &timestamp)?;
}
if self.display_level {
serializer.serialize_entry("level", &meta.level().as_serde())?;
}
let format_field_marker: std::marker::PhantomData<N> = std::marker::PhantomData;
let current_span = if self.format.display_current_span || self.format.display_span_list
{
event
.parent()
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current())
} else {
None
};
if self.format.flatten_event {
let mut visitor = tracing_serde::SerdeMapVisitor::new(serializer);
event.record(&mut visitor);
serializer = visitor.take_serializer()?;
} else {
use tracing_serde::fields::AsMap;
serializer.serialize_entry("fields", &event.field_map())?;
};
if self.display_target {
serializer.serialize_entry("target", meta.target())?;
}
if self.display_filename {
if let Some(filename) = meta.file() {
serializer.serialize_entry("filename", filename)?;
}
}
if self.display_line_number {
if let Some(line_number) = meta.line() {
serializer.serialize_entry("line_number", &line_number)?;
}
}
if self.format.display_current_span {
if let Some(ref span) = current_span {
serializer
.serialize_entry("span", &SerializableSpan(span, format_field_marker))
.unwrap_or(());
}
}
if self.format.display_span_list && current_span.is_some() {
serializer.serialize_entry(
"spans",
&SerializableContext(&ctx.ctx, format_field_marker),
)?;
}
if self.display_thread_name {
let current_thread = std::thread::current();
match current_thread.name() {
Some(name) => {
serializer.serialize_entry("threadName", name)?;
}
// fall-back to thread id when name is absent and ids are not enabled
None if !self.display_thread_id => {
serializer
.serialize_entry("threadName", &format!("{:?}", current_thread.id()))?;
}
_ => {}
}
}
if self.display_thread_id {
serializer
.serialize_entry("threadId", &format!("{:?}", std::thread::current().id()))?;
}
serializer.end()
};
visit().map_err(|_| fmt::Error)?;
writeln!(writer)
}
}
impl Default for Json {
fn default() -> Json {
Json {
flatten_event: false,
display_current_span: true,
display_span_list: true,
}
}
}
/// The JSON [`FormatFields`] implementation.
///
#[derive(Debug)]
pub struct JsonFields {
// reserve the ability to add fields to this without causing a breaking
// change in the future.
_private: (),
}
impl JsonFields {
/// Returns a new JSON [`FormatFields`] implementation.
///
pub fn new() -> Self {
Self { _private: () }
}
}
impl Default for JsonFields {
fn default() -> Self {
Self::new()
}
}
impl<'a> FormatFields<'a> for JsonFields {
/// Format the provided `fields` to the provided `writer`, returning a result.
fn format_fields<R: RecordFields>(&self, mut writer: Writer<'_>, fields: R) -> fmt::Result {
let mut v = JsonVisitor::new(&mut writer);
fields.record(&mut v);
v.finish()
}
/// Record additional field(s) on an existing span.
///
/// By default, this appends a space to the current set of fields if it is
/// non-empty, and then calls `self.format_fields`. If different behavior is
/// required, the default implementation of this method can be overridden.
fn add_fields(
&self,
current: &'a mut FormattedFields<Self>,
fields: &Record<'_>,
) -> fmt::Result {
if current.is_empty() {
// If there are no previously recorded fields, we can just reuse the
// existing string.
let mut writer = current.as_writer();
let mut v = JsonVisitor::new(&mut writer);
fields.record(&mut v);
v.finish()?;
return Ok(());
}
// If fields were previously recorded on this span, we need to parse
// the current set of fields as JSON, add the new fields, and
// re-serialize them. Otherwise, if we just appended the new fields
// to a previously serialized JSON object, we would end up with
// malformed JSON.
//
// XXX(eliza): this is far from efficient, but unfortunately, it is
// necessary as long as the JSON formatter is implemented on top of
// an interface that stores all formatted fields as strings.
//
// We should consider reimplementing the JSON formatter as a
// separate layer, rather than a formatter for the `fmt` layer —
// then, we could store fields as JSON values, and add to them
// without having to parse and re-serialize.
let mut new = String::new();
let map: BTreeMap<&'_ str, serde_json::Value> =
serde_json::from_str(current).map_err(|_| fmt::Error)?;
let mut v = JsonVisitor::new(&mut new);
v.values = map;
fields.record(&mut v);
v.finish()?;
current.fields = new;
Ok(())
}
}
/// The [visitor] produced by [`JsonFields`]'s [`MakeVisitor`] implementation.
///
/// [visitor]: crate::field::Visit
/// [`MakeVisitor`]: crate::field::MakeVisitor
pub struct JsonVisitor<'a> {
values: BTreeMap<&'a str, serde_json::Value>,
writer: &'a mut dyn Write,
}
impl fmt::Debug for JsonVisitor<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("JsonVisitor {{ values: {:?} }}", self.values))
}
}
impl<'a> JsonVisitor<'a> {
/// Returns a new default visitor that formats to the provided `writer`.
///
/// # Arguments
/// - `writer`: the writer to format to.
/// - `is_empty`: whether or not any fields have been previously written to
/// that writer.
pub fn new(writer: &'a mut dyn Write) -> Self {
Self {
values: BTreeMap::new(),
writer,
}
}
}
impl crate::field::VisitFmt for JsonVisitor<'_> {
fn writer(&mut self) -> &mut dyn fmt::Write {
self.writer
}
}
impl crate::field::VisitOutput<fmt::Result> for JsonVisitor<'_> {
fn finish(self) -> fmt::Result {
let inner = || {
let mut serializer = Serializer::new(WriteAdaptor::new(self.writer));
let mut ser_map = serializer.serialize_map(None)?;
for (k, v) in self.values {
ser_map.serialize_entry(k, &v)?;
}
ser_map.end()
};
if inner().is_err() {
Err(fmt::Error)
} else {
Ok(())
}
}
}
impl field::Visit for JsonVisitor<'_> {
#[cfg(all(tracing_unstable, feature = "valuable"))]
fn record_value(&mut self, field: &Field, value: valuable_crate::Value<'_>) {
let value = match serde_json::to_value(valuable_serde::Serializable::new(value)) {
Ok(value) => value,
Err(_e) => {
#[cfg(debug_assertions)]
unreachable!(
"`valuable::Valuable` implementations should always serialize \
successfully, but an error occurred: {}",
_e,
);
#[cfg(not(debug_assertions))]
return;
}
};
self.values.insert(field.name(), value);
}
/// Visit a double precision floating point value.
fn record_f64(&mut self, field: &Field, value: f64) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
/// Visit a signed 64-bit integer value.
fn record_i64(&mut self, field: &Field, value: i64) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
/// Visit an unsigned 64-bit integer value.
fn record_u64(&mut self, field: &Field, value: u64) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
/// Visit a boolean value.
fn record_bool(&mut self, field: &Field, value: bool) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
/// Visit a string value.
fn record_str(&mut self, field: &Field, value: &str) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
fn record_bytes(&mut self, field: &Field, value: &[u8]) {
self.values
.insert(field.name(), serde_json::Value::from(value));
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
match field.name() {
// Skip fields that are actually log metadata that have already been handled
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => (),
name if name.starts_with("r#") => {
self.values
.insert(&name[2..], serde_json::Value::from(format!("{:?}", value)));
}
name => {
self.values
.insert(name, serde_json::Value::from(format!("{:?}", value)));
}
};
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::fmt::{format::FmtSpan, test::MockMakeWriter, time::FormatTime, SubscriberBuilder};
use tracing::{self, subscriber::with_default};
use std::fmt;
use std::path::Path;
struct MockTime;
impl FormatTime for MockTime {
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
write!(w, "fake time")
}
}
fn subscriber() -> SubscriberBuilder<JsonFields, Format<Json>> {
SubscriberBuilder::default().json()
}
#[test]
fn json() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_span_list(true);
test_json(expected, subscriber, || {
let span = tracing::span!(
tracing::Level::INFO,
"json_span",
answer = 42,
number = 3,
slice = &b"abc"[..]
);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_filename() {
let current_path = Path::new("tracing-subscriber")
.join("src")
.join("fmt")
.join("format")
.join("json.rs")
.to_str()
.expect("path must be valid unicode")
// escape windows backslashes
.replace('\\', "\\\\");
let expected =
&format!("{}{}{}",
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"filename\":\"",
current_path,
"\",\"fields\":{\"message\":\"some json test\"}}\n");
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_file(true)
.with_span_list(true);
test_json(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_line_number() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"line_number\":42,\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_line_number(true)
.with_span_list(true);
test_json_with_line_number(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_flattened_event() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"message\":\"some json test\"}\n";
let subscriber = subscriber()
.flatten_event(true)
.with_current_span(true)
.with_span_list(true);
test_json(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_disabled_current_span_event() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(false)
.with_span_list(true);
test_json(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_disabled_span_list_event() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_span_list(false);
test_json(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_nested_span() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3},{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_span_list(true);
test_json(expected, subscriber, || {
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
let _guard = span.enter();
let span = tracing::span!(
tracing::Level::INFO,
"nested_json_span",
answer = 43,
number = 4
);
let _guard = span.enter();
tracing::info!("some json test");
});
}
#[test]
fn json_no_span() {
let expected =
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n";
let subscriber = subscriber()
.flatten_event(false)
.with_current_span(true)
.with_span_list(true);
test_json(expected, subscriber, || {
tracing::info!("some json test");
});
}
#[test]
fn record_works() {
// This test reproduces issue #707, where using `Span::record` causes
// any events inside the span to be ignored.
let make_writer = MockMakeWriter::default();
let subscriber = crate::fmt()
.json()
.with_writer(make_writer.clone())
.finish();
with_default(subscriber, || {
tracing::info!("an event outside the root span");
assert_eq!(
parse_as_json(&make_writer)["fields"]["message"],
"an event outside the root span"
);
let span = tracing::info_span!("the span", na = tracing::field::Empty);
span.record("na", "value");
let _enter = span.enter();
tracing::info!("an event inside the root span");
assert_eq!(
parse_as_json(&make_writer)["fields"]["message"],
"an event inside the root span"
);
});
}
#[test]
fn json_span_event_show_correct_context() {
let buffer = MockMakeWriter::default();
let subscriber = subscriber()
.with_writer(buffer.clone())
.flatten_event(false)
.with_current_span(true)
.with_span_list(false)
.with_span_events(FmtSpan::FULL)
.finish();
with_default(subscriber, || {
let context = "parent";
let parent_span = tracing::info_span!("parent_span", context);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "new");
assert_eq!(event["span"]["context"], "parent");
let _parent_enter = parent_span.enter();
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "enter");
assert_eq!(event["span"]["context"], "parent");
let context = "child";
let child_span = tracing::info_span!("child_span", context);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "new");
assert_eq!(event["span"]["context"], "child");
let _child_enter = child_span.enter();
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "enter");
assert_eq!(event["span"]["context"], "child");
drop(_child_enter);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "exit");
assert_eq!(event["span"]["context"], "child");
drop(child_span);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "close");
assert_eq!(event["span"]["context"], "child");
drop(_parent_enter);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "exit");
assert_eq!(event["span"]["context"], "parent");
drop(parent_span);
let event = parse_as_json(&buffer);
assert_eq!(event["fields"]["message"], "close");
assert_eq!(event["span"]["context"], "parent");
});
}
#[test]
fn json_span_event_with_no_fields() {
// Check span events serialize correctly.
// Discussion: https://github.com/tokio-rs/tracing/issues/829#issuecomment-661984255
let buffer = MockMakeWriter::default();
let subscriber = subscriber()
.with_writer(buffer.clone())
.flatten_event(false)
.with_current_span(false)
.with_span_list(false)
.with_span_events(FmtSpan::FULL)
.finish();
with_default(subscriber, || {
let span = tracing::info_span!("valid_json");
assert_eq!(parse_as_json(&buffer)["fields"]["message"], "new");
let _enter = span.enter();
assert_eq!(parse_as_json(&buffer)["fields"]["message"], "enter");
drop(_enter);
assert_eq!(parse_as_json(&buffer)["fields"]["message"], "exit");
drop(span);
assert_eq!(parse_as_json(&buffer)["fields"]["message"], "close");
});
}
fn parse_as_json(buffer: &MockMakeWriter) -> serde_json::Value {
let buf = String::from_utf8(buffer.buf().to_vec()).unwrap();
let json = buf
.lines()
.last()
.expect("expected at least one line to be written!");
match serde_json::from_str(json) {
Ok(v) => v,
Err(e) => panic!(
"assertion failed: JSON shouldn't be malformed\n error: {}\n json: {}",
e, json
),
}
}
fn test_json<T>(
expected: &str,
builder: crate::fmt::SubscriberBuilder<JsonFields, Format<Json>>,
producer: impl FnOnce() -> T,
) {
let make_writer = MockMakeWriter::default();
let subscriber = builder
.with_writer(make_writer.clone())
.with_timer(MockTime)
.finish();
with_default(subscriber, producer);
let buf = make_writer.buf();
let actual = std::str::from_utf8(&buf[..]).unwrap();
assert_eq!(
serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
.unwrap(),
serde_json::from_str(actual).unwrap()
);
}
fn test_json_with_line_number<T>(
expected: &str,
builder: crate::fmt::SubscriberBuilder<JsonFields, Format<Json>>,
producer: impl FnOnce() -> T,
) {
let make_writer = MockMakeWriter::default();
let subscriber = builder
.with_writer(make_writer.clone())
.with_timer(MockTime)
.finish();
with_default(subscriber, producer);
let buf = make_writer.buf();
let actual = std::str::from_utf8(&buf[..]).unwrap();
let mut expected =
serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
.unwrap();
let expect_line_number = expected.remove("line_number").is_some();
let mut actual: std::collections::HashMap<&str, serde_json::Value> =
serde_json::from_str(actual).unwrap();
let line_number = actual.remove("line_number");
if expect_line_number {
assert_eq!(line_number.map(|x| x.is_number()), Some(true));
} else {
assert!(line_number.is_none());
}
assert_eq!(actual, expected);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,514 @@
use super::*;
use crate::{
field::{VisitFmt, VisitOutput},
fmt::fmt_layer::{FmtContext, FormattedFields},
registry::LookupSpan,
};
use std::fmt;
use tracing_core::{
field::{self, Field},
Event, Level, Subscriber,
};
#[cfg(feature = "tracing-log")]
use tracing_log::NormalizeEvent;
use nu_ansi_term::{Color, Style};
/// An excessively pretty, human-readable event formatter.
///
/// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a
/// multi-line output format. Each individual event may output multiple lines of
/// text.
///
/// # Example Output
///
/// <pre><font color="#4E9A06"><b>:;</b></font> <font color="#4E9A06">cargo</font> run --example fmt-pretty
/// <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.08s
/// <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/fmt-pretty`
/// 2022-02-15T18:44:24.535324Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: preparing to shave yaks, </font><font color="#4E9A06"><b>number_of_yaks</b></font><font color="#4E9A06">: 3</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:16 <font color="#AAAAAA"><i>on</i></font> main
///
/// 2022-02-15T18:44:24.535403Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty::yak_shave</b></font><font color="#4E9A06">: shaving yaks</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:41 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535442Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535469Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 1
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535502Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 1, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535524Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 1</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535551Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535573Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: yak shaved successfully</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:25 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 2
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535600Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 2, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: true</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535618Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535644Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: hello! I&apos;m gonna shave a yak, </font><font color="#75507B"><b>excitement</b></font><font color="#75507B">: &quot;yay!&quot;</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:16 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535670Z <font color="#C4A000"> WARN</font> <font color="#C4A000"><b>fmt_pretty::yak_shave</b></font><font color="#C4A000">: could not locate yak</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:18 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shave</b> <font color="#AAAAAA"><i>with</i></font> <b>yak</b>: 3
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535698Z <font color="#3465A4">DEBUG</font> <font color="#3465A4"><b>yak_events</b></font><font color="#3465A4">: </font><font color="#3465A4"><b>yak</b></font><font color="#3465A4">: 3, </font><font color="#3465A4"><b>shaved</b></font><font color="#3465A4">: false</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:46 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535720Z <font color="#CC0000">ERROR</font> <font color="#CC0000"><b>fmt_pretty::yak_shave</b></font><font color="#CC0000">: failed to shave yak, </font><font color="#CC0000"><b>yak</b></font><font color="#CC0000">: 3, </font><font color="#CC0000"><b>error</b></font><font color="#CC0000">: missing yak, </font><font color="#CC0000"><b>error.sources</b></font><font color="#CC0000">: [out of space, out of cash]</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:51 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535742Z <font color="#75507B">TRACE</font> <font color="#75507B"><b>fmt_pretty::yak_shave</b></font><font color="#75507B">: </font><font color="#75507B"><b>yaks_shaved</b></font><font color="#75507B">: 2</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt/yak_shave.rs:55 <font color="#AAAAAA"><i>on</i></font> main
/// <font color="#AAAAAA"><i>in</i></font> fmt_pretty::yak_shave::<b>shaving_yaks</b> <font color="#AAAAAA"><i>with</i></font> <b>yaks</b>: 3
///
/// 2022-02-15T18:44:24.535765Z <font color="#4E9A06"> INFO</font> <font color="#4E9A06"><b>fmt_pretty</b></font><font color="#4E9A06">: yak shaving completed, </font><font color="#4E9A06"><b>all_yaks_shaved</b></font><font color="#4E9A06">: false</font>
/// <font color="#AAAAAA"><i>at</i></font> examples/examples/fmt-pretty.rs:19 <font color="#AAAAAA"><i>on</i></font> main
/// </pre>
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Pretty {
display_location: bool,
}
/// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation.
///
/// [visitor]: field::Visit
/// [`MakeVisitor`]: crate::field::MakeVisitor
#[derive(Debug)]
pub struct PrettyVisitor<'a> {
writer: Writer<'a>,
is_empty: bool,
style: Style,
result: fmt::Result,
}
/// An excessively pretty, human-readable [`MakeVisitor`] implementation.
///
/// [`MakeVisitor`]: crate::field::MakeVisitor
#[derive(Debug)]
pub struct PrettyFields {
/// A value to override the provided `Writer`'s ANSI formatting
/// configuration.
///
/// If this is `Some`, we override the `Writer`'s ANSI setting. This is
/// necessary in order to continue supporting the deprecated
/// `PrettyFields::with_ansi` method. If it is `None`, we don't override the
/// ANSI formatting configuration (because the deprecated method was not
/// called).
// TODO: when `PrettyFields::with_ansi` is removed, we can get rid
// of this entirely.
ansi: Option<bool>,
}
// === impl Pretty ===
impl Default for Pretty {
fn default() -> Self {
Self {
display_location: true,
}
}
}
impl Pretty {
fn style_for(level: &Level) -> Style {
match *level {
Level::TRACE => Style::new().fg(Color::Purple),
Level::DEBUG => Style::new().fg(Color::Blue),
Level::INFO => Style::new().fg(Color::Green),
Level::WARN => Style::new().fg(Color::Yellow),
Level::ERROR => Style::new().fg(Color::Red),
}
}
/// Sets whether the event's source code location is displayed.
///
/// This defaults to `true`.
#[deprecated(
since = "0.3.6",
note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead."
)]
pub fn with_source_location(self, display_location: bool) -> Self {
Self {
display_location,
..self
}
}
}
impl<C, N, T> FormatEvent<C, N> for Format<Pretty, T>
where
C: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
T: FormatTime,
{
fn format_event(
&self,
ctx: &FmtContext<'_, C, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
#[cfg(feature = "tracing-log")]
let normalized_meta = event.normalized_metadata();
#[cfg(feature = "tracing-log")]
let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
#[cfg(not(feature = "tracing-log"))]
let meta = event.metadata();
write!(&mut writer, " ")?;
// if the `Format` struct *also* has an ANSI color configuration,
// override the writer...the API for configuring ANSI color codes on the
// `Format` struct is deprecated, but we still need to honor those
// configurations.
if let Some(ansi) = self.ansi {
writer = writer.with_ansi(ansi);
}
self.format_timestamp(&mut writer)?;
let style = if self.display_level && writer.has_ansi_escapes() {
Pretty::style_for(meta.level())
} else {
Style::new()
};
if self.display_level {
write!(
writer,
"{} ",
super::FmtLevel::new(meta.level(), writer.has_ansi_escapes())
)?;
}
if self.display_target {
let target_style = if writer.has_ansi_escapes() {
style.bold()
} else {
style
};
write!(
writer,
"{}{}{}:",
target_style.prefix(),
meta.target(),
target_style.infix(style)
)?;
}
let line_number = if self.display_line_number {
meta.line()
} else {
None
};
// If the file name is disabled, format the line number right after the
// target. Otherwise, if we also display the file, it'll go on a
// separate line.
if let (Some(line_number), false, true) = (
line_number,
self.display_filename,
self.format.display_location,
) {
write!(
writer,
"{}{}{}:",
style.prefix(),
line_number,
style.infix(style)
)?;
}
writer.write_char(' ')?;
let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style);
event.record(&mut v);
v.finish()?;
writer.write_char('\n')?;
let dimmed = if writer.has_ansi_escapes() {
Style::new().dimmed().italic()
} else {
Style::new()
};
let thread = self.display_thread_name || self.display_thread_id;
if let (Some(file), true, true) = (
meta.file(),
self.format.display_location,
self.display_filename,
) {
write!(writer, " {} {}", dimmed.paint("at"), file,)?;
if let Some(line) = line_number {
write!(writer, ":{}", line)?;
}
writer.write_char(if thread { ' ' } else { '\n' })?;
} else if thread {
write!(writer, " ")?;
};
if thread {
write!(writer, "{} ", dimmed.paint("on"))?;
let thread = std::thread::current();
if self.display_thread_name {
if let Some(name) = thread.name() {
write!(writer, "{}", name)?;
if self.display_thread_id {
writer.write_char(' ')?;
}
}
}
if self.display_thread_id {
write!(writer, "{:?}", thread.id())?;
}
writer.write_char('\n')?;
}
let bold = writer.bold();
let span = event
.parent()
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current());
let scope = span.into_iter().flat_map(|span| span.scope());
for span in scope {
let meta = span.metadata();
if self.display_target {
write!(
writer,
" {} {}::{}",
dimmed.paint("in"),
meta.target(),
bold.paint(meta.name()),
)?;
} else {
write!(
writer,
" {} {}",
dimmed.paint("in"),
bold.paint(meta.name()),
)?;
}
let ext = span.extensions();
let fields = &ext
.get::<FormattedFields<N>>()
.expect("Unable to find FormattedFields in extensions; this is a bug");
if !fields.is_empty() {
write!(writer, " {} {}", dimmed.paint("with"), fields)?;
}
writer.write_char('\n')?;
}
writer.write_char('\n')
}
}
impl<'writer> FormatFields<'writer> for Pretty {
fn format_fields<R: RecordFields>(&self, writer: Writer<'writer>, fields: R) -> fmt::Result {
let mut v = PrettyVisitor::new(writer, true);
fields.record(&mut v);
v.finish()
}
fn add_fields(
&self,
current: &'writer mut FormattedFields<Self>,
fields: &span::Record<'_>,
) -> fmt::Result {
let empty = current.is_empty();
let writer = current.as_writer();
let mut v = PrettyVisitor::new(writer, empty);
fields.record(&mut v);
v.finish()
}
}
// === impl PrettyFields ===
impl Default for PrettyFields {
fn default() -> Self {
Self::new()
}
}
impl PrettyFields {
/// Returns a new default [`PrettyFields`] implementation.
pub fn new() -> Self {
// By default, don't override the `Writer`'s ANSI colors
// configuration. We'll only do this if the user calls the
// deprecated `PrettyFields::with_ansi` method.
Self { ansi: None }
}
/// Enable ANSI encoding for formatted fields.
#[deprecated(
since = "0.3.3",
note = "Use `fmt::Subscriber::with_ansi` or `fmt::Layer::with_ansi` instead."
)]
pub fn with_ansi(self, ansi: bool) -> Self {
Self {
ansi: Some(ansi),
..self
}
}
}
impl<'a> MakeVisitor<Writer<'a>> for PrettyFields {
type Visitor = PrettyVisitor<'a>;
#[inline]
fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor {
if let Some(ansi) = self.ansi {
target = target.with_ansi(ansi);
}
PrettyVisitor::new(target, true)
}
}
// === impl PrettyVisitor ===
impl<'a> PrettyVisitor<'a> {
/// Returns a new default visitor that formats to the provided `writer`.
///
/// # Arguments
/// - `writer`: the writer to format to.
/// - `is_empty`: whether or not any fields have been previously written to
/// that writer.
pub fn new(writer: Writer<'a>, is_empty: bool) -> Self {
Self {
writer,
is_empty,
style: Style::default(),
result: Ok(()),
}
}
pub(crate) fn with_style(self, style: Style) -> Self {
Self { style, ..self }
}
fn write_padded(&mut self, value: &impl fmt::Debug) {
let padding = if self.is_empty {
self.is_empty = false;
""
} else {
", "
};
self.result = write!(self.writer, "{}{:?}", padding, value);
}
fn bold(&self) -> Style {
if self.writer.has_ansi_escapes() {
self.style.bold()
} else {
Style::new()
}
}
}
impl field::Visit for PrettyVisitor<'_> {
fn record_str(&mut self, field: &Field, value: &str) {
if self.result.is_err() {
return;
}
if field.name() == "message" {
self.record_debug(field, &format_args!("{}", value))
} else {
self.record_debug(field, &value)
}
}
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
if let Some(source) = value.source() {
let bold = self.bold();
self.record_debug(
field,
&format_args!(
"{}, {}{}.sources{}: {}",
Escape(&format_args!("{}", value)),
bold.prefix(),
field,
bold.infix(self.style),
ErrorSourceList(source),
),
)
} else {
self.record_debug(field, &Escape(&format_args!("{}", value)))
}
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if self.result.is_err() {
return;
}
let bold = self.bold();
match field.name() {
"message" => {
// Escape ANSI characters to prevent malicious patterns (e.g., terminal injection attacks)
self.write_padded(&format_args!("{}{:?}", self.style.prefix(), Escape(value)))
},
// Skip fields that are actually log metadata that have already been handled
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => self.result = Ok(()),
name if name.starts_with("r#") => self.write_padded(&format_args!(
"{}{}{}: {:?}",
bold.prefix(),
&name[2..],
bold.infix(self.style),
value
)),
name => self.write_padded(&format_args!(
"{}{}{}: {:?}",
bold.prefix(),
name,
bold.infix(self.style),
value
)),
};
}
}
impl VisitOutput<fmt::Result> for PrettyVisitor<'_> {
fn finish(mut self) -> fmt::Result {
write!(&mut self.writer, "{}", self.style.suffix())?;
self.result
}
}
impl VisitFmt for PrettyVisitor<'_> {
fn writer(&mut self) -> &mut dyn fmt::Write {
&mut self.writer
}
}

1368
vendor/tracing-subscriber/src/fmt/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
use crate::fmt::format::Writer;
use crate::fmt::time::FormatTime;
use std::sync::Arc;
/// Formats [local time]s and [UTC time]s with `FormatTime` implementations
/// that use the [`chrono` crate].
///
/// [local time]: [`chrono::offset::Local`]
/// [UTC time]: [`chrono::offset::Utc`]
/// [`chrono` crate]: [`chrono`]
/// Formats the current [local time] using a [formatter] from the [`chrono`] crate.
///
/// [local time]: chrono::Local::now()
/// [formatter]: chrono::format
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct ChronoLocal {
format: Arc<ChronoFmtType>,
}
impl ChronoLocal {
/// Format the time using the [`RFC 3339`] format
/// (a subset of [`ISO 8601`]).
///
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self {
format: Arc::new(ChronoFmtType::Rfc3339),
}
}
/// Format the time using the given format string.
///
/// See [`chrono::format::strftime`] for details on the supported syntax.
pub fn new(format_string: String) -> Self {
Self {
format: Arc::new(ChronoFmtType::Custom(format_string)),
}
}
}
impl FormatTime for ChronoLocal {
fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
let t = chrono::Local::now();
match self.format.as_ref() {
ChronoFmtType::Rfc3339 => {
use chrono::format::{Fixed, Item};
write!(
w,
"{}",
t.format_with_items(core::iter::once(Item::Fixed(Fixed::RFC3339)))
)
}
ChronoFmtType::Custom(fmt) => {
write!(w, "{}", t.format(fmt))
}
}
}
}
/// Formats the current [UTC time] using a [formatter] from the [`chrono`] crate.
///
/// [UTC time]: chrono::Utc::now()
/// [formatter]: chrono::format
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct ChronoUtc {
format: Arc<ChronoFmtType>,
}
impl ChronoUtc {
/// Format the time using the [`RFC 3339`] format
/// (a subset of [`ISO 8601`]).
///
/// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
/// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self {
format: Arc::new(ChronoFmtType::Rfc3339),
}
}
/// Format the time using the given format string.
///
/// See [`chrono::format::strftime`] for details on the supported syntax.
pub fn new(format_string: String) -> Self {
Self {
format: Arc::new(ChronoFmtType::Custom(format_string)),
}
}
}
impl FormatTime for ChronoUtc {
fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
let t = chrono::Utc::now();
match self.format.as_ref() {
ChronoFmtType::Rfc3339 => w.write_str(&t.to_rfc3339()),
ChronoFmtType::Custom(fmt) => w.write_str(&format!("{}", t.format(fmt))),
}
}
}
/// The RFC 3339 format is used by default but a custom format string
/// can be used. See [`chrono::format::strftime`]for details on
/// the supported syntax.
///
/// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Default)]
enum ChronoFmtType {
/// Format according to the RFC 3339 convention.
#[default]
Rfc3339,
/// Format according to a custom format string.
Custom(String),
}
#[cfg(test)]
mod tests {
use crate::fmt::format::Writer;
use crate::fmt::time::FormatTime;
use std::sync::Arc;
use super::ChronoFmtType;
use super::ChronoLocal;
use super::ChronoUtc;
#[test]
fn test_chrono_format_time_utc_default() {
let mut buf = String::new();
let mut dst: Writer<'_> = Writer::new(&mut buf);
assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok());
// e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00"
assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
}
#[test]
fn test_chrono_format_time_utc_custom() {
let fmt = ChronoUtc {
format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
};
let mut buf = String::new();
let mut dst: Writer<'_> = Writer::new(&mut buf);
assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
// e.g. `buf` contains "Wed Aug 23 15:53:23 2023"
assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
}
#[test]
fn test_chrono_format_time_local_default() {
let mut buf = String::new();
let mut dst: Writer<'_> = Writer::new(&mut buf);
assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok());
// e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00".
assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
}
#[test]
fn test_chrono_format_time_local_custom() {
let fmt = ChronoLocal {
format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
};
let mut buf = String::new();
let mut dst: Writer<'_> = Writer::new(&mut buf);
assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
// e.g. `buf` contains "Wed Aug 23 15:55:46 2023".
assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
}
}

View File

@@ -0,0 +1,410 @@
// musl as a whole is licensed under the following standard MIT license:
//
// ----------------------------------------------------------------------
// Copyright © 2005-2020 Rich Felker, et al.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ----------------------------------------------------------------------
//
// Authors/contributors include:
//
// A. Wilcox
// Ada Worcester
// Alex Dowad
// Alex Suykov
// Alexander Monakov
// Andre McCurdy
// Andrew Kelley
// Anthony G. Basile
// Aric Belsito
// Arvid Picciani
// Bartosz Brachaczek
// Benjamin Peterson
// Bobby Bingham
// Boris Brezillon
// Brent Cook
// Chris Spiegel
// Clément Vasseur
// Daniel Micay
// Daniel Sabogal
// Daurnimator
// David Carlier
// David Edelsohn
// Denys Vlasenko
// Dmitry Ivanov
// Dmitry V. Levin
// Drew DeVault
// Emil Renner Berthing
// Fangrui Song
// Felix Fietkau
// Felix Janda
// Gianluca Anzolin
// Hauke Mehrtens
// He X
// Hiltjo Posthuma
// Isaac Dunham
// Jaydeep Patil
// Jens Gustedt
// Jeremy Huntwork
// Jo-Philipp Wich
// Joakim Sindholt
// John Spencer
// Julien Ramseier
// Justin Cormack
// Kaarle Ritvanen
// Khem Raj
// Kylie McClain
// Leah Neukirchen
// Luca Barbato
// Luka Perkov
// M Farkas-Dyck (Strake)
// Mahesh Bodapati
// Markus Wichmann
// Masanori Ogino
// Michael Clark
// Michael Forney
// Mikhail Kremnyov
// Natanael Copa
// Nicholas J. Kain
// orc
// Pascal Cuoq
// Patrick Oppenlander
// Petr Hosek
// Petr Skocik
// Pierre Carrier
// Reini Urban
// Rich Felker
// Richard Pennington
// Ryan Fairfax
// Samuel Holland
// Segev Finer
// Shiz
// sin
// Solar Designer
// Stefan Kristiansson
// Stefan O'Rear
// Szabolcs Nagy
// Timo Teräs
// Trutz Behn
// Valentin Ochs
// Will Dietz
// William Haddon
// William Pitcock
//
// Portions of this software are derived from third-party works licensed
// under terms compatible with the above MIT license:
//
// The TRE regular expression implementation (src/regex/reg* and
// src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
// under a 2-clause BSD license (license text in the source files). The
// included version has been heavily modified by Rich Felker in 2012, in
// the interests of size, simplicity, and namespace cleanliness.
//
// Much of the math library code (src/math/* and src/complex/*) is
// Copyright © 1993,2004 Sun Microsystems or
// Copyright © 2003-2011 David Schultz or
// Copyright © 2003-2009 Steven G. Kargl or
// Copyright © 2003-2009 Bruce D. Evans or
// Copyright © 2008 Stephen L. Moshier or
// Copyright © 2017-2018 Arm Limited
// and labelled as such in comments in the individual source files. All
// have been licensed under extremely permissive terms.
//
// The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008
// The Android Open Source Project and is licensed under a two-clause BSD
// license. It was taken from Bionic libc, used on Android.
//
// The AArch64 memcpy and memset code (src/string/aarch64/*) are
// Copyright © 1999-2019, Arm Limited.
//
// The implementation of DES for crypt (src/crypt/crypt_des.c) is
// Copyright © 1994 David Burren. It is licensed under a BSD license.
//
// The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
// originally written by Solar Designer and placed into the public
// domain. The code also comes with a fallback permissive license for use
// in jurisdictions that may not recognize the public domain.
//
// The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
// Valentin Ochs and is licensed under an MIT-style license.
//
// The x86_64 port was written by Nicholas J. Kain and is licensed under
// the standard MIT terms.
//
// The mips and microblaze ports were originally written by Richard
// Pennington for use in the ellcc project. The original code was adapted
// by Rich Felker for build system and code conventions during upstream
// integration. It is licensed under the standard MIT terms.
//
// The mips64 port was contributed by Imagination Technologies and is
// licensed under the standard MIT terms.
//
// The powerpc port was also originally written by Richard Pennington,
// and later supplemented and integrated by John Spencer. It is licensed
// under the standard MIT terms.
//
// All other files which have no copyright comments are original works
// produced specifically for use as part of this library, written either
// by Rich Felker, the main author of the library, or by one or more
// contibutors listed above. Details on authorship of individual files
// can be found in the git version control history of the project. The
// omission of copyright and license comments in each file is in the
// interest of source tree size.
//
// In addition, permission is hereby granted for all public header files
// (include/* and arch/*/bits/*) and crt files intended to be linked into
// applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
// the copyright notice and permission notice otherwise required by the
// license, and to use these files without any requirement of
// attribution. These files include substantial contributions from:
//
// Bobby Bingham
// John Spencer
// Nicholas J. Kain
// Rich Felker
// Richard Pennington
// Stefan Kristiansson
// Szabolcs Nagy
//
// all of whom have explicitly granted such permission.
//
// This file previously contained text expressing a belief that most of
// the files covered by the above exception were sufficiently trivial not
// to be subject to copyright, resulting in confusion over whether it
// negated the permissions granted in the license. In the spirit of
// permissive licensing, and of not having licensing issues being an
// obstacle to adoption, that text has been removed.
use std::fmt;
/// A date/time type which exists primarily to convert `SystemTime` timestamps into an ISO 8601
/// formatted string.
///
/// Yes, this exists. Before you have a heart attack, understand that the meat of this is musl's
/// [`__secs_to_tm`][1] converted to Rust via [c2rust][2] and then cleaned up by hand as part of
/// the [kudu-rs project][3], [released under MIT][4].
///
/// [1] http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c
/// [2] https://c2rust.com/
/// [3] https://github.com/danburkert/kudu-rs/blob/c9660067e5f4c1a54143f169b5eeb49446f82e54/src/timestamp.rs#L5-L18
/// [4] https://github.com/tokio-rs/tracing/issues/1644#issuecomment-963888244
///
/// All existing `strftime`-like APIs I found were unable to handle the full range of timestamps representable
/// by `SystemTime`, including `strftime` itself, since tm.tm_year is an int.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct DateTime {
year: i64,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
nanos: u32,
}
impl fmt::Display for DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.year > 9999 {
write!(f, "+{}", self.year)?;
} else if self.year < 0 {
write!(f, "{:05}", self.year)?;
} else {
write!(f, "{:04}", self.year)?;
}
write!(
f,
"-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z",
self.month,
self.day,
self.hour,
self.minute,
self.second,
self.nanos / 1_000
)
}
}
impl From<std::time::SystemTime> for DateTime {
fn from(timestamp: std::time::SystemTime) -> DateTime {
let (t, nanos) = match timestamp.duration_since(std::time::UNIX_EPOCH) {
Ok(duration) => {
debug_assert!(duration.as_secs() <= i64::MAX as u64);
(duration.as_secs() as i64, duration.subsec_nanos())
}
Err(error) => {
let duration = error.duration();
debug_assert!(duration.as_secs() <= i64::MAX as u64);
let (secs, nanos) = (duration.as_secs() as i64, duration.subsec_nanos());
if nanos == 0 {
(-secs, 0)
} else {
(-secs - 1, 1_000_000_000 - nanos)
}
}
};
// 2000-03-01 (mod 400 year, immediately after feb29
const LEAPOCH: i64 = 946_684_800 + 86400 * (31 + 29);
const DAYS_PER_400Y: i32 = 365 * 400 + 97;
const DAYS_PER_100Y: i32 = 365 * 100 + 24;
const DAYS_PER_4Y: i32 = 365 * 4 + 1;
static DAYS_IN_MONTH: [i8; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];
// Note(dcb): this bit is rearranged slightly to avoid integer overflow.
let mut days: i64 = (t / 86_400) - (LEAPOCH / 86_400);
let mut remsecs: i32 = (t % 86_400) as i32;
if remsecs < 0i32 {
remsecs += 86_400;
days -= 1
}
let mut qc_cycles: i32 = (days / i64::from(DAYS_PER_400Y)) as i32;
let mut remdays: i32 = (days % i64::from(DAYS_PER_400Y)) as i32;
if remdays < 0 {
remdays += DAYS_PER_400Y;
qc_cycles -= 1;
}
let mut c_cycles: i32 = remdays / DAYS_PER_100Y;
if c_cycles == 4 {
c_cycles -= 1;
}
remdays -= c_cycles * DAYS_PER_100Y;
let mut q_cycles: i32 = remdays / DAYS_PER_4Y;
if q_cycles == 25 {
q_cycles -= 1;
}
remdays -= q_cycles * DAYS_PER_4Y;
let mut remyears: i32 = remdays / 365;
if remyears == 4 {
remyears -= 1;
}
remdays -= remyears * 365;
let mut years: i64 = i64::from(remyears)
+ 4 * i64::from(q_cycles)
+ 100 * i64::from(c_cycles)
+ 400 * i64::from(qc_cycles);
let mut months: i32 = 0;
while i32::from(DAYS_IN_MONTH[months as usize]) <= remdays {
remdays -= i32::from(DAYS_IN_MONTH[months as usize]);
months += 1
}
if months >= 10 {
months -= 12;
years += 1;
}
DateTime {
year: years + 2000,
month: (months + 3) as u8,
day: (remdays + 1) as u8,
hour: (remsecs / 3600) as u8,
minute: (remsecs / 60 % 60) as u8,
second: (remsecs % 60) as u8,
nanos,
}
}
}
#[cfg(test)]
mod tests {
use i32;
use std::time::{Duration, UNIX_EPOCH};
use super::*;
#[test]
fn test_datetime() {
let case = |expected: &str, secs: i64, micros: u32| {
let timestamp = if secs >= 0 {
UNIX_EPOCH + Duration::new(secs as u64, micros * 1_000)
} else {
(UNIX_EPOCH - Duration::new(!secs as u64 + 1, 0)) + Duration::new(0, micros * 1_000)
};
assert_eq!(
expected,
format!("{}", DateTime::from(timestamp)),
"secs: {}, micros: {}",
secs,
micros
)
};
// Mostly generated with:
// - date -jur <secs> +"%Y-%m-%dT%H:%M:%S.000000Z"
// - http://unixtimestamp.50x.eu/
case("1970-01-01T00:00:00.000000Z", 0, 0);
case("1970-01-01T00:00:00.000001Z", 0, 1);
case("1970-01-01T00:00:00.500000Z", 0, 500_000);
case("1970-01-01T00:00:01.000001Z", 1, 1);
case("1970-01-01T00:01:01.000001Z", 60 + 1, 1);
case("1970-01-01T01:01:01.000001Z", 60 * 60 + 60 + 1, 1);
case(
"1970-01-02T01:01:01.000001Z",
24 * 60 * 60 + 60 * 60 + 60 + 1,
1,
);
case("1969-12-31T23:59:59.000000Z", -1, 0);
case("1969-12-31T23:59:59.000001Z", -1, 1);
case("1969-12-31T23:59:59.500000Z", -1, 500_000);
case("1969-12-31T23:58:59.000001Z", -60 - 1, 1);
case("1969-12-31T22:58:59.000001Z", -60 * 60 - 60 - 1, 1);
case(
"1969-12-30T22:58:59.000001Z",
-24 * 60 * 60 - 60 * 60 - 60 - 1,
1,
);
case("2038-01-19T03:14:07.000000Z", i32::MAX as i64, 0);
case("2038-01-19T03:14:08.000000Z", i32::MAX as i64 + 1, 0);
case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0);
case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0);
// Skipping these tests on windows as std::time::SystemTime range is low
// on Windows compared with that of Unix which can cause the following
// high date value tests to panic
#[cfg(not(target_os = "windows"))]
{
case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0);
case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0);
case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0);
}
case("1900-01-01T00:00:00.000000Z", -2208988800, 0);
case("1899-12-31T23:59:59.000000Z", -2208988801, 0);
case("0000-01-01T00:00:00.000000Z", -62167219200, 0);
case("-0001-12-31T23:59:59.000000Z", -62167219201, 0);
case("1234-05-06T07:08:09.000000Z", -23215049511, 0);
case("-1234-05-06T07:08:09.000000Z", -101097651111, 0);
case("2345-06-07T08:09:01.000000Z", 11847456541, 0);
case("-2345-06-07T08:09:01.000000Z", -136154620259, 0);
}
}

View File

@@ -0,0 +1,151 @@
//! Formatters for event timestamps.
use crate::fmt::format::Writer;
use std::fmt;
use std::time::Instant;
mod datetime;
#[cfg(feature = "time")]
mod time_crate;
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub use time_crate::UtcTime;
#[cfg(feature = "local-time")]
#[cfg_attr(docsrs, doc(cfg(all(unsound_local_offset, feature = "local-time"))))]
pub use time_crate::LocalTime;
#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub use time_crate::OffsetTime;
/// [`chrono`]-based implementation for [`FormatTime`].
#[cfg(feature = "chrono")]
mod chrono_crate;
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub use chrono_crate::ChronoLocal;
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub use chrono_crate::ChronoUtc;
/// A type that can measure and format the current time.
///
/// This trait is used by `Format` to include a timestamp with each `Event` when it is logged.
///
/// Notable default implementations of this trait are `SystemTime` and `()`. The former prints the
/// current time as reported by `std::time::SystemTime`, and the latter does not print the current
/// time at all. `FormatTime` is also automatically implemented for any function pointer with the
/// appropriate signature.
///
/// The full list of provided implementations can be found in [`time`].
///
/// [`time`]: self
pub trait FormatTime {
/// Measure and write out the current time.
///
/// When `format_time` is called, implementors should get the current time using their desired
/// mechanism, and write it out to the given `fmt::Write`. Implementors must insert a trailing
/// space themselves if they wish to separate the time from subsequent log message text.
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result;
}
/// Returns a new `SystemTime` timestamp provider.
///
/// This can then be configured further to determine how timestamps should be
/// configured.
///
/// This is equivalent to calling
/// ```rust
/// # fn timer() -> tracing_subscriber::fmt::time::SystemTime {
/// tracing_subscriber::fmt::time::SystemTime::default()
/// # }
/// ```
pub fn time() -> SystemTime {
SystemTime
}
/// Returns a new `Uptime` timestamp provider.
///
/// With this timer, timestamps will be formatted with the amount of time
/// elapsed since the timestamp provider was constructed.
///
/// This can then be configured further to determine how timestamps should be
/// configured.
///
/// This is equivalent to calling
/// ```rust
/// # fn timer() -> tracing_subscriber::fmt::time::Uptime {
/// tracing_subscriber::fmt::time::Uptime::default()
/// # }
/// ```
pub fn uptime() -> Uptime {
Uptime::default()
}
impl<F> FormatTime for &F
where
F: FormatTime,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
(*self).format_time(w)
}
}
impl FormatTime for () {
fn format_time(&self, _: &mut Writer<'_>) -> fmt::Result {
Ok(())
}
}
impl FormatTime for fn(&mut Writer<'_>) -> fmt::Result {
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
(*self)(w)
}
}
/// Retrieve and print the current wall-clock time.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub struct SystemTime;
/// Retrieve and print the relative elapsed wall-clock time since an epoch.
///
/// The `Default` implementation for `Uptime` makes the epoch the current time.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Uptime {
epoch: Instant,
}
impl Default for Uptime {
fn default() -> Self {
Uptime {
epoch: Instant::now(),
}
}
}
impl From<Instant> for Uptime {
fn from(epoch: Instant) -> Self {
Uptime { epoch }
}
}
impl FormatTime for SystemTime {
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
write!(
w,
"{}",
datetime::DateTime::from(std::time::SystemTime::now())
)
}
}
impl FormatTime for Uptime {
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
let e = self.epoch.elapsed();
write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos())
}
}

View File

@@ -0,0 +1,470 @@
use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor};
use std::fmt;
use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset};
/// Formats the current [local time] using a [formatter] from the [`time` crate].
///
/// To format the current [UTC time] instead, use the [`UtcTime`] type.
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
/// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/"><code>time</code>
/// crate</a> must be compiled with <code>--cfg unsound_local_offset</code> in order to use
/// local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
/// events will be logged without timestamps.
///
/// Alternatively, [`OffsetTime`] can log with a local offset if it is initialized early.
///
/// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags"><code>time</code>
/// documentation</a> for more details.
/// </pre></div>
///
/// [local time]: time::OffsetDateTime::now_local
/// [UTC time]: time::OffsetDateTime::now_utc
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[derive(Clone, Debug)]
#[cfg_attr(
docsrs,
doc(cfg(all(unsound_local_offset, feature = "time", feature = "local-time")))
)]
#[cfg(feature = "local-time")]
pub struct LocalTime<F> {
format: F,
}
/// Formats the current [UTC time] using a [formatter] from the [`time` crate].
///
/// To format the current [local time] instead, use the [`LocalTime`] type.
///
/// [local time]: time::OffsetDateTime::now_local
/// [UTC time]: time::OffsetDateTime::now_utc
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
#[derive(Clone, Debug)]
pub struct UtcTime<F> {
format: F,
}
/// Formats the current time using a fixed offset and a [formatter] from the [`time` crate].
///
/// This is typically used as an alternative to [`LocalTime`]. `LocalTime` determines the offset
/// every time it formats a message, which may be unsound or fail. With `OffsetTime`, the offset is
/// determined once. This makes it possible to do so while the program is still single-threaded and
/// handle any errors. However, this also means the offset cannot change while the program is
/// running (the offset will not change across DST changes).
///
/// [formatter]: time::formatting::Formattable
/// [`time` crate]: time
#[derive(Clone, Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
pub struct OffsetTime<F> {
offset: time::UtcOffset,
format: F,
}
// === impl LocalTime ===
#[cfg(feature = "local-time")]
impl LocalTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current [local time] in the
/// [RFC 3339] format (a subset of the [ISO 8601] timestamp format).
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(time::LocalTime::rfc_3339());
/// # drop(subscriber);
/// ```
///
/// [local time]: time::OffsetDateTime::now_local
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self::new(well_known::Rfc3339)
}
}
#[cfg(feature = "local-time")]
impl<F: Formattable> LocalTime<F> {
/// Returns a formatter that formats the current [local time] using the
/// [`time` crate] with the provided provided format. The format may be any
/// type that implements the [`Formattable`] trait.
///
///
/// <div class="example-wrap" style="display:inline-block">
/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
/// <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/">
/// <code>time</code> crate</a> must be compiled with <code>--cfg
/// unsound_local_offset</code> in order to use local timestamps. When this
/// cfg is not enabled, local timestamps cannot be recorded, and
/// events will be logged without timestamps.
///
/// See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags">
/// <code>time</code> documentation</a> for more details.
/// </pre></div>
///
/// Typically, the format will be a format description string, or one of the
/// `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// throwing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
/// use time::macros::format_description;
///
/// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]"));
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
///
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = LocalTime::new(time_format);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`LocalTime::rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::LocalTime};
///
/// let timer = LocalTime::new(time::format_description::well_known::Rfc3339);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// [local time]: time::OffsetDateTime::now_local()
/// [`time` crate]: time
/// [`Formattable`]: time::formatting::Formattable
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: https://docs.rs/time/0.3/time/macros/macro.format_description.html
/// [`time::format_description::parse`]: time::format_description::parse()
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(format: F) -> Self {
Self { format }
}
}
#[cfg(feature = "local-time")]
impl<F> FormatTime for LocalTime<F>
where
F: Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
format_datetime(now, w, &self.format)
}
}
#[cfg(feature = "local-time")]
impl<F> Default for LocalTime<F>
where
F: Formattable + Default,
{
fn default() -> Self {
Self::new(F::default())
}
}
// === impl UtcTime ===
impl UtcTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current [UTC time] in the
/// [RFC 3339] format, which is a subset of the [ISO 8601] timestamp format.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(time::UtcTime::rfc_3339());
/// # drop(subscriber);
/// ```
///
/// [local time]: time::OffsetDateTime::now_utc
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn rfc_3339() -> Self {
Self::new(well_known::Rfc3339)
}
}
impl<F: Formattable> UtcTime<F> {
/// Returns a formatter that formats the current [UTC time] using the
/// [`time` crate], with the provided provided format. The format may be any
/// type that implements the [`Formattable`] trait.
///
/// Typically, the format will be a format description string, or one of the
/// `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// failing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
/// use time::macros::format_description;
///
/// let timer = UtcTime::new(format_description!("[hour]:[minute]:[second]"));
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
///
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = UtcTime::new(time_format);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`UtcTime::rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::UtcTime};
///
/// let timer = UtcTime::new(time::format_description::well_known::Rfc3339);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// [UTC time]: time::OffsetDateTime::now_utc()
/// [`time` crate]: time
/// [`Formattable`]: time::formatting::Formattable
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: https://docs.rs/time/0.3/time/macros/macro.format_description.html
/// [`time::format_description::parse`]: time::format_description::parse
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(format: F) -> Self {
Self { format }
}
}
impl<F> FormatTime for UtcTime<F>
where
F: Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
format_datetime(OffsetDateTime::now_utc(), w, &self.format)
}
}
impl<F> Default for UtcTime<F>
where
F: Formattable + Default,
{
fn default() -> Self {
Self::new(F::default())
}
}
// === impl OffsetTime ===
#[cfg(feature = "local-time")]
impl OffsetTime<well_known::Rfc3339> {
/// Returns a formatter that formats the current time using the [local time offset] in the [RFC
/// 3339] format (a subset of the [ISO 8601] timestamp format).
///
/// Returns an error if the local time offset cannot be determined. This typically occurs in
/// multithreaded programs. To avoid this problem, initialize `OffsetTime` before forking
/// threads. When using Tokio, this means initializing `OffsetTime` before the Tokio runtime.
///
/// # Examples
///
/// ```
/// use tracing_subscriber::fmt::{self, time};
///
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!"));
/// # drop(subscriber);
/// ```
///
/// Using `OffsetTime` with Tokio:
///
/// ```
/// use tracing_subscriber::fmt::time::OffsetTime;
///
/// #[tokio::main]
/// async fn run() {
/// tracing::info!("runtime initialized");
///
/// // At this point the Tokio runtime is initialized, and we can use both Tokio and Tracing
/// // normally.
/// }
///
/// fn main() {
/// // Because we need to get the local offset before Tokio spawns any threads, our `main`
/// // function cannot use `tokio::main`.
/// tracing_subscriber::fmt()
/// .with_timer(OffsetTime::local_rfc_3339().expect("could not get local time offset"))
/// .init();
///
/// // Even though `run` is written as an `async fn`, because we used `tokio::main` on it
/// // we can call it as a synchronous function.
/// run();
/// }
/// ```
///
/// [local time offset]: time::UtcOffset::current_local_offset
/// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339
/// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
pub fn local_rfc_3339() -> Result<Self, time::error::IndeterminateOffset> {
Ok(Self::new(
UtcOffset::current_local_offset()?,
well_known::Rfc3339,
))
}
}
impl<F: time::formatting::Formattable> OffsetTime<F> {
/// Returns a formatter that formats the current time using the [`time` crate] with the provided
/// provided format and [timezone offset]. The format may be any type that implements the
/// [`Formattable`] trait.
///
///
/// Typically, the offset will be the [local offset], and format will be a format description
/// string, or one of the `time` crate's [well-known formats].
///
/// If the format description is statically known, then the
/// [`format_description!`] macro should be used. This is identical to the
/// [`time::format_description::parse`] method, but runs at compile-time,
/// throwing an error if the format description is invalid. If the desired format
/// is not known statically (e.g., a user is providing a format string), then the
/// [`time::format_description::parse`] method should be used. Note that this
/// method is fallible.
///
/// See the [`time` book] for details on the format description syntax.
///
/// # Examples
///
/// Using the [`format_description!`] macro:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::macros::format_description;
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let timer = OffsetTime::new(offset, format_description!("[hour]:[minute]:[second]"));
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using [`time::format_description::parse`]:
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let time_format = time::format_description::parse("[hour]:[minute]:[second]")
/// .expect("format string should be valid!");
/// let timer = OffsetTime::new(offset, time_format);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// Using the [`format_description!`] macro requires enabling the `time`
/// crate's "macros" feature flag.
///
/// Using a [well-known format][well-known formats] (this is equivalent to
/// [`OffsetTime::local_rfc_3339`]):
///
/// ```
/// use tracing_subscriber::fmt::{self, time::OffsetTime};
/// use time::UtcOffset;
///
/// let offset = UtcOffset::current_local_offset().expect("should get local offset!");
/// let timer = OffsetTime::new(offset, time::format_description::well_known::Rfc3339);
/// let subscriber = tracing_subscriber::fmt()
/// .with_timer(timer);
/// # drop(subscriber);
/// ```
///
/// [`time` crate]: time
/// [timezone offset]: time::UtcOffset
/// [`Formattable`]: time::formatting::Formattable
/// [local offset]: time::UtcOffset::current_local_offset()
/// [well-known formats]: time::format_description::well_known
/// [`format_description!`]: https://docs.rs/time/0.3/time/macros/macro.format_description.html
/// [`time::format_description::parse`]: time::format_description::parse
/// [`time` book]: https://time-rs.github.io/book/api/format-description.html
pub fn new(offset: time::UtcOffset, format: F) -> Self {
Self { offset, format }
}
}
impl<F> FormatTime for OffsetTime<F>
where
F: time::formatting::Formattable,
{
fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
let now = OffsetDateTime::now_utc().to_offset(self.offset);
format_datetime(now, w, &self.format)
}
}
fn format_datetime(
now: OffsetDateTime,
into: &mut Writer<'_>,
fmt: &impl Formattable,
) -> fmt::Result {
let mut into = WriteAdaptor::new(into);
now.format_into(&mut into, fmt)
.map_err(|_| fmt::Error)
.map(|_| ())
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,433 @@
use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event};
use crate::registry::{self, LookupSpan, SpanRef};
#[cfg(all(feature = "registry", feature = "std"))]
use crate::{filter::FilterId, registry::Registry};
/// Represents information about the current context provided to [`Layer`]s by the
/// wrapped [`Subscriber`].
///
/// To access [stored data] keyed by a span ID, implementors of the `Layer`
/// trait should ensure that the `Subscriber` type parameter is *also* bound by the
/// [`LookupSpan`]:
///
/// ```rust
/// use tracing::Subscriber;
/// use tracing_subscriber::{Layer, registry::LookupSpan};
///
/// pub struct MyLayer;
///
/// impl<S> Layer<S> for MyLayer
/// where
/// S: Subscriber + for<'a> LookupSpan<'a>,
/// {
/// // ...
/// }
/// ```
///
/// [`Layer`]: super::Layer
/// [`Subscriber`]: tracing_core::Subscriber
/// [stored data]: crate::registry::SpanRef
/// [`LookupSpan`]: crate::registry::LookupSpan
#[derive(Debug)]
pub struct Context<'a, S> {
subscriber: Option<&'a S>,
/// The bitmask of all [`Filtered`] layers that currently apply in this
/// context. If there is only a single [`Filtered`] wrapping the layer that
/// produced this context, then this is that filter's ID. Otherwise, if we
/// are in a nested tree with multiple filters, this is produced by
/// [`and`]-ing together the [`FilterId`]s of each of the filters that wrap
/// the current layer.
///
/// [`Filtered`]: crate::filter::Filtered
/// [`FilterId`]: crate::filter::FilterId
/// [`and`]: crate::filter::FilterId::and
#[cfg(all(feature = "registry", feature = "std"))]
filter: FilterId,
}
// === impl Context ===
impl<'a, S> Context<'a, S>
where
S: Subscriber,
{
pub(super) fn new(subscriber: &'a S) -> Self {
Self {
subscriber: Some(subscriber),
#[cfg(feature = "registry")]
filter: FilterId::none(),
}
}
/// Returns the wrapped subscriber's view of the current span.
#[inline]
pub fn current_span(&self) -> span::Current {
self.subscriber
.map(Subscriber::current_span)
// TODO: this would be more correct as "unknown", so perhaps
// `tracing-core` should make `Current::unknown()` public?
.unwrap_or_else(span::Current::none)
}
/// Returns whether the wrapped subscriber would enable the current span.
#[inline]
pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.subscriber
.map(|subscriber| subscriber.enabled(metadata))
// If this context is `None`, we are registering a callsite, so
// return `true` so that the layer does not incorrectly assume that
// the inner subscriber has disabled this metadata.
// TODO(eliza): would it be more correct for this to return an `Option`?
.unwrap_or(true)
}
/// Records the provided `event` with the wrapped subscriber.
///
/// # Notes
///
/// - The subscriber is free to expect that the event's callsite has been
/// [registered][register], and may panic or fail to observe the event if this is
/// not the case. The `tracing` crate's macros ensure that all events are
/// registered, but if the event is constructed through other means, the
/// user is responsible for ensuring that [`register_callsite`][register]
/// has been called prior to calling this method.
/// - This does _not_ call [`enabled`] on the inner subscriber. If the
/// caller wishes to apply the wrapped subscriber's filter before choosing
/// whether to record the event, it may first call [`Context::enabled`] to
/// check whether the event would be enabled. This allows `Layer`s to
/// elide constructing the event if it would not be recorded.
///
/// [register]: tracing_core::subscriber::Subscriber::register_callsite()
/// [`enabled`]: tracing_core::subscriber::Subscriber::enabled()
/// [`Context::enabled`]: Context::enabled()
#[inline]
pub fn event(&self, event: &Event<'_>) {
if let Some(subscriber) = self.subscriber {
subscriber.event(event);
}
}
/// Returns a [`SpanRef`] for the parent span of the given [`Event`], if
/// it has a parent.
///
/// If the event has an explicitly overridden parent, this method returns
/// a reference to that span. If the event's parent is the current span,
/// this returns a reference to the current span, if there is one. If this
/// returns `None`, then either the event's parent was explicitly set to
/// `None`, or the event's parent was defined contextually, but no span
/// is currently entered.
///
/// Compared to [`Context::current_span`] and [`Context::lookup_current`],
/// this respects overrides provided by the [`Event`].
///
/// Compared to [`Event::parent`], this automatically falls back to the contextual
/// span, if required.
///
/// ```rust
/// use tracing::{Event, Subscriber};
/// use tracing_subscriber::{
/// layer::{Context, Layer},
/// prelude::*,
/// registry::LookupSpan,
/// };
///
/// struct PrintingLayer;
/// impl<S> Layer<S> for PrintingLayer
/// where
/// S: Subscriber + for<'lookup> LookupSpan<'lookup>,
/// {
/// fn on_event(&self, event: &Event, ctx: Context<S>) {
/// let span = ctx.event_span(event);
/// println!("Event in span: {:?}", span.map(|s| s.name()));
/// }
/// }
///
/// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || {
/// tracing::info!("no span");
/// // Prints: Event in span: None
///
/// let span = tracing::info_span!("span");
/// tracing::info!(parent: &span, "explicitly specified");
/// // Prints: Event in span: Some("span")
///
/// let _guard = span.enter();
/// tracing::info!("contextual span");
/// // Prints: Event in span: Some("span")
/// });
/// ```
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
#[inline]
pub fn event_span(&self, event: &Event<'_>) -> Option<SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
if event.is_root() {
None
} else if event.is_contextual() {
self.lookup_current()
} else {
// TODO(eliza): this should handle parent IDs
event.parent().and_then(|id| self.span(id))
}
}
/// Returns metadata for the span with the given `id`, if it exists.
///
/// If this returns `None`, then no span exists for that ID (either it has
/// closed or the ID is invalid).
#[inline]
pub fn metadata(&self, id: &span::Id) -> Option<&'static Metadata<'static>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let span = self.span(id)?;
Some(span.metadata())
}
/// Returns [stored data] for the span with the given `id`, if it exists.
///
/// If this returns `None`, then no span exists for that ID (either it has
/// closed or the ID is invalid).
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
///
/// [stored data]: crate::registry::SpanRef
#[inline]
pub fn span(&self, id: &span::Id) -> Option<registry::SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let span = self.subscriber.as_ref()?.span(id)?;
#[cfg(all(feature = "registry", feature = "std"))]
return span.try_with_filter(self.filter);
#[cfg(not(feature = "registry"))]
Some(span)
}
/// Returns `true` if an active span exists for the given `Id`.
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
#[inline]
pub fn exists(&self, id: &span::Id) -> bool
where
S: for<'lookup> LookupSpan<'lookup>,
{
self.subscriber.as_ref().and_then(|s| s.span(id)).is_some()
}
/// Returns [stored data] for the span that the wrapped subscriber considers
/// to be the current.
///
/// If this returns `None`, then we are not currently within a span.
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
///
/// [stored data]: crate::registry::SpanRef
#[inline]
pub fn lookup_current(&self) -> Option<registry::SpanRef<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
let subscriber = *self.subscriber.as_ref()?;
let current = subscriber.current_span();
let id = current.id()?;
let span = subscriber.span(id);
debug_assert!(
span.is_some(),
"the subscriber should have data for the current span ({:?})!",
id,
);
// If we found a span, and our per-layer filter enables it, return that
// span!
#[cfg(all(feature = "registry", feature = "std"))]
{
if let Some(span) = span?.try_with_filter(self.filter) {
Some(span)
} else {
// Otherwise, the span at the *top* of the stack is disabled by
// per-layer filtering, but there may be additional spans in the stack.
//
// Currently, `LookupSpan` doesn't have a nice way of exposing access to
// the whole span stack. However, if we can downcast the innermost
// subscriber to a a `Registry`, we can iterate over its current span
// stack.
//
// TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is
// implemented, change this to use that instead...
self.lookup_current_filtered(subscriber)
}
}
#[cfg(not(feature = "registry"))]
span
}
/// Slow path for when the current span is disabled by PLF and we have a
/// registry.
// This is called by `lookup_current` in the case that per-layer filtering
// is in use. `lookup_current` is allowed to be inlined, but this method is
// factored out to prevent the loop and (potentially-recursive) subscriber
// downcasting from being inlined if `lookup_current` is inlined.
#[inline(never)]
#[cfg(all(feature = "registry", feature = "std"))]
fn lookup_current_filtered<'lookup>(
&self,
subscriber: &'lookup S,
) -> Option<registry::SpanRef<'lookup, S>>
where
S: LookupSpan<'lookup>,
{
let registry = (subscriber as &dyn Subscriber).downcast_ref::<Registry>()?;
registry
.span_stack()
.iter()
.find_map(|id| subscriber.span(id)?.try_with_filter(self.filter))
}
/// Returns an iterator over the [stored data] for all the spans in the
/// current context, starting with the specified span and ending with the
/// root of the trace tree.
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This returns the spans in reverse order (from leaf to root). Use
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
/// in case root-to-leaf ordering is desired.
/// </pre>
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
///
/// [stored data]: crate::registry::SpanRef
pub fn span_scope(&self, id: &span::Id) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.span(id)?.scope())
}
/// Returns an iterator over the [stored data] for all the spans in the
/// current context, starting with the parent span of the specified event,
/// and ending with the root of the trace tree and ending with the current span.
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: Compared to <a href="#method.scope"><code>scope</code></a> this
/// returns the spans in reverse order (from leaf to root). Use
/// <a href="../registry/struct.Scope.html#method.from_root"><code>Scope::from_root</code></a>
/// in case root-to-leaf ordering is desired.
/// </pre>
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: This requires the wrapped subscriber to
/// implement the <a href="../registry/trait.LookupSpan.html"><code>
/// LookupSpan</code></a> trait. See the documentation on
/// <a href="./struct.Context.html"><code>Context</code>'s
/// declaration</a> for details.
/// </pre>
///
/// [stored data]: crate::registry::SpanRef
pub fn event_scope(&self, event: &Event<'_>) -> Option<registry::Scope<'_, S>>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.event_span(event)?.scope())
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn with_filter(self, filter: FilterId) -> Self {
// If we already have our own `FilterId`, combine it with the provided
// one. That way, the new `FilterId` will consider a span to be disabled
// if it was disabled by the given `FilterId` *or* any `FilterId`s for
// layers "above" us in the stack.
//
// See the doc comment for `FilterId::and` for details.
let filter = self.filter.and(filter);
Self { filter, ..self }
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool
where
S: for<'lookup> LookupSpan<'lookup>,
{
self.is_enabled_inner(span, filter).unwrap_or(false)
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option<Self>
where
S: for<'lookup> LookupSpan<'lookup>,
{
if self.is_enabled_inner(span, filter)? {
Some(self.with_filter(filter))
} else {
None
}
}
#[cfg(all(feature = "registry", feature = "std"))]
fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option<bool>
where
S: for<'lookup> LookupSpan<'lookup>,
{
Some(self.span(span)?.is_enabled_for(filter))
}
}
impl<S> Context<'_, S> {
pub(crate) fn none() -> Self {
Self {
subscriber: None,
#[cfg(feature = "registry")]
filter: FilterId::none(),
}
}
}
impl<S> Clone for Context<'_, S> {
#[inline]
fn clone(&self) -> Self {
let subscriber = self.subscriber.as_ref().copied();
Context {
subscriber,
#[cfg(all(feature = "registry", feature = "std"))]
filter: self.filter,
}
}
}

View File

@@ -0,0 +1,554 @@
use tracing_core::{metadata::Metadata, span, Dispatch, Event, Interest, LevelFilter, Subscriber};
use crate::{
filter,
layer::{Context, Layer},
registry::LookupSpan,
};
#[cfg(all(feature = "registry", feature = "std"))]
use crate::{filter::FilterId, registry::Registry};
use core::{
any::{Any, TypeId},
cmp, fmt,
marker::PhantomData,
};
/// A [`Subscriber`] composed of a `Subscriber` wrapped by one or more
/// [`Layer`]s.
///
/// [`Layer`]: crate::Layer
/// [`Subscriber`]: tracing_core::Subscriber
#[derive(Clone)]
pub struct Layered<L, I, S = I> {
/// The layer.
layer: L,
/// The inner value that `self.layer` was layered onto.
///
/// If this is also a `Layer`, then this `Layered` will implement `Layer`.
/// If this is a `Subscriber`, then this `Layered` will implement
/// `Subscriber` instead.
inner: I,
// These booleans are used to determine how to combine `Interest`s and max
// level hints when per-layer filters are in use.
/// Is `self.inner` a `Registry`?
///
/// If so, when combining `Interest`s, we want to "bubble up" its
/// `Interest`.
inner_is_registry: bool,
/// Does `self.layer` have per-layer filters?
///
/// This will be true if:
/// - `self.inner` is a `Filtered`.
/// - `self.inner` is a tree of `Layered`s where _all_ arms of those
/// `Layered`s have per-layer filters.
///
/// Otherwise, if it's a `Layered` with one per-layer filter in one branch,
/// but a non-per-layer-filtered layer in the other branch, this will be
/// _false_, because the `Layered` is already handling the combining of
/// per-layer filter `Interest`s and max level hints with its non-filtered
/// `Layer`.
has_layer_filter: bool,
/// Does `self.inner` have per-layer filters?
///
/// This is determined according to the same rules as
/// `has_layer_filter` above.
inner_has_layer_filter: bool,
_s: PhantomData<fn(S)>,
}
// === impl Layered ===
impl<L, S> Layered<L, S>
where
L: Layer<S>,
S: Subscriber,
{
/// Returns `true` if this [`Subscriber`] is the same type as `T`.
pub fn is<T: Any>(&self) -> bool {
self.downcast_ref::<T>().is_some()
}
/// Returns some reference to this [`Subscriber`] value if it is of type `T`,
/// or `None` if it isn't.
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
unsafe {
let raw = self.downcast_raw(TypeId::of::<T>())?;
if raw.is_null() {
None
} else {
Some(&*(raw as *const T))
}
}
}
}
impl<L, S> Subscriber for Layered<L, S>
where
L: Layer<S>,
S: Subscriber,
{
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.pick_interest(self.layer.register_callsite(metadata), || {
self.inner.register_callsite(metadata)
})
}
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
if self.layer.enabled(metadata, self.ctx()) {
// if the outer layer enables the callsite metadata, ask the subscriber.
self.inner.enabled(metadata)
} else {
// otherwise, the callsite is disabled by the layer
// If per-layer filters are in use, and we are short-circuiting
// (rather than calling into the inner type), clear the current
// per-layer filter `enabled` state.
#[cfg(feature = "registry")]
filter::FilterState::clear_enabled();
false
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.pick_level_hint(
self.layer.max_level_hint(),
self.inner.max_level_hint(),
super::subscriber_is_none(&self.inner),
)
}
fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
let id = self.inner.new_span(span);
self.layer.on_new_span(span, &id, self.ctx());
id
}
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
self.inner.record(span, values);
self.layer.on_record(span, values, self.ctx());
}
fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
self.inner.record_follows_from(span, follows);
self.layer.on_follows_from(span, follows, self.ctx());
}
fn event_enabled(&self, event: &Event<'_>) -> bool {
if self.layer.event_enabled(event, self.ctx()) {
// if the outer layer enables the event, ask the inner subscriber.
self.inner.event_enabled(event)
} else {
// otherwise, the event is disabled by this layer
false
}
}
fn event(&self, event: &Event<'_>) {
self.inner.event(event);
self.layer.on_event(event, self.ctx());
}
fn enter(&self, span: &span::Id) {
self.inner.enter(span);
self.layer.on_enter(span, self.ctx());
}
fn exit(&self, span: &span::Id) {
self.inner.exit(span);
self.layer.on_exit(span, self.ctx());
}
fn clone_span(&self, old: &span::Id) -> span::Id {
let new = self.inner.clone_span(old);
if &new != old {
self.layer.on_id_change(old, &new, self.ctx())
};
new
}
#[inline]
fn drop_span(&self, id: span::Id) {
self.try_close(id);
}
fn try_close(&self, id: span::Id) -> bool {
#[cfg(all(feature = "registry", feature = "std"))]
let subscriber = &self.inner as &dyn Subscriber;
#[cfg(all(feature = "registry", feature = "std"))]
let mut guard = subscriber
.downcast_ref::<Registry>()
.map(|registry| registry.start_close(id.clone()));
if self.inner.try_close(id.clone()) {
// If we have a registry's close guard, indicate that the span is
// closing.
#[cfg(all(feature = "registry", feature = "std"))]
{
if let Some(g) = guard.as_mut() {
g.set_closing()
};
}
self.layer.on_close(id, self.ctx());
true
} else {
false
}
}
#[inline]
fn current_span(&self) -> span::Current {
self.inner.current_span()
}
#[doc(hidden)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
// Unlike the implementation of `Layer` for `Layered`, we don't have to
// handle the "magic PLF downcast marker" here. If a `Layered`
// implements `Subscriber`, we already know that the `inner` branch is
// going to contain something that doesn't have per-layer filters (the
// actual root `Subscriber`). Thus, a `Layered` that implements
// `Subscriber` will always be propagating the root subscriber's
// `Interest`/level hint, even if it includes a `Layer` that has
// per-layer filters, because it will only ever contain layers where
// _one_ child has per-layer filters.
//
// The complex per-layer filter detection logic is only relevant to
// *trees* of layers, which involve the `Layer` implementation for
// `Layered`, not *lists* of layers, where every `Layered` implements
// `Subscriber`. Of course, a linked list can be thought of as a
// degenerate tree...but luckily, we are able to make a type-level
// distinction between individual `Layered`s that are definitely
// list-shaped (their inner child implements `Subscriber`), and
// `Layered`s that might be tree-shaped (the inner child is also a
// `Layer`).
// If downcasting to `Self`, return a pointer to `self`.
if id == TypeId::of::<Self>() {
return Some(self as *const _ as *const ());
}
self.layer
.downcast_raw(id)
.or_else(|| self.inner.downcast_raw(id))
}
}
impl<S, A, B> Layer<S> for Layered<A, B, S>
where
A: Layer<S>,
B: Layer<S>,
S: Subscriber,
{
fn on_register_dispatch(&self, subscriber: &Dispatch) {
self.layer.on_register_dispatch(subscriber);
self.inner.on_register_dispatch(subscriber);
}
fn on_layer(&mut self, subscriber: &mut S) {
self.layer.on_layer(subscriber);
self.inner.on_layer(subscriber);
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.pick_interest(self.layer.register_callsite(metadata), || {
self.inner.register_callsite(metadata)
})
}
fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
if self.layer.enabled(metadata, ctx.clone()) {
// if the outer subscriber enables the callsite metadata, ask the inner layer.
self.inner.enabled(metadata, ctx)
} else {
// otherwise, the callsite is disabled by this layer
false
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
self.pick_level_hint(
self.layer.max_level_hint(),
self.inner.max_level_hint(),
super::layer_is_none(&self.inner),
)
}
#[inline]
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
self.inner.on_new_span(attrs, id, ctx.clone());
self.layer.on_new_span(attrs, id, ctx);
}
#[inline]
fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
self.inner.on_record(span, values, ctx.clone());
self.layer.on_record(span, values, ctx);
}
#[inline]
fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) {
self.inner.on_follows_from(span, follows, ctx.clone());
self.layer.on_follows_from(span, follows, ctx);
}
#[inline]
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
if self.layer.event_enabled(event, ctx.clone()) {
// if the outer layer enables the event, ask the inner subscriber.
self.inner.event_enabled(event, ctx)
} else {
// otherwise, the event is disabled by this layer
false
}
}
#[inline]
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
self.inner.on_event(event, ctx.clone());
self.layer.on_event(event, ctx);
}
#[inline]
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
self.inner.on_enter(id, ctx.clone());
self.layer.on_enter(id, ctx);
}
#[inline]
fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
self.inner.on_exit(id, ctx.clone());
self.layer.on_exit(id, ctx);
}
#[inline]
fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
self.inner.on_close(id.clone(), ctx.clone());
self.layer.on_close(id, ctx);
}
#[inline]
fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) {
self.inner.on_id_change(old, new, ctx.clone());
self.layer.on_id_change(old, new, ctx);
}
#[doc(hidden)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
match id {
// If downcasting to `Self`, return a pointer to `self`.
id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
// Oh, we're looking for per-layer filters!
//
// This should only happen if we are inside of another `Layered`,
// and it's trying to determine how it should combine `Interest`s
// and max level hints.
//
// In that case, this `Layered` should be considered to be
// "per-layer filtered" if *both* the outer layer and the inner
// layer/subscriber have per-layer filters. Otherwise, this `Layered
// should *not* be considered per-layer filtered (even if one or the
// other has per layer filters). If only one `Layer` is per-layer
// filtered, *this* `Layered` will handle aggregating the `Interest`
// and level hints on behalf of its children, returning the
// aggregate (which is the value from the &non-per-layer-filtered*
// child).
//
// Yes, this rule *is* slightly counter-intuitive, but it's
// necessary due to a weird edge case that can occur when two
// `Layered`s where one side is per-layer filtered and the other
// isn't are `Layered` together to form a tree. If we didn't have
// this rule, we would actually end up *ignoring* `Interest`s from
// the non-per-layer-filtered layers, since both branches would
// claim to have PLF.
//
// If you don't understand this...that's fine, just don't mess with
// it. :)
id if filter::is_plf_downcast_marker(id) => {
self.layer.downcast_raw(id).and(self.inner.downcast_raw(id))
}
// Otherwise, try to downcast both branches normally...
_ => self
.layer
.downcast_raw(id)
.or_else(|| self.inner.downcast_raw(id)),
}
}
}
impl<'a, L, S> LookupSpan<'a> for Layered<L, S>
where
S: Subscriber + LookupSpan<'a>,
{
type Data = S::Data;
fn span_data(&'a self, id: &span::Id) -> Option<Self::Data> {
self.inner.span_data(id)
}
#[cfg(all(feature = "registry", feature = "std"))]
fn register_filter(&mut self) -> FilterId {
self.inner.register_filter()
}
}
impl<L, S> Layered<L, S>
where
S: Subscriber,
{
fn ctx(&self) -> Context<'_, S> {
Context::new(&self.inner)
}
}
impl<A, B, S> Layered<A, B, S>
where
A: Layer<S>,
S: Subscriber,
{
pub(super) fn new(layer: A, inner: B, inner_has_layer_filter: bool) -> Self {
#[cfg(all(feature = "registry", feature = "std"))]
let inner_is_registry = TypeId::of::<S>() == TypeId::of::<crate::registry::Registry>();
#[cfg(not(all(feature = "registry", feature = "std")))]
let inner_is_registry = false;
let inner_has_layer_filter = inner_has_layer_filter || inner_is_registry;
let has_layer_filter = filter::layer_has_plf(&layer);
Self {
layer,
inner,
has_layer_filter,
inner_has_layer_filter,
inner_is_registry,
_s: PhantomData,
}
}
fn pick_interest(&self, outer: Interest, inner: impl FnOnce() -> Interest) -> Interest {
if self.has_layer_filter {
return inner();
}
// If the outer layer has disabled the callsite, return now so that
// the inner layer/subscriber doesn't get its hopes up.
if outer.is_never() {
// If per-layer filters are in use, and we are short-circuiting
// (rather than calling into the inner type), clear the current
// per-layer filter interest state.
#[cfg(feature = "registry")]
filter::FilterState::take_interest();
return outer;
}
// The `inner` closure will call `inner.register_callsite()`. We do this
// before the `if` statement to ensure that the inner subscriber is
// informed that the callsite exists regardless of the outer layer's
// filtering decision.
let inner = inner();
if outer.is_sometimes() {
// if this interest is "sometimes", return "sometimes" to ensure that
// filters are reevaluated.
return outer;
}
// If there is a per-layer filter in the `inner` stack, and it returns
// `never`, change the interest to `sometimes`, because the `outer`
// layer didn't return `never`. This means that _some_ layer still wants
// to see that callsite, even though the inner stack's per-layer filter
// didn't want it. Therefore, returning `sometimes` will ensure
// `enabled` is called so that the per-layer filter can skip that
// span/event, while the `outer` layer still gets to see it.
if inner.is_never() && self.inner_has_layer_filter {
return Interest::sometimes();
}
// otherwise, allow the inner subscriber or subscriber to weigh in.
inner
}
fn pick_level_hint(
&self,
outer_hint: Option<LevelFilter>,
inner_hint: Option<LevelFilter>,
inner_is_none: bool,
) -> Option<LevelFilter> {
if self.inner_is_registry {
return outer_hint;
}
if self.has_layer_filter && self.inner_has_layer_filter {
return Some(cmp::max(outer_hint?, inner_hint?));
}
if self.has_layer_filter && inner_hint.is_none() {
return None;
}
if self.inner_has_layer_filter && outer_hint.is_none() {
return None;
}
// If the layer is `Option::None`, then we
// want to short-circuit the layer underneath, if it
// returns `None`, to override the `None` layer returning
// `Some(OFF)`, which should ONLY apply when there are
// no other layers that return `None`. Note this
// `None` does not == `Some(TRACE)`, it means
// something more like: "whatever all the other
// layers agree on, default to `TRACE` if none
// have an opinion". We also choose do this AFTER
// we check for per-layer filters, which
// have their own logic.
//
// Also note that this does come at some perf cost, but
// this function is only called on initialization and
// subscriber reloading.
if super::layer_is_none(&self.layer) {
return cmp::max(outer_hint, Some(inner_hint?));
}
// Similarly, if the layer on the inside is `None` and it returned an
// `Off` hint, we want to override that with the outer hint.
if inner_is_none && inner_hint == Some(LevelFilter::OFF) {
return outer_hint;
}
cmp::max(outer_hint, inner_hint)
}
}
impl<A, B, S> fmt::Debug for Layered<A, B, S>
where
A: fmt::Debug,
B: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(all(feature = "registry", feature = "std"))]
let alt = f.alternate();
let mut s = f.debug_struct("Layered");
// These additional fields are more verbose and usually only necessary
// for internal debugging purposes, so only print them if alternate mode
// is enabled.
#[cfg(all(feature = "registry", feature = "std"))]
{
if alt {
s.field("inner_is_registry", &self.inner_is_registry)
.field("has_layer_filter", &self.has_layer_filter)
.field("inner_has_layer_filter", &self.inner_has_layer_filter);
}
}
s.field("layer", &self.layer)
.field("inner", &self.inner)
.finish()
}
}

1910
vendor/tracing-subscriber/src/layer/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
use super::*;
use tracing_core::subscriber::NoSubscriber;
#[derive(Debug)]
pub(crate) struct NopLayer;
impl<S: Subscriber> Layer<S> for NopLayer {}
#[allow(dead_code)]
struct NopLayer2;
impl<S: Subscriber> Layer<S> for NopLayer2 {}
/// A layer that holds a string.
///
/// Used to test that pointers returned by downcasting are actually valid.
struct StringLayer(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer {}
struct StringLayer2(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer2 {}
struct StringLayer3(&'static str);
impl<S: Subscriber> Layer<S> for StringLayer3 {}
pub(crate) struct StringSubscriber(&'static str);
impl Subscriber for StringSubscriber {
fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest {
Interest::never()
}
fn enabled(&self, _: &Metadata<'_>) -> bool {
false
}
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
span::Id::from_u64(1)
}
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn event(&self, _: &Event<'_>) {}
fn enter(&self, _: &span::Id) {}
fn exit(&self, _: &span::Id) {}
}
fn assert_subscriber(_s: impl Subscriber) {}
fn assert_layer<S: Subscriber>(_l: &impl Layer<S>) {}
#[test]
fn layer_is_subscriber() {
let s = NopLayer.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn two_layers_are_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn three_layers_are_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.and_then(NopLayer)
.with_subscriber(NoSubscriber::default());
assert_subscriber(s)
}
#[test]
fn three_layers_are_layer() {
let layers = NopLayer.and_then(NopLayer).and_then(NopLayer);
assert_layer(&layers);
let _ = layers.with_subscriber(NoSubscriber::default());
}
#[test]
#[cfg(feature = "alloc")]
fn box_layer_is_layer() {
use alloc::boxed::Box;
let l: Box<dyn Layer<NoSubscriber> + Send + Sync> = Box::new(NopLayer);
assert_layer(&l);
l.with_subscriber(NoSubscriber::default());
}
#[test]
fn downcasts_to_subscriber() {
let s = NopLayer
.and_then(NopLayer)
.and_then(NopLayer)
.with_subscriber(StringSubscriber("subscriber"));
let subscriber =
<dyn Subscriber>::downcast_ref::<StringSubscriber>(&s).expect("subscriber should downcast");
assert_eq!(subscriber.0, "subscriber");
}
#[test]
fn downcasts_to_layer() {
let s = StringLayer("layer_1")
.and_then(StringLayer2("layer_2"))
.and_then(StringLayer3("layer_3"))
.with_subscriber(NoSubscriber::default());
let layer = <dyn Subscriber>::downcast_ref::<StringLayer>(&s).expect("layer 1 should downcast");
assert_eq!(layer.0, "layer_1");
let layer =
<dyn Subscriber>::downcast_ref::<StringLayer2>(&s).expect("layer 2 should downcast");
assert_eq!(layer.0, "layer_2");
let layer =
<dyn Subscriber>::downcast_ref::<StringLayer3>(&s).expect("layer 3 should downcast");
assert_eq!(layer.0, "layer_3");
}
#[cfg(all(feature = "registry", feature = "std"))]
mod registry_tests {
use super::*;
use crate::registry::LookupSpan;
#[test]
fn context_event_span() {
use std::sync::{Arc, Mutex};
let last_event_span = Arc::new(Mutex::new(None));
struct RecordingLayer {
last_event_span: Arc<Mutex<Option<&'static str>>>,
}
impl<S> Layer<S> for RecordingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
let span = ctx.event_span(event);
*self.last_event_span.lock().unwrap() = span.map(|s| s.name());
}
}
tracing::subscriber::with_default(
crate::registry().with(RecordingLayer {
last_event_span: last_event_span.clone(),
}),
|| {
tracing::info!("no span");
assert_eq!(*last_event_span.lock().unwrap(), None);
let parent = tracing::info_span!("explicit");
tracing::info!(parent: &parent, "explicit span");
assert_eq!(*last_event_span.lock().unwrap(), Some("explicit"));
let _guard = tracing::info_span!("contextual").entered();
tracing::info!("contextual span");
assert_eq!(*last_event_span.lock().unwrap(), Some("contextual"));
},
);
}
/// Tests for how max-level hints are calculated when combining layers
/// with and without per-layer filtering.
mod max_level_hints {
use super::*;
use crate::filter::*;
#[test]
fn mixed_with_unfiltered() {
let subscriber = crate::registry()
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO));
assert_eq!(subscriber.max_level_hint(), None);
}
#[test]
fn mixed_with_unfiltered_layered() {
let subscriber = crate::registry().with(NopLayer).with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::TRACE)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn mixed_interleaved() {
let subscriber = crate::registry()
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer)
.with(NopLayer.with_filter(LevelFilter::INFO));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn mixed_layered() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::INFO).and_then(NopLayer))
.with(NopLayer.and_then(NopLayer.with_filter(LevelFilter::INFO)));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer.with_filter(filter_fn(|_| true)));
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted_nested_outer() {
// if a nested tree of per-layer filters has an _outer_ filter with
// no max level hint, it should return `None`.
let subscriber = crate::registry()
.with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::WARN)),
)
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(LevelFilter::DEBUG)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), None);
}
#[test]
fn plf_only_unhinted_nested_inner() {
// If a nested tree of per-layer filters has an _inner_ filter with
// no max-level hint, but the _outer_ filter has a max level hint,
// it should pick the outer hint. This is because the outer filter
// will disable the spans/events before they make it to the inner
// filter.
let subscriber = dbg!(crate::registry().with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::INFO),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn unhinted_nested_inner() {
let subscriber = dbg!(crate::registry()
.with(NopLayer.and_then(NopLayer).with_filter(LevelFilter::INFO))
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::WARN),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn unhinted_nested_inner_mixed() {
let subscriber = dbg!(crate::registry()
.with(
NopLayer
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::INFO)
)
.with(
NopLayer
.with_filter(filter_fn(|_| true))
.and_then(NopLayer.with_filter(filter_fn(|_| true)))
.with_filter(LevelFilter::WARN),
));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO));
}
#[test]
fn plf_only_picks_max() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::WARN))
.with(NopLayer.with_filter(LevelFilter::DEBUG));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
#[test]
fn many_plf_only_picks_max() {
let subscriber = crate::registry()
.with(NopLayer.with_filter(LevelFilter::WARN))
.with(NopLayer.with_filter(LevelFilter::DEBUG))
.with(NopLayer.with_filter(LevelFilter::INFO))
.with(NopLayer.with_filter(LevelFilter::ERROR));
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
#[test]
fn nested_plf_only_picks_max() {
let subscriber = crate::registry()
.with(
NopLayer.with_filter(LevelFilter::INFO).and_then(
NopLayer
.with_filter(LevelFilter::WARN)
.and_then(NopLayer.with_filter(LevelFilter::DEBUG)),
),
)
.with(
NopLayer
.with_filter(LevelFilter::INFO)
.and_then(NopLayer.with_filter(LevelFilter::ERROR)),
);
assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG));
}
}
}

252
vendor/tracing-subscriber/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,252 @@
//! Utilities for implementing and composing [`tracing`] subscribers.
//!
//! [`tracing`] is a framework for instrumenting Rust programs to collect
//! scoped, structured, and async-aware diagnostics. The [`Subscriber`] trait
//! represents the functionality necessary to collect this trace data. This
//! crate contains tools for composing subscribers out of smaller units of
//! behaviour, and batteries-included implementations of common subscriber
//! functionality.
//!
//! `tracing-subscriber` is intended for use by both `Subscriber` authors and
//! application authors using `tracing` to instrument their applications.
//!
//! *Compiler support: [requires `rustc` 1.65+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
//! ## `Layer`s and `Filter`s
//!
//! The most important component of the `tracing-subscriber` API is the
//! [`Layer`] trait, which provides a composable abstraction for building
//! [`Subscriber`]s. Like the [`Subscriber`] trait, a [`Layer`] defines a
//! particular behavior for collecting trace data. Unlike [`Subscriber`]s,
//! which implement a *complete* strategy for how trace data is collected,
//! [`Layer`]s provide *modular* implementations of specific behaviors.
//! Therefore, they can be [composed together] to form a [`Subscriber`] which is
//! capable of recording traces in a variety of ways. See the [`layer` module's
//! documentation][layer] for details on using [`Layer`]s.
//!
//! In addition, the [`Filter`] trait defines an interface for filtering what
//! spans and events are recorded by a particular layer. This allows different
//! [`Layer`]s to handle separate subsets of the trace data emitted by a
//! program. See the [documentation on per-layer filtering][plf] for more
//! information on using [`Filter`]s.
//!
//! [`Layer`]: crate::layer::Layer
//! [composed together]: crate::layer#composing-layers
//! [layer]: crate::layer
//! [`Filter`]: crate::layer::Filter
//! [plf]: crate::layer#per-layer-filtering
//!
//! ## Included Subscribers
//!
//! The following `Subscriber`s are provided for application authors:
//!
//! - [`fmt`] - Formats and logs tracing data (requires the `fmt` feature flag)
//!
//! ## Feature Flags
//!
//! - `std`: Enables APIs that depend on the Rust standard library
//! (enabled by default).
//! - `alloc`: Depend on [`liballoc`] (enabled by "std").
//! - `env-filter`: Enables the [`EnvFilter`] type, which implements filtering
//! similar to the [`env_logger` crate]. **Requires "std"**.
//! - `fmt`: Enables the [`fmt`] module, which provides a subscriber
//! implementation for printing formatted representations of trace events.
//! Enabled by default. **Requires "registry" and "std"**.
//! - `ansi`: Enables `fmt` support for ANSI terminal colors. Enabled by
//! default.
//! - `registry`: enables the [`registry`] module. Enabled by default.
//! **Requires "std"**.
//! - `json`: Enables `fmt` support for JSON output. In JSON output, the ANSI
//! feature does nothing. **Requires "fmt" and "std"**.
//! - `local-time`: Enables local time formatting when using the [`time`
//! crate]'s timestamp formatters with the `fmt` subscriber.
//!
//! [`registry`]: mod@registry
//!
//! ### Optional Dependencies
//!
//! - [`tracing-log`]: Enables better formatting for events emitted by `log`
//! macros in the `fmt` subscriber. Enabled by default.
//! - [`time`][`time` crate]: Enables support for using the [`time` crate] for timestamp
//! formatting in the `fmt` subscriber.
//! - [`smallvec`]: Causes the `EnvFilter` type to use the `smallvec` crate (rather
//! than `Vec`) as a performance optimization. Enabled by default.
//! - [`parking_lot`]: Use the `parking_lot` crate's `RwLock` implementation
//! rather than the Rust standard library's implementation.
//!
//! ### `no_std` Support
//!
//! In embedded systems and other bare-metal applications, `tracing` can be
//! used without requiring the Rust standard library, although some features are
//! disabled. Although most of the APIs provided by `tracing-subscriber`, such
//! as [`fmt`] and [`EnvFilter`], require the standard library, some
//! functionality, such as the [`Layer`] trait, can still be used in
//! `no_std` environments.
//!
//! The dependency on the standard library is controlled by two crate feature
//! flags, "std", which enables the dependency on [`libstd`], and "alloc", which
//! enables the dependency on [`liballoc`] (and is enabled by the "std"
//! feature). These features are enabled by default, but `no_std` users can
//! disable them using:
//!
//! ```toml
//! # Cargo.toml
//! tracing-subscriber = { version = "0.3", default-features = false }
//! ```
//!
//! Additional APIs are available when [`liballoc`] is available. To enable
//! `liballoc` but not `std`, use:
//!
//! ```toml
//! # Cargo.toml
//! tracing-subscriber = { version = "0.3", default-features = false, features = ["alloc"] }
//! ```
//!
//! ### Unstable Features
//!
//! These feature flags enable **unstable** features. The public API may break in 0.1.x
//! releases. To enable these features, the `--cfg tracing_unstable` must be passed to
//! `rustc` when compiling.
//!
//! The following unstable feature flags are currently available:
//!
//! * `valuable`: Enables support for serializing values recorded using the
//! [`valuable`] crate as structured JSON in the [`format::Json`] formatter.
//!
//! #### Enabling Unstable Features
//!
//! The easiest way to set the `tracing_unstable` cfg is to use the `RUSTFLAGS`
//! env variable when running `cargo` commands:
//!
//! ```shell
//! RUSTFLAGS="--cfg tracing_unstable" cargo build
//! ```
//! Alternatively, the following can be added to the `.cargo/config` file in a
//! project to automatically enable the cfg flag for that project:
//!
//! ```toml
//! [build]
//! rustflags = ["--cfg", "tracing_unstable"]
//! ```
//!
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
//! [`valuable`]: https://crates.io/crates/valuable
//! [`format::Json`]: crate::fmt::format::Json
//!
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
//! version is 1.65. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
//! stable compiler version is 1.69, the minimum supported version will not be
//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
//! [`Subscriber`]: tracing_core::subscriber::Subscriber
//! [`tracing`]: https://docs.rs/tracing/latest/tracing
//! [`EnvFilter`]: filter::EnvFilter
//! [`fmt`]: mod@fmt
//! [`tracing`]: https://crates.io/crates/tracing
//! [`tracing-log`]: https://crates.io/crates/tracing-log
//! [`smallvec`]: https://crates.io/crates/smallvec
//! [`env_logger` crate]: https://crates.io/crates/env_logger
//! [`parking_lot`]: https://crates.io/crates/parking_lot
//! [`time` crate]: https://crates.io/crates/time
//! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html
//! [`libstd`]: https://doc.rust-lang.org/std/index.html
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/main/assets/logo-type.png",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(
docsrs,
// Allows displaying cfgs/feature flags in the documentation.
feature(doc_cfg),
// Allows adding traits to RustDoc's list of "notable traits"
feature(doc_notable_trait),
// Fail the docs build if any intra-docs links are broken
deny(rustdoc::broken_intra_doc_links),
)]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub,
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_interfaces,
private_bounds,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
// Using struct update syntax when a struct has no additional fields avoids
// a potential source change if additional fields are added to the struct in the
// future, reducing diff noise. Allow this even though clippy considers it
// "needless".
#![allow(clippy::needless_update)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[macro_use]
mod macros;
pub mod field;
pub mod filter;
pub mod prelude;
pub mod registry;
pub mod layer;
pub mod util;
feature! {
#![feature = "std"]
pub mod reload;
pub(crate) mod sync;
}
feature! {
#![all(feature = "fmt", feature = "std")]
pub mod fmt;
pub use fmt::fmt;
pub use fmt::Subscriber as FmtSubscriber;
}
feature! {
#![all(feature = "env-filter", feature = "std")]
pub use filter::EnvFilter;
}
pub use layer::Layer;
feature! {
#![all(feature = "registry", feature = "std")]
pub use registry::Registry;
/// Returns a default [`Registry`].
pub fn registry() -> Registry {
Registry::default()
}
}
mod sealed {
pub trait Sealed<A = ()> {}
}

28
vendor/tracing-subscriber/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
#[cfg(feature = "std")]
macro_rules! try_lock {
($lock:expr) => {
try_lock!($lock, else return)
};
($lock:expr, else $els:expr) => {
if let ::core::result::Result::Ok(l) = $lock {
l
} else if std::thread::panicking() {
$els
} else {
panic!("lock poisoned")
}
};
}
macro_rules! feature {
(
#![$meta:meta]
$($item:item)*
) => {
$(
#[cfg($meta)]
#[cfg_attr(docsrs, doc(cfg($meta)))]
$item
)*
}
}

View File

@@ -0,0 +1,19 @@
//! The `tracing-subscriber` prelude.
//!
//! This brings into scope a number of extension traits that define methods on
//! types defined here and in other crates.
pub use crate::field::{
MakeExt as __tracing_subscriber_field_MakeExt,
RecordFields as __tracing_subscriber_field_RecordFields,
};
pub use crate::layer::{
Layer as __tracing_subscriber_Layer, SubscriberExt as __tracing_subscriber_SubscriberExt,
};
pub use crate::util::SubscriberInitExt as _;
feature! {
#![all(feature = "fmt", feature = "std")]
pub use crate::fmt::writer::MakeWriterExt as _;
}

View File

@@ -0,0 +1,274 @@
// taken from https://github.com/hyperium/http/blob/master/src/extensions.rs.
use crate::sync::{RwLockReadGuard, RwLockWriteGuard};
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt,
hash::{BuildHasherDefault, Hasher},
};
#[allow(warnings)]
type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
/// With TypeIds as keys, there's no need to hash them. They are already hashes
/// themselves, coming from the compiler. The IdHasher holds the u64 of
/// the TypeId, and then returns it, instead of doing any bit fiddling.
#[derive(Default, Debug)]
struct IdHasher(u64);
impl Hasher for IdHasher {
fn write(&mut self, _: &[u8]) {
unreachable!("TypeId calls write_u64");
}
#[inline]
fn write_u64(&mut self, id: u64) {
self.0 = id;
}
#[inline]
fn finish(&self) -> u64 {
self.0
}
}
/// An immutable, read-only reference to a Span's extensions.
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct Extensions<'a> {
inner: RwLockReadGuard<'a, ExtensionsInner>,
}
impl<'a> Extensions<'a> {
#[cfg(feature = "registry")]
pub(crate) fn new(inner: RwLockReadGuard<'a, ExtensionsInner>) -> Self {
Self { inner }
}
/// Immutably borrows a type previously inserted into this `Extensions`.
pub fn get<T: 'static>(&self) -> Option<&T> {
self.inner.get::<T>()
}
}
/// An mutable reference to a Span's extensions.
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct ExtensionsMut<'a> {
inner: RwLockWriteGuard<'a, ExtensionsInner>,
}
impl<'a> ExtensionsMut<'a> {
#[cfg(feature = "registry")]
pub(crate) fn new(inner: RwLockWriteGuard<'a, ExtensionsInner>) -> Self {
Self { inner }
}
/// Insert a type into this `Extensions`.
///
/// Note that extensions are _not_
/// `Layer`-specific—they are _span_-specific. This means that
/// other layers can access and mutate extensions that
/// a different Layer recorded. For example, an application might
/// have a layer that records execution timings, alongside a layer
/// that reports spans and events to a distributed
/// tracing system that requires timestamps for spans.
/// Ideally, if one layer records a timestamp _x_, the other layer
/// should be able to reuse timestamp _x_.
///
/// Therefore, extensions should generally be newtypes, rather than common
/// types like [`String`](std::string::String), to avoid accidental
/// cross-`Layer` clobbering.
///
/// ## Panics
///
/// If `T` is already present in `Extensions`, then this method will panic.
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) {
assert!(self.replace(val).is_none())
}
/// Replaces an existing `T` into this extensions.
///
/// If `T` is not present, `Option::None` will be returned.
pub fn replace<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.inner.insert(val)
}
/// Get a mutable reference to a type previously inserted on this `ExtensionsMut`.
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.inner.get_mut::<T>()
}
/// Remove a type from this `Extensions`.
///
/// If a extension of this type existed, it will be returned.
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.inner.remove::<T>()
}
}
/// A type map of span extensions.
///
/// [ExtensionsInner] is used by `SpanData` to store and
/// span-specific data. A given `Layer` can read and write
/// data that it is interested in recording and emitting.
#[derive(Default)]
pub(crate) struct ExtensionsInner {
map: AnyMap,
}
impl ExtensionsInner {
/// Create an empty `Extensions`.
#[cfg(any(test, feature = "registry"))]
#[inline]
#[cfg(any(test, feature = "registry"))]
pub(crate) fn new() -> ExtensionsInner {
ExtensionsInner {
map: AnyMap::default(),
}
}
/// Insert a type into this `Extensions`.
///
/// If a extension of this type already existed, it will
/// be returned.
pub(crate) fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|boxed| {
#[allow(warnings)]
{
(boxed as Box<Any + 'static>)
.downcast()
.ok()
.map(|boxed| *boxed)
}
})
}
/// Get a reference to a type previously inserted on this `Extensions`.
pub(crate) fn get<T: 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
}
/// Get a mutable reference to a type previously inserted on this `Extensions`.
pub(crate) fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
}
/// Remove a type from this `Extensions`.
///
/// If a extension of this type existed, it will be returned.
pub(crate) fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>()).and_then(|boxed| {
#[allow(warnings)]
{
(boxed as Box<Any + 'static>)
.downcast()
.ok()
.map(|boxed| *boxed)
}
})
}
/// Clear the `ExtensionsInner` in-place, dropping any elements in the map but
/// retaining allocated capacity.
///
/// This permits the hash map allocation to be pooled by the registry so
/// that future spans will not need to allocate new hashmaps.
#[cfg(any(test, feature = "registry"))]
pub(crate) fn clear(&mut self) {
self.map.clear();
}
}
impl fmt::Debug for ExtensionsInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Extensions")
.field("len", &self.map.len())
.field("capacity", &self.map.capacity())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct MyType(i32);
#[test]
fn test_extensions() {
let mut extensions = ExtensionsInner::new();
extensions.insert(5i32);
extensions.insert(MyType(10));
assert_eq!(extensions.get(), Some(&5i32));
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
assert_eq!(extensions.remove::<i32>(), Some(5i32));
assert!(extensions.get::<i32>().is_none());
assert_eq!(extensions.get::<bool>(), None);
assert_eq!(extensions.get(), Some(&MyType(10)));
}
#[test]
fn clear_retains_capacity() {
let mut extensions = ExtensionsInner::new();
extensions.insert(5i32);
extensions.insert(MyType(10));
extensions.insert(true);
assert_eq!(extensions.map.len(), 3);
let prev_capacity = extensions.map.capacity();
extensions.clear();
assert_eq!(
extensions.map.len(),
0,
"after clear(), extensions map should have length 0"
);
assert_eq!(
extensions.map.capacity(),
prev_capacity,
"after clear(), extensions map should retain prior capacity"
);
}
#[test]
fn clear_drops_elements() {
use std::sync::Arc;
struct DropMePlease(Arc<()>);
struct DropMeTooPlease(Arc<()>);
let mut extensions = ExtensionsInner::new();
let val1 = DropMePlease(Arc::new(()));
let val2 = DropMeTooPlease(Arc::new(()));
let val1_dropped = Arc::downgrade(&val1.0);
let val2_dropped = Arc::downgrade(&val2.0);
extensions.insert(val1);
extensions.insert(val2);
assert!(val1_dropped.upgrade().is_some());
assert!(val2_dropped.upgrade().is_some());
extensions.clear();
assert!(
val1_dropped.upgrade().is_none(),
"after clear(), val1 should be dropped"
);
assert!(
val2_dropped.upgrade().is_none(),
"after clear(), val2 should be dropped"
);
}
}

View File

@@ -0,0 +1,598 @@
//! Storage for span data shared by multiple [`Layer`]s.
//!
//! ## Using the Span Registry
//!
//! This module provides the [`Registry`] type, a [`Subscriber`] implementation
//! which tracks per-span data and exposes it to [`Layer`]s. When a `Registry`
//! is used as the base `Subscriber` of a `Layer` stack, the
//! [`layer::Context`][ctx] type will provide methods allowing `Layer`s to
//! [look up span data][lookup] stored in the registry. While [`Registry`] is a
//! reasonable default for storing spans and events, other stores that implement
//! [`LookupSpan`] and [`Subscriber`] themselves (with [`SpanData`] implemented
//! by the per-span data they store) can be used as a drop-in replacement.
//!
//! For example, we might create a `Registry` and add multiple `Layer`s like so:
//! ```rust
//! use tracing_subscriber::{registry::Registry, Layer, prelude::*};
//! # use tracing_core::Subscriber;
//! # pub struct FooLayer {}
//! # pub struct BarLayer {}
//! # impl<S: Subscriber> Layer<S> for FooLayer {}
//! # impl<S: Subscriber> Layer<S> for BarLayer {}
//! # impl FooLayer {
//! # fn new() -> Self { Self {} }
//! # }
//! # impl BarLayer {
//! # fn new() -> Self { Self {} }
//! # }
//!
//! let subscriber = Registry::default()
//! .with(FooLayer::new())
//! .with(BarLayer::new());
//! ```
//!
//! If a type implementing `Layer` depends on the functionality of a `Registry`
//! implementation, it should bound its `Subscriber` type parameter with the
//! [`LookupSpan`] trait, like so:
//!
//! ```rust
//! use tracing_subscriber::{registry, Layer};
//! use tracing_core::Subscriber;
//!
//! pub struct MyLayer {
//! // ...
//! }
//!
//! impl<S> Layer<S> for MyLayer
//! where
//! S: Subscriber + for<'a> registry::LookupSpan<'a>,
//! {
//! // ...
//! }
//! ```
//! When this bound is added, the `Layer` implementation will be guaranteed
//! access to the [`Context`][ctx] methods, such as [`Context::span`][lookup], that
//! require the root subscriber to be a registry.
//!
//! [`Layer`]: crate::layer::Layer
//! [`Subscriber`]: tracing_core::Subscriber
//! [ctx]: crate::layer::Context
//! [lookup]: crate::layer::Context::span()
use tracing_core::{field::FieldSet, span::Id, Metadata};
feature! {
#![feature = "std"]
/// A module containing a type map of span extensions.
mod extensions;
pub use extensions::{Extensions, ExtensionsMut};
}
feature! {
#![all(feature = "registry", feature = "std")]
mod sharded;
mod stack;
pub use sharded::Data;
pub use sharded::Registry;
use crate::filter::FilterId;
}
/// Provides access to stored span data.
///
/// Subscribers which store span data and associate it with span IDs should
/// implement this trait; if they do, any [`Layer`]s wrapping them can look up
/// metadata via the [`Context`] type's [`span()`] method.
///
/// [`Layer`]: super::layer::Layer
/// [`Context`]: super::layer::Context
/// [`span()`]: super::layer::Context::span
pub trait LookupSpan<'a> {
/// The type of span data stored in this registry.
type Data: SpanData<'a>;
/// Returns the [`SpanData`] for a given `Id`, if it exists.
///
/// <pre class="ignore" style="white-space:normal;font:inherit;">
/// <strong>Note</strong>: users of the <code>LookupSpan</code> trait should
/// typically call the <a href="#method.span"><code>span</code></a> method rather
/// than this method. The <code>span</code> method is implemented by
/// <em>calling</em> <code>span_data</code>, but returns a reference which is
/// capable of performing more sophisiticated queries.
/// </pre>
///
fn span_data(&'a self, id: &Id) -> Option<Self::Data>;
/// Returns a [`SpanRef`] for the span with the given `Id`, if it exists.
///
/// A `SpanRef` is similar to [`SpanData`], but it allows performing
/// additional lookups against the registryr that stores the wrapped data.
///
/// In general, _users_ of the `LookupSpan` trait should use this method
/// rather than the [`span_data`] method; while _implementors_ of this trait
/// should only implement `span_data`.
///
/// [`span_data`]: LookupSpan::span_data()
fn span(&'a self, id: &Id) -> Option<SpanRef<'a, Self>>
where
Self: Sized,
{
let data = self.span_data(id)?;
Some(SpanRef {
registry: self,
data,
#[cfg(feature = "registry")]
filter: FilterId::none(),
})
}
/// Registers a [`Filter`] for [per-layer filtering] with this
/// [`Subscriber`].
///
/// The [`Filter`] can then use the returned [`FilterId`] to
/// [check if it previously enabled a span][check].
///
/// # Panics
///
/// If this `Subscriber` does not support [per-layer filtering].
///
/// [`Filter`]: crate::layer::Filter
/// [per-layer filtering]: crate::layer::Layer#per-layer-filtering
/// [`Subscriber`]: tracing_core::Subscriber
/// [`FilterId`]: crate::filter::FilterId
/// [check]: SpanData::is_enabled_for
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
fn register_filter(&mut self) -> FilterId {
panic!(
"{} does not currently support filters",
std::any::type_name::<Self>()
)
}
}
/// A stored representation of data associated with a span.
pub trait SpanData<'a> {
/// Returns this span's ID.
fn id(&self) -> Id;
/// Returns a reference to the span's `Metadata`.
fn metadata(&self) -> &'static Metadata<'static>;
/// Returns a reference to the ID
fn parent(&self) -> Option<&Id>;
/// Returns a reference to this span's `Extensions`.
///
/// The extensions may be used by `Layer`s to store additional data
/// describing the span.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn extensions(&self) -> Extensions<'_>;
/// Returns a mutable reference to this span's `Extensions`.
///
/// The extensions may be used by `Layer`s to store additional data
/// describing the span.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn extensions_mut(&self) -> ExtensionsMut<'_>;
/// Returns `true` if this span is enabled for the [per-layer filter][plf]
/// corresponding to the provided [`FilterId`].
///
/// ## Default Implementation
///
/// By default, this method assumes that the [`LookupSpan`] implementation
/// does not support [per-layer filtering][plf], and always returns `true`.
///
/// [plf]: crate::layer::Layer#per-layer-filtering
/// [`FilterId`]: crate::filter::FilterId
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
fn is_enabled_for(&self, filter: FilterId) -> bool {
let _ = filter;
true
}
}
/// A reference to [span data] and the associated [registry].
///
/// This type implements all the same methods as [`SpanData`], and provides
/// additional methods for querying the registry based on values from the span.
///
/// [registry]: LookupSpan
#[derive(Debug)]
pub struct SpanRef<'a, R: LookupSpan<'a>> {
registry: &'a R,
data: R::Data,
#[cfg(feature = "registry")]
filter: FilterId,
}
/// An iterator over the parents of a span, ordered from leaf to root.
///
/// This is returned by the [`SpanRef::scope`] method.
#[derive(Debug)]
pub struct Scope<'a, R> {
registry: &'a R,
next: Option<Id>,
#[cfg(all(feature = "registry", feature = "std"))]
filter: FilterId,
}
feature! {
#![any(feature = "alloc", feature = "std")]
#[cfg(not(feature = "smallvec"))]
use alloc::vec::{self, Vec};
use core::{fmt,iter};
/// An iterator over the parents of a span, ordered from root to leaf.
///
/// This is returned by the [`Scope::from_root`] method.
pub struct ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
#[cfg(feature = "smallvec")]
spans: iter::Rev<smallvec::IntoIter<SpanRefVecArray<'a, R>>>,
#[cfg(not(feature = "smallvec"))]
spans: iter::Rev<vec::IntoIter<SpanRef<'a, R>>>,
}
#[cfg(feature = "smallvec")]
type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
impl<'a, R> Scope<'a, R>
where
R: LookupSpan<'a>,
{
/// Flips the order of the iterator, so that it is ordered from root to leaf.
///
/// The iterator will first return the root span, then that span's immediate child,
/// and so on until it finally returns the span that [`SpanRef::scope`] was called on.
///
/// If any items were consumed from the [`Scope`] before calling this method then they
/// will *not* be returned from the [`ScopeFromRoot`].
///
/// **Note**: this will allocate if there are many spans remaining, or if the
/// "smallvec" feature flag is not enabled.
#[allow(clippy::wrong_self_convention)]
pub fn from_root(self) -> ScopeFromRoot<'a, R> {
#[cfg(feature = "smallvec")]
type Buf<T> = smallvec::SmallVec<T>;
#[cfg(not(feature = "smallvec"))]
type Buf<T> = Vec<T>;
ScopeFromRoot {
spans: self.collect::<Buf<_>>().into_iter().rev(),
}
}
}
impl<'a, R> Iterator for ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.spans.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.spans.size_hint()
}
}
impl<'a, R> fmt::Debug for ScopeFromRoot<'a, R>
where
R: LookupSpan<'a>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("ScopeFromRoot { .. }")
}
}
}
impl<'a, R> Iterator for Scope<'a, R>
where
R: LookupSpan<'a>,
{
type Item = SpanRef<'a, R>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let curr = self.registry.span(self.next.as_ref()?)?;
#[cfg(all(feature = "registry", feature = "std"))]
let curr = curr.with_filter(self.filter);
self.next = curr.data.parent().cloned();
// If the `Scope` is filtered, check if the current span is enabled
// by the selected filter ID.
#[cfg(all(feature = "registry", feature = "std"))]
{
if !curr.is_enabled_for(self.filter) {
// The current span in the chain is disabled for this
// filter. Try its parent.
continue;
}
}
return Some(curr);
}
}
}
impl<'a, R> SpanRef<'a, R>
where
R: LookupSpan<'a>,
{
/// Returns this span's ID.
pub fn id(&self) -> Id {
self.data.id()
}
/// Returns a static reference to the span's metadata.
pub fn metadata(&self) -> &'static Metadata<'static> {
self.data.metadata()
}
/// Returns the span's name,
pub fn name(&self) -> &'static str {
self.data.metadata().name()
}
/// Returns a list of [fields] defined by the span.
///
/// [fields]: tracing_core::field
pub fn fields(&self) -> &FieldSet {
self.data.metadata().fields()
}
/// Returns a `SpanRef` describing this span's parent, or `None` if this
/// span is the root of its trace tree.
pub fn parent(&self) -> Option<Self> {
let id = self.data.parent()?;
let data = self.registry.span_data(id)?;
#[cfg(all(feature = "registry", feature = "std"))]
{
// move these into mut bindings if the registry feature is enabled,
// since they may be mutated in the loop.
let mut data = data;
loop {
// Is this parent enabled by our filter?
if data.is_enabled_for(self.filter) {
return Some(Self {
registry: self.registry,
filter: self.filter,
data,
});
}
// It's not enabled. If the disabled span has a parent, try that!
let id = data.parent()?;
data = self.registry.span_data(id)?;
}
}
#[cfg(not(all(feature = "registry", feature = "std")))]
Some(Self {
registry: self.registry,
data,
})
}
/// Returns an iterator over all parents of this span, starting with this span,
/// ordered from leaf to root.
///
/// The iterator will first return the span, then the span's immediate parent,
/// followed by that span's parent, and so on, until it reaches a root span.
///
/// ```rust
/// use tracing::{span, Subscriber};
/// use tracing_subscriber::{
/// layer::{Context, Layer},
/// prelude::*,
/// registry::LookupSpan,
/// };
///
/// struct PrintingLayer;
/// impl<S> Layer<S> for PrintingLayer
/// where
/// S: Subscriber + for<'lookup> LookupSpan<'lookup>,
/// {
/// fn on_enter(&self, id: &span::Id, ctx: Context<S>) {
/// let span = ctx.span(id).unwrap();
/// let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
/// println!("Entering span: {:?}", scope);
/// }
/// }
///
/// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || {
/// let _root = tracing::info_span!("root").entered();
/// // Prints: Entering span: ["root"]
/// let _child = tracing::info_span!("child").entered();
/// // Prints: Entering span: ["child", "root"]
/// let _leaf = tracing::info_span!("leaf").entered();
/// // Prints: Entering span: ["leaf", "child", "root"]
/// });
/// ```
///
/// If the opposite order (from the root to this span) is desired, calling [`Scope::from_root`] on
/// the returned iterator reverses the order.
///
/// ```rust
/// # use tracing::{span, Subscriber};
/// # use tracing_subscriber::{
/// # layer::{Context, Layer},
/// # prelude::*,
/// # registry::LookupSpan,
/// # };
/// # struct PrintingLayer;
/// impl<S> Layer<S> for PrintingLayer
/// where
/// S: Subscriber + for<'lookup> LookupSpan<'lookup>,
/// {
/// fn on_enter(&self, id: &span::Id, ctx: Context<S>) {
/// let span = ctx.span(id).unwrap();
/// let scope = span.scope().from_root().map(|span| span.name()).collect::<Vec<_>>();
/// println!("Entering span: {:?}", scope);
/// }
/// }
///
/// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || {
/// let _root = tracing::info_span!("root").entered();
/// // Prints: Entering span: ["root"]
/// let _child = tracing::info_span!("child").entered();
/// // Prints: Entering span: ["root", "child"]
/// let _leaf = tracing::info_span!("leaf").entered();
/// // Prints: Entering span: ["root", "child", "leaf"]
/// });
/// ```
pub fn scope(&self) -> Scope<'a, R> {
Scope {
registry: self.registry,
next: Some(self.id()),
#[cfg(feature = "registry")]
filter: self.filter,
}
}
/// Returns a reference to this span's `Extensions`.
///
/// The extensions may be used by `Layer`s to store additional data
/// describing the span.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn extensions(&self) -> Extensions<'_> {
self.data.extensions()
}
/// Returns a mutable reference to this span's `Extensions`.
///
/// The extensions may be used by `Layer`s to store additional data
/// describing the span.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn extensions_mut(&self) -> ExtensionsMut<'_> {
self.data.extensions_mut()
}
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn try_with_filter(self, filter: FilterId) -> Option<Self> {
if self.is_enabled_for(filter) {
return Some(self.with_filter(filter));
}
None
}
#[inline]
#[cfg(all(feature = "registry", feature = "std"))]
pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool {
self.data.is_enabled_for(filter)
}
#[inline]
#[cfg(all(feature = "registry", feature = "std"))]
fn with_filter(self, filter: FilterId) -> Self {
Self { filter, ..self }
}
}
#[cfg(all(test, feature = "registry", feature = "std"))]
mod tests {
use crate::{
layer::{Context, Layer},
prelude::*,
registry::LookupSpan,
};
use std::sync::{Arc, Mutex};
use tracing::{span, Subscriber};
#[test]
fn spanref_scope_iteration_order() {
let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
#[derive(Default)]
struct PrintingLayer {
last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
}
impl<S> Layer<S> for PrintingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
let scope = span.scope().map(|span| span.name()).collect::<Vec<_>>();
*self.last_entered_scope.lock().unwrap() = scope;
}
}
let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer {
last_entered_scope: last_entered_scope.clone(),
}));
let _root = tracing::info_span!("root").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
let _child = tracing::info_span!("child").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]);
let _leaf = tracing::info_span!("leaf").entered();
assert_eq!(
&*last_entered_scope.lock().unwrap(),
&["leaf", "child", "root"]
);
}
#[test]
fn spanref_scope_fromroot_iteration_order() {
let last_entered_scope = Arc::new(Mutex::new(Vec::new()));
#[derive(Default)]
struct PrintingLayer {
last_entered_scope: Arc<Mutex<Vec<&'static str>>>,
}
impl<S> Layer<S> for PrintingLayer
where
S: Subscriber + for<'lookup> LookupSpan<'lookup>,
{
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).unwrap();
let scope = span
.scope()
.from_root()
.map(|span| span.name())
.collect::<Vec<_>>();
*self.last_entered_scope.lock().unwrap() = scope;
}
}
let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer {
last_entered_scope: last_entered_scope.clone(),
}));
let _root = tracing::info_span!("root").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]);
let _child = tracing::info_span!("child").entered();
assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]);
let _leaf = tracing::info_span!("leaf").entered();
assert_eq!(
&*last_entered_scope.lock().unwrap(),
&["root", "child", "leaf"]
);
}
}

View File

@@ -0,0 +1,905 @@
use sharded_slab::{pool::Ref, Clear, Pool};
use thread_local::ThreadLocal;
use super::stack::SpanStack;
use crate::{
filter::{FilterId, FilterMap, FilterState},
registry::{
extensions::{Extensions, ExtensionsInner, ExtensionsMut},
LookupSpan, SpanData,
},
sync::RwLock,
};
use std::{
cell::{self, Cell, RefCell},
sync::atomic::{fence, AtomicUsize, Ordering},
};
use tracing_core::{
dispatcher::{self, Dispatch},
span::{self, Current, Id},
Event, Interest, Metadata, Subscriber,
};
/// A shared, reusable store for spans.
///
/// A `Registry` is a [`Subscriber`] around which multiple [`Layer`]s
/// implementing various behaviors may be [added]. Unlike other types
/// implementing `Subscriber`, `Registry` does not actually record traces itself:
/// instead, it collects and stores span data that is exposed to any [`Layer`]s
/// wrapping it through implementations of the [`LookupSpan`] trait.
/// The `Registry` is responsible for storing span metadata, recording
/// relationships between spans, and tracking which spans are active and which
/// are closed. In addition, it provides a mechanism for [`Layer`]s to store
/// user-defined per-span data, called [extensions], in the registry. This
/// allows [`Layer`]-specific data to benefit from the `Registry`'s
/// high-performance concurrent storage.
///
/// This registry is implemented using a [lock-free sharded slab][slab], and is
/// highly optimized for concurrent access.
///
/// # Span ID Generation
///
/// Span IDs are not globally unique, but the registry ensures that
/// no two currently active spans have the same ID within a process.
///
/// One of the primary responsibilities of the registry is to generate [span
/// IDs]. Therefore, it's important for other code that interacts with the
/// registry, such as [`Layer`]s, to understand the guarantees of the
/// span IDs that are generated.
///
/// The registry's span IDs are guaranteed to be unique **at a given point
/// in time**. This means that an active span will never be assigned the
/// same ID as another **currently active** span. However, the registry
/// **will** eventually reuse the IDs of [closed] spans, although an ID
/// will never be reassigned immediately after a span has closed.
///
/// Spans are not [considered closed] by the `Registry` until *every*
/// [`Span`] reference with that ID has been dropped.
///
/// Thus: span IDs generated by the registry should be considered unique
/// only at a given point in time, and only relative to other spans
/// generated by the same process. Two spans with the same ID will not exist
/// in the same process concurrently. However, if historical span data is
/// being stored, the same ID may occur for multiple spans times in that
/// data. If spans must be uniquely identified in historical data, the user
/// code storing this data must assign its own unique identifiers to those
/// spans. A counter is generally sufficient for this.
///
/// Similarly, span IDs generated by the registry are not unique outside of
/// a given process. Distributed tracing systems may require identifiers
/// that are unique across multiple processes on multiple machines (for
/// example, [OpenTelemetry's `SpanId`s and `TraceId`s][ot]). `tracing` span
/// IDs generated by the registry should **not** be used for this purpose.
/// Instead, code which integrates with a distributed tracing system should
/// generate and propagate its own IDs according to the rules specified by
/// the distributed tracing system. These IDs can be associated with
/// `tracing` spans using [fields] and/or [stored span data].
///
/// [span IDs]: tracing_core::span::Id
/// [slab]: sharded_slab
/// [`Layer`]: crate::Layer
/// [added]: crate::layer::Layer#composing-layers
/// [extensions]: super::Extensions
/// [closed]: https://docs.rs/tracing/latest/tracing/span/index.html#closing-spans
/// [considered closed]: tracing_core::subscriber::Subscriber::try_close()
/// [`Span`]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html
/// [ot]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spancontext
/// [fields]: tracing_core::field
/// [stored span data]: crate::registry::SpanData::extensions_mut
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
#[derive(Debug)]
pub struct Registry {
spans: Pool<DataInner>,
current_spans: ThreadLocal<RefCell<SpanStack>>,
next_filter_id: u8,
}
/// Span data stored in a [`Registry`].
///
/// The registry stores well-known data defined by tracing: span relationships,
/// metadata and reference counts. Additional user-defined data provided by
/// [`Layer`s], such as formatted fields, metrics, or distributed traces should
/// be stored in the [extensions] typemap.
///
/// [`Layer`s]: crate::layer::Layer
/// [extensions]: Extensions
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
#[derive(Debug)]
pub struct Data<'a> {
/// Immutable reference to the pooled `DataInner` entry.
inner: Ref<'a, DataInner>,
}
/// Stored data associated with a span.
///
/// This type is pooled using [`sharded_slab::Pool`]; when a span is
/// dropped, the `DataInner` entry at that span's slab index is cleared
/// in place and reused by a future span. Thus, the `Default` and
/// [`sharded_slab::Clear`] implementations for this type are
/// load-bearing.
#[derive(Debug)]
struct DataInner {
filter_map: FilterMap,
metadata: &'static Metadata<'static>,
parent: Option<Id>,
ref_count: AtomicUsize,
// The span's `Extensions` typemap. Allocations for the `HashMap` backing
// this are pooled and reused in place.
pub(crate) extensions: RwLock<ExtensionsInner>,
}
// === impl Registry ===
impl Default for Registry {
fn default() -> Self {
Self {
spans: Pool::new(),
current_spans: ThreadLocal::new(),
next_filter_id: 0,
}
}
}
#[inline]
fn idx_to_id(idx: usize) -> Id {
Id::from_u64(idx as u64 + 1)
}
#[inline]
fn id_to_idx(id: &Id) -> usize {
id.into_u64() as usize - 1
}
/// A guard that tracks how many [`Registry`]-backed `Layer`s have
/// processed an `on_close` event.
///
/// This is needed to enable a [`Registry`]-backed Layer to access span
/// data after the `Layer` has recieved the `on_close` callback.
///
/// Once all `Layer`s have processed this event, the [`Registry`] knows
/// that is able to safely remove the span tracked by `id`. `CloseGuard`
/// accomplishes this through a two-step process:
/// 1. Whenever a [`Registry`]-backed `Layer::on_close` method is
/// called, `Registry::start_close` is closed.
/// `Registry::start_close` increments a thread-local `CLOSE_COUNT`
/// by 1 and returns a `CloseGuard`.
/// 2. The `CloseGuard` is dropped at the end of `Layer::on_close`. On
/// drop, `CloseGuard` checks thread-local `CLOSE_COUNT`. If
/// `CLOSE_COUNT` is 0, the `CloseGuard` removes the span with the
/// `id` from the registry, as all `Layers` that might have seen the
/// `on_close` notification have processed it. If `CLOSE_COUNT` is
/// greater than 0, `CloseGuard` decrements the counter by one and
/// _does not_ remove the span from the [`Registry`].
///
pub(crate) struct CloseGuard<'a> {
id: Id,
registry: &'a Registry,
is_closing: bool,
}
impl Registry {
fn get(&self, id: &Id) -> Option<Ref<'_, DataInner>> {
self.spans.get(id_to_idx(id))
}
/// Returns a guard which tracks how many `Layer`s have
/// processed an `on_close` notification via the `CLOSE_COUNT` thread-local.
/// For additional details, see [`CloseGuard`].
///
pub(crate) fn start_close(&self, id: Id) -> CloseGuard<'_> {
CLOSE_COUNT.with(|count| {
let c = count.get();
count.set(c + 1);
});
CloseGuard {
id,
registry: self,
is_closing: false,
}
}
pub(crate) fn has_per_layer_filters(&self) -> bool {
self.next_filter_id > 0
}
pub(crate) fn span_stack(&self) -> cell::Ref<'_, SpanStack> {
self.current_spans.get_or_default().borrow()
}
}
thread_local! {
/// `CLOSE_COUNT` is the thread-local counter used by `CloseGuard` to
/// track how many layers have processed the close.
/// For additional details, see [`CloseGuard`].
///
static CLOSE_COUNT: Cell<usize> = const { Cell::new(0) };
}
impl Subscriber for Registry {
fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest {
if self.has_per_layer_filters() {
return FilterState::take_interest().unwrap_or_else(Interest::always);
}
Interest::always()
}
fn enabled(&self, _: &Metadata<'_>) -> bool {
if self.has_per_layer_filters() {
return FilterState::event_enabled();
}
true
}
#[inline]
fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id {
let parent = if attrs.is_root() {
None
} else if attrs.is_contextual() {
self.current_span().id().map(|id| self.clone_span(id))
} else {
attrs.parent().map(|id| self.clone_span(id))
};
let id = self
.spans
// Check out a `DataInner` entry from the pool for the new span. If
// there are free entries already allocated in the pool, this will
// preferentially reuse one; otherwise, a new `DataInner` is
// allocated and added to the pool.
.create_with(|data| {
data.metadata = attrs.metadata();
data.parent = parent;
data.filter_map = crate::filter::FILTERING.with(|filtering| filtering.filter_map());
#[cfg(debug_assertions)]
{
if data.filter_map != FilterMap::new() {
debug_assert!(self.has_per_layer_filters());
}
}
let refs = data.ref_count.get_mut();
debug_assert_eq!(*refs, 0);
*refs = 1;
})
.expect("Unable to allocate another span");
idx_to_id(id)
}
/// This is intentionally not implemented, as recording fields
/// on a span is the responsibility of layers atop of this registry.
#[inline]
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
fn event_enabled(&self, _event: &Event<'_>) -> bool {
if self.has_per_layer_filters() {
return FilterState::event_enabled();
}
true
}
/// This is intentionally not implemented, as recording events
/// is the responsibility of layers atop of this registry.
fn event(&self, _: &Event<'_>) {}
fn enter(&self, id: &span::Id) {
if self
.current_spans
.get_or_default()
.borrow_mut()
.push(id.clone())
{
self.clone_span(id);
}
}
fn exit(&self, id: &span::Id) {
if let Some(spans) = self.current_spans.get() {
if spans.borrow_mut().pop(id) {
dispatcher::get_default(|dispatch| dispatch.try_close(id.clone()));
}
}
}
fn clone_span(&self, id: &span::Id) -> span::Id {
let span = self
.get(id)
.unwrap_or_else(|| panic!(
"tried to clone {:?}, but no span exists with that ID\n\
This may be caused by consuming a parent span (`parent: span`) rather than borrowing it (`parent: &span`).",
id,
));
// Like `std::sync::Arc`, adds to the ref count (on clone) don't require
// a strong ordering; if we call` clone_span`, the reference count must
// always at least 1. The only synchronization necessary is between
// calls to `try_close`: we have to ensure that all threads have
// dropped their refs to the span before the span is closed.
let refs = span.ref_count.fetch_add(1, Ordering::Relaxed);
assert_ne!(
refs, 0,
"tried to clone a span ({:?}) that already closed",
id
);
id.clone()
}
fn current_span(&self) -> Current {
self.current_spans
.get()
.and_then(|spans| {
let spans = spans.borrow();
let id = spans.current()?;
let span = self.get(id)?;
Some(Current::new(id.clone(), span.metadata))
})
.unwrap_or_else(Current::none)
}
/// Decrements the reference count of the span with the given `id`, and
/// removes the span if it is zero.
///
/// The allocated span slot will be reused when a new span is created.
fn try_close(&self, id: span::Id) -> bool {
let span = match self.get(&id) {
Some(span) => span,
None if std::thread::panicking() => return false,
None => panic!("tried to drop a ref to {:?}, but no such span exists!", id),
};
let refs = span.ref_count.fetch_sub(1, Ordering::Release);
if !std::thread::panicking() {
assert!(refs < usize::MAX, "reference count overflow!");
}
if refs > 1 {
return false;
}
// Synchronize if we are actually removing the span (stolen
// from std::Arc); this ensures that all other `try_close` calls on
// other threads happen-before we actually remove the span.
fence(Ordering::Acquire);
true
}
}
impl<'a> LookupSpan<'a> for Registry {
type Data = Data<'a>;
fn span_data(&'a self, id: &Id) -> Option<Self::Data> {
let inner = self.get(id)?;
Some(Data { inner })
}
fn register_filter(&mut self) -> FilterId {
let id = FilterId::new(self.next_filter_id);
self.next_filter_id += 1;
id
}
}
// === impl CloseGuard ===
impl CloseGuard<'_> {
pub(crate) fn set_closing(&mut self) {
self.is_closing = true;
}
}
impl Drop for CloseGuard<'_> {
fn drop(&mut self) {
// If this returns with an error, we are already panicking. At
// this point, there's nothing we can really do to recover
// except by avoiding a double-panic.
let _ = CLOSE_COUNT.try_with(|count| {
let c = count.get();
// Decrement the count to indicate that _this_ guard's
// `on_close` callback has completed.
//
// Note that we *must* do this before we actually remove the span
// from the registry, since dropping the `DataInner` may trigger a
// new close, if this span is the last reference to a parent span.
count.set(c - 1);
// If the current close count is 1, this stack frame is the last
// `on_close` call. If the span is closing, it's okay to remove the
// span.
if c == 1 && self.is_closing {
self.registry.spans.clear(id_to_idx(&self.id));
}
});
}
}
// === impl Data ===
impl<'a> SpanData<'a> for Data<'a> {
fn id(&self) -> Id {
idx_to_id(self.inner.key())
}
fn metadata(&self) -> &'static Metadata<'static> {
self.inner.metadata
}
fn parent(&self) -> Option<&Id> {
self.inner.parent.as_ref()
}
fn extensions(&self) -> Extensions<'_> {
Extensions::new(self.inner.extensions.read().expect("Mutex poisoned"))
}
fn extensions_mut(&self) -> ExtensionsMut<'_> {
ExtensionsMut::new(self.inner.extensions.write().expect("Mutex poisoned"))
}
#[inline]
fn is_enabled_for(&self, filter: FilterId) -> bool {
self.inner.filter_map.is_enabled(filter)
}
}
// === impl DataInner ===
impl Default for DataInner {
fn default() -> Self {
// Since `DataInner` owns a `&'static Callsite` pointer, we need
// something to use as the initial default value for that callsite.
// Since we can't access a `DataInner` until it has had actual span data
// inserted into it, the null metadata will never actually be accessed.
struct NullCallsite;
impl tracing_core::callsite::Callsite for NullCallsite {
fn set_interest(&self, _: Interest) {
unreachable!(
"/!\\ Tried to register the null callsite /!\\\n \
This should never have happened and is definitely a bug. \
A `tracing` bug report would be appreciated."
)
}
fn metadata(&self) -> &Metadata<'_> {
unreachable!(
"/!\\ Tried to access the null callsite's metadata /!\\\n \
This should never have happened and is definitely a bug. \
A `tracing` bug report would be appreciated."
)
}
}
static NULL_CALLSITE: NullCallsite = NullCallsite;
static NULL_METADATA: Metadata<'static> = tracing_core::metadata! {
name: "",
target: "",
level: tracing_core::Level::TRACE,
fields: &[],
callsite: &NULL_CALLSITE,
kind: tracing_core::metadata::Kind::SPAN,
};
Self {
filter_map: FilterMap::new(),
metadata: &NULL_METADATA,
parent: None,
ref_count: AtomicUsize::new(0),
extensions: RwLock::new(ExtensionsInner::new()),
}
}
}
impl Clear for DataInner {
/// Clears the span's data in place, dropping the parent's reference count.
fn clear(&mut self) {
// A span is not considered closed until all of its children have closed.
// Therefore, each span's `DataInner` holds a "reference" to the parent
// span, keeping the parent span open until all its children have closed.
// When we close a span, we must then decrement the parent's ref count
// (potentially, allowing it to close, if this child is the last reference
// to that span).
// We have to actually unpack the option inside the `get_default`
// closure, since it is a `FnMut`, but testing that there _is_ a value
// here lets us avoid the thread-local access if we don't need the
// dispatcher at all.
if self.parent.is_some() {
// Note that --- because `Layered::try_close` works by calling
// `try_close` on the inner subscriber and using the return value to
// determine whether to call the `Layer`'s `on_close` callback ---
// we must call `try_close` on the entire subscriber stack, rather
// than just on the registry. If the registry called `try_close` on
// itself directly, the layers wouldn't see the close notification.
let subscriber = dispatcher::get_default(Dispatch::clone);
if let Some(parent) = self.parent.take() {
let _ = subscriber.try_close(parent);
}
}
// Clear (but do not deallocate!) the pooled `HashMap` for the span's extensions.
self.extensions
.get_mut()
.unwrap_or_else(|l| {
// This function can be called in a `Drop` impl, such as while
// panicking, so ignore lock poisoning.
l.into_inner()
})
.clear();
self.filter_map = FilterMap::new();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{layer::Context, registry::LookupSpan, Layer};
use std::{
collections::HashMap,
sync::{Arc, Mutex, Weak},
};
use tracing::{self, subscriber::with_default};
use tracing_core::{
dispatcher,
span::{Attributes, Id},
Subscriber,
};
struct AssertionLayer;
impl<S> Layer<S> for AssertionLayer
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
dbg!(format_args!("closing {:?}", id));
assert!(&ctx.span(&id).is_some());
}
}
#[test]
fn single_layer_can_access_closed_span() {
let subscriber = AssertionLayer.with_subscriber(Registry::default());
with_default(subscriber, || {
let span = tracing::debug_span!("span");
drop(span);
});
}
#[test]
fn multiple_layers_can_access_closed_span() {
let subscriber = AssertionLayer
.and_then(AssertionLayer)
.with_subscriber(Registry::default());
with_default(subscriber, || {
let span = tracing::debug_span!("span");
drop(span);
});
}
struct CloseLayer {
inner: Arc<Mutex<CloseState>>,
}
struct CloseHandle {
state: Arc<Mutex<CloseState>>,
}
#[derive(Default)]
struct CloseState {
open: HashMap<&'static str, Weak<()>>,
closed: Vec<(&'static str, Weak<()>)>,
}
#[allow(dead_code)] // Field is exercised via checking `Arc::downgrade()`
struct SetRemoved(Arc<()>);
impl<S> Layer<S> for CloseLayer
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_new_span(&self, _: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Missing span; this is a bug");
let mut lock = self.inner.lock().unwrap();
let is_removed = Arc::new(());
assert!(
lock.open
.insert(span.name(), Arc::downgrade(&is_removed))
.is_none(),
"test layer saw multiple spans with the same name, the test is probably messed up"
);
let mut extensions = span.extensions_mut();
extensions.insert(SetRemoved(is_removed));
}
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
let span = if let Some(span) = ctx.span(&id) {
span
} else {
println!(
"span {:?} did not exist in `on_close`, are we panicking?",
id
);
return;
};
let name = span.name();
println!("close {} ({:?})", name, id);
if let Ok(mut lock) = self.inner.lock() {
if let Some(is_removed) = lock.open.remove(name) {
assert!(is_removed.upgrade().is_some());
lock.closed.push((name, is_removed));
}
}
}
}
impl CloseLayer {
fn new() -> (Self, CloseHandle) {
let state = Arc::new(Mutex::new(CloseState::default()));
(
Self {
inner: state.clone(),
},
CloseHandle { state },
)
}
}
impl CloseState {
fn is_open(&self, span: &str) -> bool {
self.open.contains_key(span)
}
fn is_closed(&self, span: &str) -> bool {
self.closed.iter().any(|(name, _)| name == &span)
}
}
impl CloseHandle {
fn assert_closed(&self, span: &str) {
let lock = self.state.lock().unwrap();
assert!(
lock.is_closed(span),
"expected {} to be closed{}",
span,
if lock.is_open(span) {
" (it was still open)"
} else {
", but it never existed (is there a problem with the test?)"
}
)
}
fn assert_open(&self, span: &str) {
let lock = self.state.lock().unwrap();
assert!(
lock.is_open(span),
"expected {} to be open{}",
span,
if lock.is_closed(span) {
" (it was still open)"
} else {
", but it never existed (is there a problem with the test?)"
}
)
}
fn assert_removed(&self, span: &str) {
let lock = self.state.lock().unwrap();
let is_removed = match lock.closed.iter().find(|(name, _)| name == &span) {
Some((_, is_removed)) => is_removed,
None => panic!(
"expected {} to be removed from the registry, but it was not closed {}",
span,
if lock.is_closed(span) {
" (it was still open)"
} else {
", but it never existed (is there a problem with the test?)"
}
),
};
assert!(
is_removed.upgrade().is_none(),
"expected {} to have been removed from the registry",
span
)
}
fn assert_not_removed(&self, span: &str) {
let lock = self.state.lock().unwrap();
let is_removed = match lock.closed.iter().find(|(name, _)| name == &span) {
Some((_, is_removed)) => is_removed,
None if lock.is_open(span) => return,
None => unreachable!(),
};
assert!(
is_removed.upgrade().is_some(),
"expected {} to have been removed from the registry",
span
)
}
#[allow(unused)] // may want this for future tests
fn assert_last_closed(&self, span: Option<&str>) {
let lock = self.state.lock().unwrap();
let last = lock.closed.last().map(|(span, _)| span);
assert_eq!(
last,
span.as_ref(),
"expected {:?} to have closed last",
span
);
}
fn assert_closed_in_order(&self, order: impl AsRef<[&'static str]>) {
let lock = self.state.lock().unwrap();
let order = order.as_ref();
for (i, name) in order.iter().enumerate() {
assert_eq!(
lock.closed.get(i).map(|(span, _)| span),
Some(name),
"expected close order: {:?}, actual: {:?}",
order,
lock.closed.iter().map(|(name, _)| name).collect::<Vec<_>>()
);
}
}
}
#[test]
fn spans_are_removed_from_registry() {
let (close_layer, state) = CloseLayer::new();
let subscriber = AssertionLayer
.and_then(close_layer)
.with_subscriber(Registry::default());
// Create a `Dispatch` (which is internally reference counted) so that
// the subscriber lives to the end of the test. Otherwise, if we just
// passed the subscriber itself to `with_default`, we could see the span
// be dropped when the subscriber itself is dropped, destroying the
// registry.
let dispatch = dispatcher::Dispatch::new(subscriber);
dispatcher::with_default(&dispatch, || {
let span = tracing::debug_span!("span1");
drop(span);
let span = tracing::info_span!("span2");
drop(span);
});
state.assert_removed("span1");
state.assert_removed("span2");
// Ensure the registry itself outlives the span.
drop(dispatch);
}
#[test]
fn spans_are_only_closed_when_the_last_ref_drops() {
let (close_layer, state) = CloseLayer::new();
let subscriber = AssertionLayer
.and_then(close_layer)
.with_subscriber(Registry::default());
// Create a `Dispatch` (which is internally reference counted) so that
// the subscriber lives to the end of the test. Otherwise, if we just
// passed the subscriber itself to `with_default`, we could see the span
// be dropped when the subscriber itself is dropped, destroying the
// registry.
let dispatch = dispatcher::Dispatch::new(subscriber);
let span2 = dispatcher::with_default(&dispatch, || {
let span = tracing::debug_span!("span1");
drop(span);
let span2 = tracing::info_span!("span2");
let span2_clone = span2.clone();
drop(span2);
span2_clone
});
state.assert_removed("span1");
state.assert_not_removed("span2");
drop(span2);
state.assert_removed("span1");
// Ensure the registry itself outlives the span.
drop(dispatch);
}
#[test]
fn span_enter_guards_are_dropped_out_of_order() {
let (close_layer, state) = CloseLayer::new();
let subscriber = AssertionLayer
.and_then(close_layer)
.with_subscriber(Registry::default());
// Create a `Dispatch` (which is internally reference counted) so that
// the subscriber lives to the end of the test. Otherwise, if we just
// passed the subscriber itself to `with_default`, we could see the span
// be dropped when the subscriber itself is dropped, destroying the
// registry.
let dispatch = dispatcher::Dispatch::new(subscriber);
dispatcher::with_default(&dispatch, || {
let span1 = tracing::debug_span!("span1");
let span2 = tracing::info_span!("span2");
let enter1 = span1.enter();
let enter2 = span2.enter();
drop(enter1);
drop(span1);
state.assert_removed("span1");
state.assert_not_removed("span2");
drop(enter2);
state.assert_not_removed("span2");
drop(span2);
state.assert_removed("span1");
state.assert_removed("span2");
});
}
#[test]
fn child_closes_parent() {
// This test asserts that if a parent span's handle is dropped before
// a child span's handle, the parent will remain open until child
// closes, and will then be closed.
let (close_layer, state) = CloseLayer::new();
let subscriber = close_layer.with_subscriber(Registry::default());
let dispatch = dispatcher::Dispatch::new(subscriber);
dispatcher::with_default(&dispatch, || {
let span1 = tracing::info_span!("parent");
let span2 = tracing::info_span!(parent: &span1, "child");
state.assert_open("parent");
state.assert_open("child");
drop(span1);
state.assert_open("parent");
state.assert_open("child");
drop(span2);
state.assert_closed("parent");
state.assert_closed("child");
});
}
#[test]
fn child_closes_grandparent() {
// This test asserts that, when a span is kept open by a child which
// is *itself* kept open by a child, closing the grandchild will close
// both the parent *and* the grandparent.
let (close_layer, state) = CloseLayer::new();
let subscriber = close_layer.with_subscriber(Registry::default());
let dispatch = dispatcher::Dispatch::new(subscriber);
dispatcher::with_default(&dispatch, || {
let span1 = tracing::info_span!("grandparent");
let span2 = tracing::info_span!(parent: &span1, "parent");
let span3 = tracing::info_span!(parent: &span2, "child");
state.assert_open("grandparent");
state.assert_open("parent");
state.assert_open("child");
drop(span1);
drop(span2);
state.assert_open("grandparent");
state.assert_open("parent");
state.assert_open("child");
drop(span3);
state.assert_closed_in_order(["child", "parent", "grandparent"]);
});
}
}

View File

@@ -0,0 +1,77 @@
pub(crate) use tracing_core::span::Id;
#[derive(Debug)]
struct ContextId {
id: Id,
duplicate: bool,
}
/// `SpanStack` tracks what spans are currently executing on a thread-local basis.
///
/// A "separate current span" for each thread is a semantic choice, as each span
/// can be executing in a different thread.
#[derive(Debug, Default)]
pub(crate) struct SpanStack {
stack: Vec<ContextId>,
}
impl SpanStack {
#[inline]
pub(super) fn push(&mut self, id: Id) -> bool {
let duplicate = self.stack.iter().any(|i| i.id == id);
self.stack.push(ContextId { id, duplicate });
!duplicate
}
#[inline]
pub(super) fn pop(&mut self, expected_id: &Id) -> bool {
if let Some((idx, _)) = self
.stack
.iter()
.enumerate()
.rev()
.find(|(_, ctx_id)| ctx_id.id == *expected_id)
{
let ContextId { id: _, duplicate } = self.stack.remove(idx);
return !duplicate;
}
false
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = &Id> {
self.stack
.iter()
.rev()
.filter_map(|ContextId { id, duplicate }| if !*duplicate { Some(id) } else { None })
}
#[inline]
pub(crate) fn current(&self) -> Option<&Id> {
self.iter().next()
}
}
#[cfg(test)]
mod tests {
use super::{Id, SpanStack};
#[test]
fn pop_last_span() {
let mut stack = SpanStack::default();
let id = Id::from_u64(1);
stack.push(id.clone());
assert!(stack.pop(&id));
}
#[test]
fn pop_first_span() {
let mut stack = SpanStack::default();
stack.push(Id::from_u64(1));
stack.push(Id::from_u64(2));
let id = Id::from_u64(1);
assert!(stack.pop(&id));
}
}

394
vendor/tracing-subscriber/src/reload.rs vendored Normal file
View File

@@ -0,0 +1,394 @@
//! Wrapper for a `Layer` to allow it to be dynamically reloaded.
//!
//! This module provides a [`Layer` type] implementing the [`Layer` trait] or [`Filter` trait]
//! which wraps another type implementing the corresponding trait. This
//! allows the wrapped type to be replaced with another
//! instance of that type at runtime.
//!
//! This can be used in cases where a subset of `Layer` or `Filter` functionality
//! should be dynamically reconfigured, such as when filtering directives may
//! change at runtime. Note that this layer introduces a (relatively small)
//! amount of overhead, and should thus only be used as needed.
//!
//! # Examples
//!
//! Reloading a [global filtering](crate::layer#global-filtering) layer:
//!
//! ```rust
//! # use tracing::info;
//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
//! let filter = filter::LevelFilter::WARN;
//! let (filter, reload_handle) = reload::Layer::new(filter);
//! tracing_subscriber::registry()
//! .with(filter)
//! .with(fmt::Layer::default())
//! .init();
//! #
//! # // specifying the Registry type is required
//! # let _: &reload::Handle<filter::LevelFilter, tracing_subscriber::Registry> = &reload_handle;
//! #
//! info!("This will be ignored");
//! reload_handle.modify(|filter| *filter = filter::LevelFilter::INFO);
//! info!("This will be logged");
//! ```
//!
//! Reloading a [`Filtered`](crate::filter::Filtered) layer:
//!
//! ```rust
//! # use tracing::info;
//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
//! let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN);
//! let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer);
//! #
//! # // specifying the Registry type is required
//! # let _: &reload::Handle<filter::Filtered<fmt::Layer<tracing_subscriber::Registry>,
//! # filter::LevelFilter, tracing_subscriber::Registry>,tracing_subscriber::Registry>
//! # = &reload_handle;
//! #
//! tracing_subscriber::registry()
//! .with(filtered_layer)
//! .init();
//! info!("This will be ignored");
//! reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO);
//! info!("This will be logged");
//! ```
//!
//! ## Note
//!
//! The [`Layer`] implementation is unable to implement downcasting functionality,
//! so certain [`Layer`] will fail to downcast if wrapped in a `reload::Layer`.
//!
//! If you only want to be able to dynamically change the
//! `Filter` on a layer, prefer wrapping that `Filter` in the `reload::Layer`.
//!
//! [`Filter` trait]: crate::layer::Filter
//! [`Layer` type]: Layer
//! [`Layer` trait]: super::layer::Layer
use crate::layer;
use crate::sync::RwLock;
use core::any::TypeId;
use std::{
error, fmt,
marker::PhantomData,
sync::{Arc, Weak},
};
use tracing_core::{
callsite, span,
subscriber::{Interest, Subscriber},
Dispatch, Event, LevelFilter, Metadata,
};
/// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime.
#[derive(Debug)]
pub struct Layer<L, S> {
// TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may
// eventually wish to replace it with a sharded lock implementation on top
// of our internal `RwLock` wrapper type. If possible, we should profile
// this first to determine if it's necessary.
inner: Arc<RwLock<L>>,
_s: PhantomData<fn(S)>,
}
/// Allows reloading the state of an associated [`Layer`](crate::layer::Layer).
#[derive(Debug)]
pub struct Handle<L, S> {
inner: Weak<RwLock<L>>,
_s: PhantomData<fn(S)>,
}
/// Indicates that an error occurred when reloading a layer.
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
#[derive(Debug)]
enum ErrorKind {
SubscriberGone,
Poisoned,
}
// ===== impl Layer =====
impl<L, S> crate::Layer<S> for Layer<L, S>
where
L: crate::Layer<S> + 'static,
S: Subscriber,
{
fn on_register_dispatch(&self, subscriber: &Dispatch) {
try_lock!(self.inner.read()).on_register_dispatch(subscriber);
}
fn on_layer(&mut self, subscriber: &mut S) {
try_lock!(self.inner.write(), else return).on_layer(subscriber);
}
#[inline]
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
try_lock!(self.inner.read(), else return Interest::sometimes()).register_callsite(metadata)
}
#[inline]
fn enabled(&self, metadata: &Metadata<'_>, ctx: layer::Context<'_, S>) -> bool {
try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
}
#[inline]
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
}
#[inline]
fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_record(span, values, ctx)
}
#[inline]
fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_follows_from(span, follows, ctx)
}
#[inline]
fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool {
try_lock!(self.inner.read(), else return false).event_enabled(event, ctx)
}
#[inline]
fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_event(event, ctx)
}
#[inline]
fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_enter(id, ctx)
}
#[inline]
fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_exit(id, ctx)
}
#[inline]
fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_close(id, ctx)
}
#[inline]
fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_id_change(old, new, ctx)
}
#[inline]
fn max_level_hint(&self) -> Option<LevelFilter> {
try_lock!(self.inner.read(), else return None).max_level_hint()
}
#[doc(hidden)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
// Safety: it is generally unsafe to downcast through a reload, because
// the pointer can be invalidated after the lock is dropped.
// `NoneLayerMarker` is a special case because it
// is never dereferenced.
//
// Additionally, even if the marker type *is* dereferenced (which it
// never will be), the pointer should be valid even if the subscriber
// is reloaded, because all `NoneLayerMarker` pointers that we return
// actually point to the global static singleton `NoneLayerMarker`,
// rather than to a field inside the lock.
if id == TypeId::of::<layer::NoneLayerMarker>() {
return try_lock!(self.inner.read(), else return None).downcast_raw(id);
}
None
}
}
// ===== impl Filter =====
#[cfg(all(feature = "registry", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
impl<S, L> crate::layer::Filter<S> for Layer<L, S>
where
L: crate::layer::Filter<S> + 'static,
S: Subscriber,
{
#[inline]
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata)
}
#[inline]
fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool {
try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
}
#[inline]
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
}
#[inline]
fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_record(span, values, ctx)
}
#[inline]
fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_enter(id, ctx)
}
#[inline]
fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_exit(id, ctx)
}
#[inline]
fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
try_lock!(self.inner.read()).on_close(id, ctx)
}
#[inline]
fn max_level_hint(&self) -> Option<LevelFilter> {
try_lock!(self.inner.read(), else return None).max_level_hint()
}
}
impl<L, S> Layer<L, S> {
/// Wraps the given [`Layer`] or [`Filter`], returning a `reload::Layer`
/// and a `Handle` that allows the inner value to be modified at runtime.
///
/// [`Layer`]: crate::layer::Layer
/// [`Filter`]: crate::layer::Filter
pub fn new(inner: L) -> (Self, Handle<L, S>) {
let this = Self {
inner: Arc::new(RwLock::new(inner)),
_s: PhantomData,
};
let handle = this.handle();
(this, handle)
}
/// Returns a `Handle` that can be used to reload the wrapped [`Layer`] or [`Filter`].
///
/// [`Layer`]: crate::layer::Layer
/// [`Filter`]: crate::layer::Filter
pub fn handle(&self) -> Handle<L, S> {
Handle {
inner: Arc::downgrade(&self.inner),
_s: PhantomData,
}
}
}
// ===== impl Handle =====
impl<L, S> Handle<L, S> {
/// Replace the current [`Layer`] or [`Filter`] with the provided `new_value`.
///
/// [`Handle::reload`] cannot be used with the [`Filtered`] layer; use
/// [`Handle::modify`] instead (see [this issue] for additional details).
///
/// However, if the _only_ the [`Filter`] needs to be modified, use
/// `reload::Layer` to wrap the `Filter` directly.
///
/// [`Layer`]: crate::layer::Layer
/// [`Filter`]: crate::layer::Filter
/// [`Filtered`]: crate::filter::Filtered
///
/// [this issue]: https://github.com/tokio-rs/tracing/issues/1629
pub fn reload(&self, new_value: impl Into<L>) -> Result<(), Error> {
self.modify(|layer| {
*layer = new_value.into();
})
}
/// Invokes a closure with a mutable reference to the current layer or filter,
/// allowing it to be modified in place.
pub fn modify(&self, f: impl FnOnce(&mut L)) -> Result<(), Error> {
let inner = self.inner.upgrade().ok_or(Error {
kind: ErrorKind::SubscriberGone,
})?;
let mut lock = try_lock!(inner.write(), else return Err(Error::poisoned()));
f(&mut *lock);
// Release the lock before rebuilding the interest cache, as that
// function will lock the new layer.
drop(lock);
callsite::rebuild_interest_cache();
// If the `log` crate compatibility feature is in use, set `log`'s max
// level as well, in case the max `tracing` level changed. We do this
// *after* rebuilding the interest cache, as that's when the `tracing`
// max level filter is re-computed.
#[cfg(feature = "tracing-log")]
tracing_log::log::set_max_level(tracing_log::AsLog::as_log(
&crate::filter::LevelFilter::current(),
));
Ok(())
}
/// Returns a clone of the layer or filter's current value if it still exists.
/// Otherwise, if the subscriber has been dropped, returns `None`.
pub fn clone_current(&self) -> Option<L>
where
L: Clone,
{
self.with_current(L::clone).ok()
}
/// Invokes a closure with a borrowed reference to the current layer or filter,
/// returning the result (or an error if the subscriber no longer exists).
pub fn with_current<T>(&self, f: impl FnOnce(&L) -> T) -> Result<T, Error> {
let inner = self.inner.upgrade().ok_or(Error {
kind: ErrorKind::SubscriberGone,
})?;
let inner = try_lock!(inner.read(), else return Err(Error::poisoned()));
Ok(f(&*inner))
}
}
impl<L, S> Clone for Handle<L, S> {
fn clone(&self) -> Self {
Handle {
inner: self.inner.clone(),
_s: PhantomData,
}
}
}
// ===== impl Error =====
impl Error {
fn poisoned() -> Self {
Self {
kind: ErrorKind::Poisoned,
}
}
/// Returns `true` if this error occurred because the layer was poisoned by
/// a panic on another thread.
pub fn is_poisoned(&self) -> bool {
matches!(self.kind, ErrorKind::Poisoned)
}
/// Returns `true` if this error occurred because the `Subscriber`
/// containing the reloadable layer was dropped.
pub fn is_dropped(&self) -> bool {
matches!(self.kind, ErrorKind::SubscriberGone)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self.kind {
ErrorKind::SubscriberGone => "subscriber no longer exists",
ErrorKind::Poisoned => "lock poisoned",
};
f.pad(msg)
}
}
impl error::Error for Error {}

65
vendor/tracing-subscriber/src/sync.rs vendored Normal file
View File

@@ -0,0 +1,65 @@
//! Abstracts over sync primitive implementations.
//!
//! Optionally, we allow the Rust standard library's `RwLock` to be replaced
//! with the `parking_lot` crate's implementation. This may provide improved
//! performance in some cases. However, the `parking_lot` dependency is an
//! opt-in feature flag. Because `parking_lot::RwLock` has a slightly different
//! API than `std::sync::RwLock` (it does not support poisoning on panics), we
//! wrap it with a type that provides the same method signatures. This allows us
//! to transparently swap `parking_lot` in without changing code at the callsite.
#[allow(unused_imports)] // may be used later;
pub(crate) use std::sync::{LockResult, PoisonError, TryLockResult};
#[cfg(not(feature = "parking_lot"))]
pub(crate) use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
#[cfg(feature = "parking_lot")]
pub(crate) use self::parking_lot_impl::*;
#[cfg(feature = "parking_lot")]
mod parking_lot_impl {
pub(crate) use parking_lot::{RwLockReadGuard, RwLockWriteGuard};
use std::sync::{LockResult, TryLockError, TryLockResult};
#[derive(Debug)]
pub(crate) struct RwLock<T> {
inner: parking_lot::RwLock<T>,
}
impl<T> RwLock<T> {
pub(crate) fn new(val: T) -> Self {
Self {
inner: parking_lot::RwLock::new(val),
}
}
#[inline]
pub(crate) fn get_mut(&mut self) -> LockResult<&mut T> {
Ok(self.inner.get_mut())
}
#[inline]
pub(crate) fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
Ok(self.inner.read())
}
#[inline]
#[allow(dead_code)] // may be used later;
pub(crate) fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> {
self.inner.try_read().ok_or(TryLockError::WouldBlock)
}
#[inline]
pub(crate) fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
Ok(self.inner.write())
}
}
impl<T: Default> Default for RwLock<T> {
fn default() -> Self {
RwLock {
inner: parking_lot::RwLock::default(),
}
}
}
}

153
vendor/tracing-subscriber/src/util.rs vendored Normal file
View File

@@ -0,0 +1,153 @@
//! Extension traits and other utilities to make working with subscribers more
//! ergonomic.
use core::fmt;
#[cfg(feature = "std")]
use std::error::Error;
use tracing_core::dispatcher::{self, Dispatch};
#[cfg(feature = "tracing-log")]
use tracing_log::AsLog;
/// Extension trait adding utility methods for subscriber initialization.
///
/// This trait provides extension methods to make configuring and setting a
/// [default subscriber] more ergonomic. It is automatically implemented for all
/// types that can be converted into a [trace dispatcher]. Since `Dispatch`
/// implements `From<T>` for all `T: Subscriber`, all `Subscriber`
/// implementations will implement this extension trait as well. Types which
/// can be converted into `Subscriber`s, such as builders that construct a
/// `Subscriber`, may implement `Into<Dispatch>`, and will also receive an
/// implementation of this trait.
///
/// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
/// [trace dispatcher]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html
pub trait SubscriberInitExt
where
Self: Into<Dispatch>,
{
/// Sets `self` as the [default subscriber] in the current scope, returning a
/// guard that will unset it when dropped.
///
/// If the "tracing-log" feature flag is enabled, this will also initialize
/// a [`log`] compatibility layer. This allows the subscriber to consume
/// `log::Record`s as though they were `tracing` `Event`s.
///
/// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
/// [`log`]: https://crates.io/log
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn set_default(self) -> dispatcher::DefaultGuard {
#[cfg(feature = "tracing-log")]
let _ = tracing_log::LogTracer::init();
dispatcher::set_default(&self.into())
}
/// Attempts to set `self` as the [global default subscriber] in the current
/// scope, returning an error if one is already set.
///
/// If the "tracing-log" feature flag is enabled, this will also attempt to
/// initialize a [`log`] compatibility layer. This allows the subscriber to
/// consume `log::Record`s as though they were `tracing` `Event`s.
///
/// This method returns an error if a global default subscriber has already
/// been set, or if a `log` logger has already been set (when the
/// "tracing-log" feature is enabled).
///
/// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
/// [`log`]: https://crates.io/log
fn try_init(self) -> Result<(), TryInitError> {
dispatcher::set_global_default(self.into()).map_err(TryInitError::new)?;
// Since we are setting the global default subscriber, we can
// opportunistically go ahead and set its global max level hint as
// the max level for the `log` crate as well. This should make
// skipping `log` diagnostics much faster.
#[cfg(feature = "tracing-log")]
tracing_log::LogTracer::builder()
// Note that we must call this *after* setting the global default
// subscriber, so that we get its max level hint.
.with_max_level(tracing_core::LevelFilter::current().as_log())
.init()
.map_err(TryInitError::new)?;
Ok(())
}
/// Attempts to set `self` as the [global default subscriber] in the current
/// scope, panicking if this fails.
///
/// If the "tracing-log" feature flag is enabled, this will also attempt to
/// initialize a [`log`] compatibility layer. This allows the subscriber to
/// consume `log::Record`s as though they were `tracing` `Event`s.
///
/// This method panics if a global default subscriber has already been set,
/// or if a `log` logger has already been set (when the "tracing-log"
/// feature is enabled).
///
/// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
/// [`log`]: https://crates.io/log
fn init(self) {
self.try_init()
.expect("failed to set global default subscriber")
}
}
impl<T> SubscriberInitExt for T where T: Into<Dispatch> {}
/// Error returned by [`try_init`](SubscriberInitExt::try_init) if a global default subscriber could not be initialized.
pub struct TryInitError {
#[cfg(feature = "std")]
inner: Box<dyn Error + Send + Sync + 'static>,
#[cfg(not(feature = "std"))]
_p: (),
}
// ==== impl TryInitError ====
impl TryInitError {
#[cfg(feature = "std")]
fn new(e: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
Self { inner: e.into() }
}
#[cfg(not(feature = "std"))]
fn new<T>(_: T) -> Self {
Self { _p: () }
}
}
impl fmt::Debug for TryInitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "std")]
{
fmt::Debug::fmt(&self.inner, f)
}
#[cfg(not(feature = "std"))]
{
f.write_str("TryInitError(())")
}
}
}
impl fmt::Display for TryInitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "std")]
{
fmt::Display::fmt(&self.inner, f)
}
#[cfg(not(feature = "std"))]
{
f.write_str("failed to set global default subscriber")
}
}
}
#[cfg(feature = "std")]
impl Error for TryInitError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.inner.source()
}
}