mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-09-28 21:42:15 +00:00
fmt, subscriber: move fmt into subscriber (#311)
## Motivation As discussed in #308, there are a large number of crates in this repository, which can be confusing for users and can increase the maintainance burden for maintainers. Also, the `tracing-fmt` and `tracing-subscriber` crates both contain filtering implementations with similar behaviour and APIs, but `tracing-subscriber`'s filter module offers more advanced features (filtering on field values), and is usable with any subscriber implementation. Two separate filter implementations also has the potential to be confusing for users. ## Solution This branch moves most of the code from `tracing-fmt` into a module in `tracing-subscriber`, and changes the `tracing-fmt` builder APIs to use the `Filter` type in `tracing-subscriber`. The `tracing-subscriber/fmt` feature flag can be used to disable the formatting subscriber when it is not used. The `tracing-fmt` crate has been updated to re-export the APIs from `tracing-subscriber`, and marked as deprecated. Once we've published a new version of `tracing-subscriber` with the format APIs, we can publish a final release of `tracing-fmt` that will update the documentation & mark all APIs as deprecated, so that users know to move to the `tracing-subscriber` crate. Refs: #308 Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
parent
0852c82641
commit
2520f97964
@ -13,7 +13,7 @@ edition = "2018"
|
||||
|
||||
[dev-dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
tracing-futures = { path = "../tracing-futures", default-features = false, features = ["std-future"] }
|
||||
tokio = { git = "https://github.com/tokio-rs/tokio.git" }
|
||||
tracing-attributes = { path = "../tracing-attributes" }
|
||||
|
@ -43,8 +43,8 @@ async fn write(stream: &mut TcpStream) -> io::Result<usize> {
|
||||
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let addr = "127.0.0.1:6142".parse()?;
|
||||
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from("async_fn=trace"))
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_filter("async_fn=trace")
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||
|
||||
|
@ -37,8 +37,8 @@ use tracing_futures::Instrument;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::new("echo=trace"))
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_filter("echo=trace")
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
|
@ -38,10 +38,10 @@ use std::{env, net::SocketAddr};
|
||||
|
||||
#[instrument]
|
||||
async fn transfer(
|
||||
inbound: TcpStream,
|
||||
mut inbound: TcpStream,
|
||||
proxy_addr: SocketAddr,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let outbound = TcpStream::connect(&proxy_addr).await?;
|
||||
let mut outbound = TcpStream::connect(&proxy_addr).await?;
|
||||
|
||||
let (mut ri, mut wi) = inbound.split();
|
||||
let (mut ro, mut wo) = outbound.split();
|
||||
@ -86,8 +86,8 @@ async fn transfer(
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::new("proxy_server=trace"))
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_filter("proxy_server=trace")
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
|
@ -38,9 +38,7 @@ async fn subtask(number: usize) -> usize {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter("trace".parse::<tracing_fmt::filter::EnvFilter>()?)
|
||||
.finish();
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::new();
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
parent_task(10).await;
|
||||
Ok(())
|
||||
|
@ -45,7 +45,7 @@ quote = "1"
|
||||
[dev-dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-core = "0.1.4"
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
|
||||
[badges]
|
||||
azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" }
|
||||
|
@ -27,8 +27,9 @@ fn fibonacci_seq(to: u64) -> Vec<u64> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from("args=trace"))
|
||||
use tracing_subscriber::{fmt, Filter};
|
||||
let subscriber = fmt::Subscriber::builder()
|
||||
.with_filter(Filter::from("args=trace"))
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
|
@ -11,8 +11,8 @@ fn suggest_band() -> String {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from("basic=trace"))
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_filter("basic=trace")
|
||||
.finish();
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
let num_recs = 1;
|
||||
|
@ -23,9 +23,8 @@ log = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-fmt = { path = "../tracing-fmt" }
|
||||
tracing-futures = { path = "../tracing-futures" }
|
||||
tracing-subscriber = { path = "../tracing-subscriber" }
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
hyper = "0.12.25"
|
||||
futures = "0.1"
|
||||
tokio = "0.1.22"
|
||||
|
@ -107,7 +107,7 @@ fn echo(req: Request<Body>) -> Instrumented<BoxFut> {
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder().finish();
|
||||
let subscriber = tracing_subscriber::FmtSubscriber::new();
|
||||
let mut builder = env_logger::Builder::new();
|
||||
builder
|
||||
.filter(Some("hyper_echo"), log::LevelFilter::Off)
|
||||
|
@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "tracing-fmt"
|
||||
version = "0.0.1-alpha.3"
|
||||
version = "0.0.1"
|
||||
authors = ["Eliza Weisman <eliza@buoyant.io>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tracing"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tracing-fmt/0.0.1-alpha.3/tracing_fmt"
|
||||
documentation = "https://docs.rs/tracing-fmt/0.0.1/tracing_fmt"
|
||||
description = """
|
||||
A (currently experimental) `tracing` subscriber that formats and logs trace data
|
||||
A `tracing` subscriber that formats and logs trace data. Moved to the `tracing-subscriber` crate.
|
||||
"""
|
||||
categories = [
|
||||
"development-tools::debugging",
|
||||
@ -19,21 +19,19 @@ readme = "README.md"
|
||||
|
||||
[badges]
|
||||
azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" }
|
||||
maintenance = { status = "experimental" }
|
||||
maintenance = { status = "deprecated" }
|
||||
|
||||
[features]
|
||||
default = ["ansi", "chrono", "tracing-log"]
|
||||
ansi = ["ansi_term"]
|
||||
|
||||
[dependencies]
|
||||
tracing-core = "0.1.5"
|
||||
tracing-log = { version = "0.0.1-alpha.1", optional = true }
|
||||
ansi_term = { version = "0.11", optional = true }
|
||||
regex = "1"
|
||||
lazy_static = "1"
|
||||
owning_ref = "0.4.0"
|
||||
parking_lot = { version = ">= 0.7, < 0.10", features = ["owning_ref"] }
|
||||
chrono = { version = "0.4", optional = true }
|
||||
ansi = ["tracing-subscriber/ansi"]
|
||||
chrono = ["tracing-subscriber/chrono"]
|
||||
tracing-log = ["tracing-subscriber/tracing-log"]
|
||||
|
||||
[dependencies.tracing-subscriber]
|
||||
version = "0.0.1-alpha.3"
|
||||
path = "../tracing-subscriber"
|
||||
features = ["fmt", "filter"]
|
||||
|
||||
[dev-dependencies]
|
||||
tracing = { version = "0.1" }
|
||||
|
@ -1,15 +1,17 @@
|
||||
# tracing-fmt
|
||||
|
||||
**Warning: Until `tracing-fmt` has a 0.1.0 release on crates.io, please treat every release as potentially breaking.**
|
||||
|
||||
A (currently experimental) [`tracing`][tracing] subscriber
|
||||
that formats, colors, and logs trace data.
|
||||
**Note**: This library is now part of the [`tracing-subscriber`] crate. This
|
||||
crate now re-exports its public API from `tracing-subscriber`. Using
|
||||
`tracing-fmt` is now deprecated; users are encouraged to use the APIs in
|
||||
this library from their new home in `tracing-subscriber::fmt`.
|
||||
|
||||
[![Crates.io][crates-badge]][crates-url]
|
||||
[![Documentation][docs-badge]][docs-url]
|
||||
[![MIT licensed][mit-badge]][mit-url]
|
||||
[![Build Status][azure-badge]][azure-url]
|
||||
[![Gitter chat][gitter-badge]][gitter-url]
|
||||

|
||||
|
||||
|
||||
[Documentation][docs-url] |
|
||||
[Chat][gitter-url]
|
||||
@ -27,6 +29,7 @@ that formats, colors, and logs trace data.
|
||||
[gitter-badge]: https://img.shields.io/gitter/room/tokio-rs/tracing.svg
|
||||
[gitter-url]: https://gitter.im/tokio-rs/tracing
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
[`tracing`][tracing] is a framework for instrumenting Rust programs with context-aware,
|
||||
@ -43,3 +46,6 @@ This project is licensed under the [MIT license](LICENSE).
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tracing by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
|
||||
[`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
|
||||
[`tracing-subscriber`]: https://crates.io/crates/tracing-subscriber/
|
||||
|
@ -1,806 +0,0 @@
|
||||
use crate::{filter::Filter, span::Context};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use tracing_core::{subscriber::Interest, Level, Metadata};
|
||||
|
||||
use std::{cmp::Ordering, env, error::Error, fmt, str::FromStr};
|
||||
|
||||
pub const DEFAULT_FILTER_ENV: &str = "RUST_LOG";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EnvFilter {
|
||||
directives: Vec<Directive>,
|
||||
max_level: LevelFilter,
|
||||
includes_span_directive: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FromEnvError {
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseError {
|
||||
directive: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Directive {
|
||||
target: Option<String>,
|
||||
in_span: Option<String>,
|
||||
// TODO: this can probably be a `SmallVec` someday, since a span won't have
|
||||
// over 32 fields.
|
||||
fields: Vec<String>,
|
||||
level: LevelFilter,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum LevelFilter {
|
||||
Off,
|
||||
Level(Level),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
Parse(ParseError),
|
||||
Env(env::VarError),
|
||||
}
|
||||
|
||||
// ===== impl EnvFilter =====
|
||||
|
||||
impl EnvFilter {
|
||||
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
|
||||
/// variable, ignoring any invalid filter directives.
|
||||
pub fn from_default_env() -> Self {
|
||||
Self::from_env(DEFAULT_FILTER_ENV)
|
||||
}
|
||||
|
||||
/// Returns a new `EnvFilter` from the value of the given environment
|
||||
/// variable, ignoring any invalid filter directives.
|
||||
pub fn from_env<A: AsRef<str>>(env: A) -> Self {
|
||||
env::var(env.as_ref()).map(Self::new).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns a new `EnvFilter` from the directives in the given string,
|
||||
/// ignoring any that are invalid.
|
||||
pub fn new<S: AsRef<str>>(dirs: S) -> Self {
|
||||
Self::new2(parse_directives(dirs.as_ref()))
|
||||
}
|
||||
|
||||
/// Returns a new `EnvFilter` from the directives in the given string,
|
||||
/// or an error if any are invalid.
|
||||
pub fn try_new<S: AsRef<str>>(dirs: S) -> Result<Self, ParseError> {
|
||||
Ok(Self::new2(try_parse_directives(dirs.as_ref())?))
|
||||
}
|
||||
|
||||
/// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment
|
||||
/// variable, or an error if the environment variable contains any invalid
|
||||
/// filter directives.
|
||||
pub fn try_from_default_env() -> Result<Self, FromEnvError> {
|
||||
Self::try_from_env(DEFAULT_FILTER_ENV)
|
||||
}
|
||||
|
||||
/// Returns a new `EnvFilter` from the value of the given environment
|
||||
/// variable, or an error if the environment variable is unset or contains
|
||||
/// any invalid filter directives.
|
||||
pub fn try_from_env<A: AsRef<str>>(env: A) -> Result<Self, FromEnvError> {
|
||||
env::var(env.as_ref())?.parse().map_err(Into::into)
|
||||
}
|
||||
|
||||
fn new2(mut directives: Vec<Directive>) -> Self {
|
||||
if directives.is_empty() {
|
||||
directives.push(Directive::default());
|
||||
} else {
|
||||
directives.sort_by_key(Directive::len);
|
||||
}
|
||||
|
||||
let max_level = directives
|
||||
.iter()
|
||||
.map(|directive| &directive.level)
|
||||
.max()
|
||||
.cloned()
|
||||
.unwrap_or(LevelFilter::Level(Level::ERROR));
|
||||
|
||||
let includes_span_directive = directives
|
||||
.iter()
|
||||
.any(|directive| directive.in_span.is_some());
|
||||
|
||||
EnvFilter {
|
||||
directives,
|
||||
max_level,
|
||||
includes_span_directive,
|
||||
}
|
||||
}
|
||||
|
||||
fn directives_for<'a>(
|
||||
&'a self,
|
||||
metadata: &'a Metadata<'a>,
|
||||
) -> impl Iterator<Item = &'a Directive> + 'a {
|
||||
let target = metadata.target();
|
||||
let name = metadata.name();
|
||||
self.directives
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(move |d| match d.target.as_ref() {
|
||||
None => Some(d),
|
||||
Some(t) if target.starts_with(t) => Some(d),
|
||||
_ => d
|
||||
.in_span
|
||||
.as_ref()
|
||||
.and_then(|span| if span == name { Some(d) } else { None }),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<S> for EnvFilter
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn from(s: S) -> Self {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for EnvFilter {
|
||||
type Err = ParseError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EnvFilter {
|
||||
fn default() -> Self {
|
||||
Self::new2(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> Filter<N> for EnvFilter {
|
||||
fn callsite_enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, N>) -> Interest {
|
||||
if !self.includes_span_directive && self.max_level < *metadata.level() {
|
||||
return Interest::never();
|
||||
}
|
||||
|
||||
let mut interest = Interest::never();
|
||||
for directive in self.directives_for(metadata) {
|
||||
let accepts_level = directive.level >= *metadata.level();
|
||||
match directive.in_span.as_ref() {
|
||||
// The directive applies to anything within the span described
|
||||
// by this metadata. The span must always be enabled.
|
||||
Some(span) if span == metadata.name() => return Interest::always(),
|
||||
|
||||
// The directive may accept this metadata, but
|
||||
// only within a particular span. Keep searching
|
||||
// to see if there's one that always applies.
|
||||
Some(_) if accepts_level => interest = Interest::sometimes(),
|
||||
|
||||
// The directive doesn't care about the current span, and the
|
||||
// levels are compatible. We are always interested.
|
||||
None if accepts_level => return Interest::always(),
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
interest
|
||||
}
|
||||
|
||||
fn enabled<'a>(&self, metadata: &Metadata<'_>, ctx: &Context<'a, N>) -> bool {
|
||||
for directive in self.directives_for(metadata) {
|
||||
let accepts_level = directive.level >= *metadata.level();
|
||||
match directive.in_span.as_ref() {
|
||||
// The directive applies to anything within the span described
|
||||
// by this metadata. The span must always be enabled.
|
||||
Some(span) if span == metadata.name() => return true,
|
||||
|
||||
// The directive doesn't care about the current span, and the
|
||||
// levels are compatible. We are always interested.
|
||||
None if accepts_level => return true,
|
||||
|
||||
// The directive only applies if we're in a particular span;
|
||||
// check if we're currently in that span.
|
||||
Some(desired) if accepts_level => {
|
||||
// Are we within the desired span?
|
||||
let in_span = ctx
|
||||
.visit_spans(|_, span| {
|
||||
if span.name() == desired {
|
||||
// If there are no fields, then let's exit.
|
||||
// if there are fields and none of them match
|
||||
// try the next span
|
||||
if directive.fields.is_empty()
|
||||
|| directive
|
||||
.fields
|
||||
.iter()
|
||||
.any(|field| span.fields().contains(field))
|
||||
{
|
||||
// Return `Err` to short-circuit the span visitation.
|
||||
Err(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.is_err();
|
||||
|
||||
if in_span {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_directives(spec: &str) -> Vec<Directive> {
|
||||
spec.split(',')
|
||||
.filter_map(|dir| {
|
||||
Directive::parse(dir).or_else(|| {
|
||||
eprintln!("ignoring invalid filter directive '{}'", dir);
|
||||
None
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn try_parse_directives(spec: &str) -> Result<Vec<Directive>, ParseError> {
|
||||
spec.split(',')
|
||||
.map(|dir| Directive::parse(dir).ok_or_else(|| ParseError::new(dir)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl fmt::Display for EnvFilter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut directives = self.directives.iter();
|
||||
if let Some(d) = directives.next() {
|
||||
write!(f, "{}", d)?;
|
||||
for d in directives {
|
||||
write!(f, ",{}", d)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Directive =====
|
||||
|
||||
impl Directive {
|
||||
fn len(&self) -> usize {
|
||||
self.target
|
||||
.as_ref()
|
||||
.map(String::len)
|
||||
.or_else(|| self.in_span.as_ref().map(String::len))
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn parse(from: &str) -> Option<Self> {
|
||||
lazy_static! {
|
||||
static ref DIRECTIVE_RE: Regex = Regex::new(
|
||||
r"(?x)
|
||||
^(?P<global_level>trace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF[0-5])$ |
|
||||
^
|
||||
(?: # target name or span name
|
||||
(?P<target>[\w:]+)|(?P<span>\[[^\]]*\])
|
||||
){1,2}
|
||||
(?: # level or nothing
|
||||
=(?P<level>trace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF[0-5])?
|
||||
)?
|
||||
$
|
||||
"
|
||||
)
|
||||
.unwrap();
|
||||
static ref SPAN_PART_RE: Regex =
|
||||
Regex::new(r#"(?P<name>\w+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap();
|
||||
static ref FIELD_FILTER_RE: Regex =
|
||||
Regex::new(r#"([\w_0-9]+(?:=(?:[\w0-9]+|".+"))?)(?: |$)"#).unwrap();
|
||||
}
|
||||
|
||||
fn parse_level(from: &str) -> Option<LevelFilter> {
|
||||
// TODO: maybe this whole function ought to be replaced by a
|
||||
// `FromStr` impl for `Level` in `tracing_core`...?
|
||||
from.parse::<usize>()
|
||||
.ok()
|
||||
.and_then(|num| match num {
|
||||
0 => Some(LevelFilter::Off),
|
||||
1 => Some(LevelFilter::Level(Level::ERROR)),
|
||||
2 => Some(LevelFilter::Level(Level::WARN)),
|
||||
3 => Some(LevelFilter::Level(Level::INFO)),
|
||||
4 => Some(LevelFilter::Level(Level::DEBUG)),
|
||||
5 => Some(LevelFilter::Level(Level::TRACE)),
|
||||
_ => None,
|
||||
})
|
||||
.or_else(|| match from {
|
||||
"" => Some(LevelFilter::Level(Level::ERROR)),
|
||||
s if s.eq_ignore_ascii_case("error") => Some(LevelFilter::Level(Level::ERROR)),
|
||||
s if s.eq_ignore_ascii_case("warn") => Some(LevelFilter::Level(Level::WARN)),
|
||||
s if s.eq_ignore_ascii_case("info") => Some(LevelFilter::Level(Level::INFO)),
|
||||
s if s.eq_ignore_ascii_case("debug") => Some(LevelFilter::Level(Level::DEBUG)),
|
||||
s if s.eq_ignore_ascii_case("trace") => Some(LevelFilter::Level(Level::TRACE)),
|
||||
s if s.eq_ignore_ascii_case("off") => Some(LevelFilter::Off),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
let caps = DIRECTIVE_RE.captures(from)?;
|
||||
|
||||
if let Some(level) = caps
|
||||
.name("global_level")
|
||||
.and_then(|c| parse_level(c.as_str()))
|
||||
{
|
||||
return Some(Directive {
|
||||
level,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
let target = caps.name("target").and_then(|c| {
|
||||
let s = c.as_str();
|
||||
if parse_level(s).is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(s.to_owned())
|
||||
}
|
||||
});
|
||||
|
||||
let (in_span, fields) = caps
|
||||
.name("span")
|
||||
.and_then(|cap| {
|
||||
let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']');
|
||||
let caps = SPAN_PART_RE.captures(cap)?;
|
||||
let span = caps.name("name").map(|c| c.as_str().to_owned());
|
||||
let fields = caps
|
||||
.name("fields")
|
||||
.map(|c| {
|
||||
FIELD_FILTER_RE
|
||||
.find_iter(c.as_str())
|
||||
.map(|c| c.as_str().trim().to_owned())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_else(Vec::new);
|
||||
Some((span, fields))
|
||||
})
|
||||
.unwrap_or_else(|| (None, Vec::new()));
|
||||
|
||||
let level = caps
|
||||
.name("level")
|
||||
.and_then(|c| parse_level(c.as_str()))
|
||||
.unwrap_or(LevelFilter::Level(Level::ERROR));
|
||||
|
||||
Some(Directive {
|
||||
level,
|
||||
target,
|
||||
in_span,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Directive {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
level: LevelFilter::Level(Level::ERROR),
|
||||
target: None,
|
||||
in_span: None,
|
||||
fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Directive {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut write_equals = false;
|
||||
|
||||
if let Some(ref tgt) = self.target {
|
||||
write!(f, "{}", tgt)?;
|
||||
write_equals = true;
|
||||
}
|
||||
|
||||
if self.in_span.is_some() || !self.fields.is_empty() {
|
||||
"[".fmt(f)?;
|
||||
if let Some(ref span) = self.in_span {
|
||||
write!(f, "{}", span)?;
|
||||
}
|
||||
|
||||
let mut fields = self.fields.iter();
|
||||
if let Some(field) = fields.next() {
|
||||
write!(f, "{{{}", field)?;
|
||||
for field in fields {
|
||||
write!(f, " {}", field)?;
|
||||
}
|
||||
"}".fmt(f)?;
|
||||
}
|
||||
"]".fmt(f)?;
|
||||
write_equals = true;
|
||||
}
|
||||
|
||||
if write_equals {
|
||||
"=".fmt(f)?;
|
||||
}
|
||||
|
||||
self.level.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FromEnvError =====
|
||||
|
||||
impl From<ParseError> for FromEnvError {
|
||||
fn from(p: ParseError) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::Parse(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<env::VarError> for FromEnvError {
|
||||
fn from(v: env::VarError) -> Self {
|
||||
Self {
|
||||
kind: ErrorKind::Env(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FromEnvError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.kind {
|
||||
ErrorKind::Parse(ref p) => p.fmt(f),
|
||||
ErrorKind::Env(ref e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for FromEnvError {
|
||||
fn description(&self) -> &str {
|
||||
match self.kind {
|
||||
ErrorKind::Parse(ref p) => p.description(),
|
||||
ErrorKind::Env(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)] // for compatibility with minimum Rust version 1.26.0
|
||||
fn cause(&self) -> Option<&dyn Error> {
|
||||
match self.kind {
|
||||
ErrorKind::Parse(ref p) => Some(p),
|
||||
ErrorKind::Env(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ParseError =====
|
||||
|
||||
impl ParseError {
|
||||
fn new(directive: &str) -> Self {
|
||||
ParseError {
|
||||
directive: directive.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "invalid filter directive '{}'", self.directive)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ParseError {
|
||||
fn description(&self) -> &str {
|
||||
"invalid filter directive"
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl LevelFilter =====
|
||||
|
||||
impl PartialEq<Level> for LevelFilter {
|
||||
fn eq(&self, other: &Level) -> bool {
|
||||
match self {
|
||||
LevelFilter::Off => false,
|
||||
LevelFilter::Level(l) => l == other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Level> for LevelFilter {
|
||||
fn partial_cmp(&self, other: &Level) -> Option<Ordering> {
|
||||
match self {
|
||||
LevelFilter::Off => Some(Ordering::Less),
|
||||
LevelFilter::Level(l) => l.partial_cmp(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LevelFilter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LevelFilter::Off => f.pad("off"),
|
||||
LevelFilter::Level(Level::ERROR) => f.pad("error"),
|
||||
LevelFilter::Level(Level::WARN) => f.pad("warn"),
|
||||
LevelFilter::Level(Level::INFO) => f.pad("info"),
|
||||
LevelFilter::Level(Level::DEBUG) => f.pad("debug"),
|
||||
LevelFilter::Level(Level::TRACE) => f.pad("trace"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::format::NewRecorder;
|
||||
use crate::span::*;
|
||||
use tracing_core::field::FieldSet;
|
||||
use tracing_core::*;
|
||||
|
||||
struct Cs;
|
||||
|
||||
impl Callsite for Cs {
|
||||
fn set_interest(&self, _interest: Interest) {}
|
||||
fn metadata(&self) -> &Metadata<'_> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_enabled_no_span_directive() {
|
||||
let filter = EnvFilter::from("app=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&[], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_never());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_off() {
|
||||
let filter = EnvFilter::from("app=off");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::ERROR,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&[], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_never());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_enabled_includes_span_directive() {
|
||||
let filter = EnvFilter::from("app[mySpan]=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&[], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_always());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_enabled_includes_span_directive_field() {
|
||||
let filter = EnvFilter::from("app[mySpan{field=\"value\"}]=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&["field=\"value\""], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_always());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_disabled_includes_directive_field() {
|
||||
let filter = EnvFilter::from("app[{field=\"novalue\"}]=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&["field=\"value\""], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_never());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_disabled_includes_directive_field_no_value() {
|
||||
let filter = EnvFilter::from("app[mySpan{field}]=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&["field=\"value\""], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_always());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callsite_enabled_includes_span_directive_multiple_fields() {
|
||||
let filter = EnvFilter::from("app[mySpan{field=\"value\" field2=2}]=debug");
|
||||
let store = Store::with_capacity(1);
|
||||
let ctx = Context::new(&store, &NewRecorder);
|
||||
let meta = Metadata::new(
|
||||
"mySpan",
|
||||
"app",
|
||||
Level::TRACE,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
FieldSet::new(&["field=\"value\""], identify_callsite!(&Cs)),
|
||||
Kind::SPAN,
|
||||
);
|
||||
|
||||
let interest = filter.callsite_enabled(&meta, &ctx);
|
||||
assert!(interest.is_always());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_directives_valid() {
|
||||
let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
|
||||
assert_eq!(dirs.len(), 4, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::ERROR));
|
||||
assert_eq!(dirs[0].in_span, None);
|
||||
|
||||
assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::Level(Level::ERROR));
|
||||
assert_eq!(dirs[1].in_span, None);
|
||||
|
||||
assert_eq!(dirs[2].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Level(Level::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_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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::ERROR));
|
||||
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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::WARN));
|
||||
assert_eq!(dirs[1].in_span, None);
|
||||
|
||||
assert_eq!(dirs[1].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::Level(Level::DEBUG));
|
||||
assert_eq!(dirs[1].in_span, None);
|
||||
}
|
||||
|
||||
#[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, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::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::Level(Level::ERROR));
|
||||
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::Level(Level::DEBUG));
|
||||
assert_eq!(dirs[2].in_span, Some("baz".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_directives_with_fields() {
|
||||
let dirs = parse_directives(
|
||||
"[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug",
|
||||
);
|
||||
assert_eq!(dirs.len(), 3, "\ngot: {:?}", dirs);
|
||||
assert_eq!(dirs[0].target, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Level(Level::ERROR));
|
||||
assert_eq!(dirs[0].in_span, Some("span1".to_string()));
|
||||
assert_eq!(&dirs[0].fields[..], &["foo=1"]);
|
||||
|
||||
assert_eq!(dirs[1].target, None);
|
||||
assert_eq!(dirs[1].level, LevelFilter::Level(Level::ERROR));
|
||||
assert_eq!(dirs[1].in_span, Some("span2".to_string()));
|
||||
assert_eq!(&dirs[1].fields[..], &["bar=2", "baz=false"]);
|
||||
|
||||
assert_eq!(dirs[2].target, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Level(Level::DEBUG));
|
||||
assert_eq!(dirs[2].in_span, None);
|
||||
assert_eq!(&dirs[2].fields[..], &["quux=\"quuux\""]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let f1: EnvFilter =
|
||||
"[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let _: EnvFilter = format!("{}", f1).parse().unwrap();
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
//! Filter implementations for determining what spans and events to record.
|
||||
use crate::span;
|
||||
|
||||
use tracing_core::{subscriber::Interest, Metadata};
|
||||
|
||||
/// A policy for determining what spans and events should be enabled.
|
||||
pub trait Filter<N> {
|
||||
fn callsite_enabled(&self, metadata: &Metadata<'_>, ctx: &span::Context<'_, N>) -> Interest {
|
||||
if self.enabled(metadata, ctx) {
|
||||
Interest::always()
|
||||
} else {
|
||||
Interest::never()
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata<'_>, ctx: &span::Context<'_, N>) -> bool;
|
||||
}
|
||||
|
||||
pub mod env;
|
||||
pub mod reload;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{env::EnvFilter, reload::ReloadFilter};
|
||||
|
||||
impl<'a, F, N> Filter<N> for F
|
||||
where
|
||||
F: Fn(&Metadata<'_>, &span::Context<'_, N>) -> bool,
|
||||
N: crate::NewVisitor<'a>,
|
||||
{
|
||||
fn enabled(&self, metadata: &Metadata<'_>, ctx: &span::Context<'_, N>) -> bool {
|
||||
(self)(metadata, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn none() -> NoFilter {
|
||||
NoFilter { _p: () }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NoFilter {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
impl<N> Filter<N> for NoFilter {
|
||||
fn enabled(&self, _: &Metadata<'_>, _: &span::Context<'_, N>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
use crate::{filter::Filter, span::Context};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
error, fmt,
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use tracing_core::{callsite, subscriber::Interest, Metadata};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReloadFilter<F, N>
|
||||
where
|
||||
F: Filter<N>,
|
||||
{
|
||||
inner: Arc<RwLock<F>>,
|
||||
_f: PhantomData<fn(N)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Handle<F, N>
|
||||
where
|
||||
F: Filter<N>,
|
||||
{
|
||||
inner: Weak<RwLock<F>>,
|
||||
_f: PhantomData<fn(N)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ErrorKind {
|
||||
SubscriberGone,
|
||||
}
|
||||
|
||||
// ===== impl ReloadFilter =====
|
||||
|
||||
impl<F, N> Filter<N> for ReloadFilter<F, N>
|
||||
where
|
||||
F: Filter<N>,
|
||||
{
|
||||
fn callsite_enabled(&self, metadata: &Metadata<'_>, ctx: &Context<'_, N>) -> Interest {
|
||||
self.inner.read().callsite_enabled(metadata, ctx)
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata<'_>, ctx: &Context<'_, N>) -> bool {
|
||||
self.inner.read().enabled(metadata, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, N> ReloadFilter<F, N>
|
||||
where
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
pub fn new(f: F) -> Self
|
||||
where
|
||||
F: Filter<N>,
|
||||
{
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(f)),
|
||||
_f: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> Handle<F, N> {
|
||||
Handle {
|
||||
inner: Arc::downgrade(&self.inner),
|
||||
_f: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Handle =====
|
||||
|
||||
impl<F, N> Handle<F, N>
|
||||
where
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
pub fn reload(&self, new_filter: impl Into<F>) -> Result<(), Error> {
|
||||
self.modify(|filter| {
|
||||
*filter = new_filter.into();
|
||||
})
|
||||
}
|
||||
|
||||
/// Invokes a closure with a mutable reference to the current filter,
|
||||
/// allowing it to be modified in place.
|
||||
pub fn modify(&self, f: impl FnOnce(&mut F)) -> Result<(), Error> {
|
||||
let inner = self.inner.upgrade().ok_or(Error {
|
||||
kind: ErrorKind::SubscriberGone,
|
||||
})?;
|
||||
|
||||
let mut lock = inner.write();
|
||||
f(&mut *lock);
|
||||
// Release the lock before rebuilding the interest cache, as that
|
||||
// function will lock the new filter.
|
||||
drop(lock);
|
||||
|
||||
callsite::rebuild_interest_cache();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a clone of the filter's current value if it still exists.
|
||||
/// Otherwise, if the filter has been dropped, returns `None`.
|
||||
pub fn clone_current(&self) -> Option<F>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
self.with_current(F::clone).ok()
|
||||
}
|
||||
|
||||
/// Invokes a closure with a borrowed reference to the current filter,
|
||||
/// returning the result (or an error if the filter no longer exists).
|
||||
pub fn with_current<T>(&self, f: impl FnOnce(&F) -> T) -> Result<T, Error> {
|
||||
let inner = self.inner.upgrade().ok_or(Error {
|
||||
kind: ErrorKind::SubscriberGone,
|
||||
})?;
|
||||
let inner = inner.read();
|
||||
Ok(f(&*inner))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, N> Clone for Handle<F, N>
|
||||
where
|
||||
F: Filter<N>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Handle {
|
||||
inner: self.inner.clone(),
|
||||
_f: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Error =====
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
error::Error::description(self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match self.kind {
|
||||
ErrorKind::SubscriberGone => "subscriber no longer exists",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use tracing::trace;
|
||||
use tracing_core::{
|
||||
dispatcher::{self, Dispatch},
|
||||
Metadata,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn reload_handle() {
|
||||
static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
|
||||
static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
enum Filter {
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
impl<N> filter::Filter<N> for Filter {
|
||||
fn callsite_enabled(&self, _: &Metadata<'_>, _: &Context<'_, N>) -> Interest {
|
||||
Interest::sometimes()
|
||||
}
|
||||
|
||||
fn enabled(&self, _: &Metadata<'_>, _: &Context<'_, N>) -> bool {
|
||||
match self {
|
||||
Filter::One => FILTER1_CALLS.fetch_add(1, Ordering::Relaxed),
|
||||
Filter::Two => FILTER2_CALLS.fetch_add(1, Ordering::Relaxed),
|
||||
};
|
||||
true
|
||||
}
|
||||
}
|
||||
fn event() {
|
||||
trace!("my event");
|
||||
}
|
||||
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
.compact()
|
||||
.with_filter(Filter::One)
|
||||
.with_filter_reloading();
|
||||
let handle = subscriber.reload_handle();
|
||||
let subscriber = Dispatch::new(subscriber.finish());
|
||||
|
||||
dispatcher::with_default(&subscriber, || {
|
||||
assert_eq!(FILTER1_CALLS.load(Ordering::Relaxed), 0);
|
||||
assert_eq!(FILTER2_CALLS.load(Ordering::Relaxed), 0);
|
||||
|
||||
event();
|
||||
|
||||
assert_eq!(FILTER1_CALLS.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(FILTER2_CALLS.load(Ordering::Relaxed), 0);
|
||||
|
||||
handle.reload(Filter::Two).expect("should reload");
|
||||
|
||||
event();
|
||||
|
||||
assert_eq!(FILTER1_CALLS.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(FILTER2_CALLS.load(Ordering::Relaxed), 1);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reload_from_env() {
|
||||
use crate::filter::EnvFilter;
|
||||
let subscriber = FmtSubscriber::builder().with_filter_reloading().finish();
|
||||
let reload_handle = subscriber.reload_handle();
|
||||
reload_handle
|
||||
.reload(EnvFilter::from_default_env())
|
||||
.expect("should reload");
|
||||
}
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
//! A `Subscriber` for formatting and logging `tracing` data.
|
||||
//!
|
||||
//! **Note**: This library is now part of the [`tracing-subscriber`] crate. This
|
||||
//! crate now re-exports its public API from `tracing-subscriber`. Using
|
||||
//! `tracing-fmt` is now deprecated; users are encouraged to use the APIs in
|
||||
//! this library from their new home in `tracing-subscriber::fmt`.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! [`tracing`] is a framework for instrumenting Rust programs with context-aware,
|
||||
@ -10,452 +15,39 @@
|
||||
//!
|
||||
//! [`tracing`]: https://crates.io/crates/tracing
|
||||
//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
|
||||
#![doc(html_root_url = "https://docs.rs/tracing-f,t/0.0.1-alpha.3")]
|
||||
//! [`tracing-subscriber`]: https://crates.io/crates/tracing-subscriber/
|
||||
#![doc(html_root_url = "https://docs.rs/tracing-fmt/0.0.1")]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
use tracing_core::{field, subscriber::Interest, Event, Metadata};
|
||||
|
||||
use std::{any::TypeId, cell::RefCell, fmt, io};
|
||||
|
||||
pub mod filter;
|
||||
pub mod format;
|
||||
mod span;
|
||||
pub mod time;
|
||||
pub mod writer;
|
||||
#![deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt`")]
|
||||
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt`")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{filter::Filter, format::FormatEvent, span::Context, writer::MakeWriter};
|
||||
pub use crate::{format::FormatEvent, writer::MakeWriter};
|
||||
|
||||
/// A `Subscriber` that logs formatted representations of `tracing` events.
|
||||
#[derive(Debug)]
|
||||
pub struct FmtSubscriber<
|
||||
N = format::NewRecorder,
|
||||
E = format::Format<format::Full>,
|
||||
F = filter::EnvFilter,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
new_visitor: N,
|
||||
fmt_event: E,
|
||||
filter: F,
|
||||
spans: span::Store,
|
||||
settings: Settings,
|
||||
make_writer: W,
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt`")]
|
||||
#[doc(inline)]
|
||||
pub use tracing_subscriber::{fmt::Builder, fmt::Context, FmtSubscriber};
|
||||
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt::format`")]
|
||||
pub mod format {
|
||||
#[doc(inline)]
|
||||
pub use tracing_subscriber::fmt::format::*;
|
||||
}
|
||||
|
||||
/// Configures and constructs `FmtSubscriber`s.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Builder<
|
||||
N = format::NewRecorder,
|
||||
E = format::Format<format::Full>,
|
||||
F = filter::EnvFilter,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
new_visitor: N,
|
||||
fmt_event: E,
|
||||
filter: F,
|
||||
settings: Settings,
|
||||
make_writer: W,
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt::writer`")]
|
||||
pub mod writer {
|
||||
#[doc(inline)]
|
||||
pub use tracing_subscriber::fmt::writer::*;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Settings {
|
||||
inherit_fields: bool,
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::fmt::time`")]
|
||||
pub mod time {
|
||||
#[doc(inline)]
|
||||
pub use tracing_subscriber::fmt::time::*;
|
||||
}
|
||||
|
||||
impl FmtSubscriber {
|
||||
pub fn builder() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FmtSubscriber {
|
||||
fn default() -> Self {
|
||||
Builder::default().finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> FmtSubscriber<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a>,
|
||||
{
|
||||
#[inline]
|
||||
fn ctx(&self) -> span::Context<'_, N> {
|
||||
span::Context::new(&self.spans, &self.new_visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> FmtSubscriber<N, E, filter::ReloadFilter<F, N>, W>
|
||||
where
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
/// Returns a `Handle` that may be used to reload this subscriber's
|
||||
/// filter.
|
||||
pub fn reload_handle(&self) -> filter::reload::Handle<F, N> {
|
||||
self.filter.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> tracing_core::Subscriber for FmtSubscriber<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
F: Filter<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
{
|
||||
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
|
||||
self.filter.callsite_enabled(metadata, &self.ctx())
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
|
||||
self.filter.enabled(metadata, &self.ctx())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id {
|
||||
self.spans.new_span(attrs, &self.new_visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
|
||||
self.spans.record(span, values, &self.new_visitor)
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
|
||||
// TODO: implement this please
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event<'_>) {
|
||||
thread_local! {
|
||||
static BUF: RefCell<String> = RefCell::new(String::new());
|
||||
}
|
||||
|
||||
BUF.with(|buf| {
|
||||
let borrow = buf.try_borrow_mut();
|
||||
let mut a;
|
||||
let mut b;
|
||||
let buf = match borrow {
|
||||
Ok(buf) => {
|
||||
a = buf;
|
||||
&mut *a
|
||||
}
|
||||
_ => {
|
||||
b = String::new();
|
||||
&mut b
|
||||
}
|
||||
};
|
||||
let ctx = span::Context::new(&self.spans, &self.new_visitor);
|
||||
|
||||
if self.fmt_event.format_event(&ctx, buf, event).is_ok() {
|
||||
let mut writer = self.make_writer.make_writer();
|
||||
let _ = io::Write::write_all(&mut writer, buf.as_bytes());
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
});
|
||||
}
|
||||
|
||||
fn enter(&self, id: &span::Id) {
|
||||
// TODO: add on_enter hook
|
||||
self.spans.push(id);
|
||||
}
|
||||
|
||||
fn exit(&self, id: &span::Id) {
|
||||
self.spans.pop(id);
|
||||
}
|
||||
|
||||
fn current_span(&self) -> span::Current {
|
||||
if let Some(id) = self.spans.current() {
|
||||
if let Some(meta) = self.spans.get(&id).map(|span| span.metadata()) {
|
||||
return span::Current::new(id, meta);
|
||||
}
|
||||
}
|
||||
span::Current::none()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_span(&self, id: &span::Id) -> span::Id {
|
||||
self.spans.clone_span(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_close(&self, id: span::Id) -> bool {
|
||||
self.spans.drop_span(id)
|
||||
}
|
||||
|
||||
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
|
||||
match () {
|
||||
_ if id == TypeId::of::<Self>() => Some(self as *const Self as *const ()),
|
||||
_ if id == TypeId::of::<F>() => Some(&self.filter as *const F as *const ()),
|
||||
_ if id == TypeId::of::<E>() => Some(&self.fmt_event as *const E as *const ()),
|
||||
_ if id == TypeId::of::<N>() => Some(&self.new_visitor as *const N as *const ()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NewVisitor<'a> {
|
||||
type Visitor: field::Visit + 'a;
|
||||
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor;
|
||||
}
|
||||
|
||||
impl<'a, F, R> NewVisitor<'a> for F
|
||||
where
|
||||
F: Fn(&'a mut dyn fmt::Write, bool) -> R,
|
||||
R: field::Visit + 'a,
|
||||
{
|
||||
type Visitor = R;
|
||||
|
||||
#[inline]
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor {
|
||||
(self)(writer, is_empty)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder {
|
||||
filter: filter::EnvFilter::from_default_env(),
|
||||
new_visitor: format::NewRecorder,
|
||||
fmt_event: format::Format::default(),
|
||||
settings: Settings::default(),
|
||||
make_writer: io::stdout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
F: Filter<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
{
|
||||
pub fn finish(self) -> FmtSubscriber<N, E, F, W> {
|
||||
FmtSubscriber {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
spans: span::Store::with_capacity(32),
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, L, T, F, W> Builder<N, format::Format<L, T>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
/// Use the given `timer` for log message timestamps.
|
||||
pub fn with_timer<T2>(self, timer: T2) -> Builder<N, format::Format<L, T2>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event.with_timer(timer),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not emit timestamps with log messages.
|
||||
pub fn without_time(self) -> Builder<N, format::Format<L, ()>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event.without_time(),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable ANSI encoding for formatted events.
|
||||
#[cfg(feature = "ansi")]
|
||||
pub fn with_ansi(self, ansi: bool) -> Builder<N, format::Format<L, T>, F, W> {
|
||||
Builder {
|
||||
fmt_event: self.fmt_event.with_ansi(ansi),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether or not an event's target is displayed.
|
||||
pub fn with_target(self, display_target: bool) -> Builder<N, format::Format<L, T>, F, W> {
|
||||
Builder {
|
||||
fmt_event: self.fmt_event.with_target(display_target),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W>
|
||||
where
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
/// Configures the subscriber being built to allow filter reloading at
|
||||
/// runtime.
|
||||
pub fn with_filter_reloading(self) -> Builder<N, E, filter::ReloadFilter<F, N>, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: filter::ReloadFilter::new(self.filter),
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, filter::ReloadFilter<F, N>, W>
|
||||
where
|
||||
F: Filter<N> + 'static,
|
||||
{
|
||||
/// Returns a `Handle` that may be used to reload the constructed subscriber's
|
||||
/// filter.
|
||||
pub fn reload_handle(&self) -> filter::reload::Handle<F, N> {
|
||||
self.filter.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// Sets the Visitor that the subscriber being built will use to record
|
||||
/// fields.
|
||||
pub fn with_visitor<N2>(self, new_visitor: N2) -> Builder<N2, E, F, W>
|
||||
where
|
||||
N2: for<'a> NewVisitor<'a> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the filter that the subscriber being built will use to determine if
|
||||
/// a span or event is enabled.
|
||||
pub fn with_filter<F2>(self, filter: F2) -> Builder<N, E, F2, W>
|
||||
where
|
||||
F2: Filter<N> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the subscriber being built to use a less verbose formatter.
|
||||
///
|
||||
/// See [`format::Compact`].
|
||||
pub fn compact(self) -> Builder<N, format::Format<format::Compact>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
{
|
||||
Builder {
|
||||
fmt_event: format::Format::default().compact(),
|
||||
filter: self.filter,
|
||||
new_visitor: self.new_visitor,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the function that the subscriber being built should use to format
|
||||
/// events that occur.
|
||||
pub fn on_event<E2>(self, fmt_event: E2) -> Builder<N, E2, F, W>
|
||||
where
|
||||
E2: FormatEvent<N> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether or not spans inherit their parents' field values (disabled
|
||||
/// by default).
|
||||
pub fn inherit_fields(self, inherit_fields: bool) -> Self {
|
||||
Builder {
|
||||
settings: Settings { inherit_fields },
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// Sets the [`MakeWriter`] that the subscriber being built will use to write events.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Using `stderr` rather than `stdout`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io;
|
||||
///
|
||||
/// let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
/// .with_writer(io::stderr)
|
||||
/// .finish();
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeWriter`]: trait.MakeWriter.html
|
||||
pub fn with_writer<W2>(self, make_writer: W2) -> Builder<N, E, F, W2>
|
||||
where
|
||||
W2: MakeWriter + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use tracing_core::dispatcher::Dispatch;
|
||||
|
||||
#[test]
|
||||
fn impls() {
|
||||
let f = format::Format::default().with_timer(time::Uptime::default());
|
||||
let subscriber = FmtSubscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
|
||||
let f = format::Format::default();
|
||||
let subscriber = FmtSubscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
|
||||
let f = format::Format::default().compact();
|
||||
let subscriber = FmtSubscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscriber_downcasts() {
|
||||
let subscriber = FmtSubscriber::new();
|
||||
let dispatch = Dispatch::new(subscriber);
|
||||
assert!(dispatch.downcast_ref::<FmtSubscriber>().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscriber_downcasts_to_parts() {
|
||||
let subscriber = FmtSubscriber::new();
|
||||
let dispatch = Dispatch::new(subscriber);
|
||||
assert!(dispatch.downcast_ref::<format::NewRecorder>().is_some());
|
||||
assert!(dispatch.downcast_ref::<filter::EnvFilter>().is_some());
|
||||
assert!(dispatch.downcast_ref::<format::Format>().is_some())
|
||||
}
|
||||
#[deprecated(since = "0.0.1", note = "moved to `tracing-subscriber::filter`")]
|
||||
pub mod filter {
|
||||
#[doc(inline)]
|
||||
pub use tracing_subscriber::Filter as EnvFilter;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ tokio_02 = { package = "tokio", version = "0.2.0-alpha.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = "0.1.22"
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
tracing-core = "0.1.2"
|
||||
|
||||
[badges]
|
||||
|
@ -17,7 +17,11 @@ use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder().finish();
|
||||
use tracing_subscriber::{fmt, Filter};
|
||||
|
||||
let subscriber = fmt::Subscriber::builder()
|
||||
.with_filter(Filter::from_default_env())
|
||||
.finish();
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
let listen_addr = env::args()
|
||||
|
@ -32,9 +32,8 @@ fn subtask(number: usize) -> impl Future<Item = usize, Error = ()> {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter("trace".parse::<tracing_fmt::filter::EnvFilter>().unwrap())
|
||||
.finish();
|
||||
use tracing_subscriber::fmt;
|
||||
let subscriber = fmt::Subscriber::builder().with_filter("trace").finish();
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
tokio::run(parent_task(10));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.0.1-alpha.2"
|
||||
version = "0.0.1-alpha.3"
|
||||
authors = ["Eliza Weisman <eliza@buoyant.io>", "Tokio Contributors <team@tokio.rs>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
@ -19,8 +19,11 @@ categories = [
|
||||
keywords = ["logging", "tracing", "metrics", "subscriber"]
|
||||
|
||||
[features]
|
||||
default = ["filter", "smallvec"]
|
||||
|
||||
default = ["filter", "smallvec", "fmt", "ansi", "chrono", "tracing-log"]
|
||||
filter = ["matchers", "regex", "lazy_static"]
|
||||
fmt = ["owning_ref", "parking_lot"]
|
||||
ansi = ["fmt", "ansi_term"]
|
||||
|
||||
[dependencies]
|
||||
tracing-core = "0.1.2"
|
||||
@ -32,8 +35,14 @@ regex = { optional = true, version = "1" }
|
||||
smallvec = { optional = true, version = "0.6.10"}
|
||||
lazy_static = { optional = true, version = "1" }
|
||||
|
||||
# fmt
|
||||
tracing-log = { version = "0.0.1-alpha.1", optional = true }
|
||||
ansi_term = { version = "0.11", optional = true }
|
||||
owning_ref = { version = "0.4.0", optional = true }
|
||||
parking_lot = { version = ">= 0.7, < 0.10", features = ["owning_ref"], optional = true }
|
||||
chrono = { version = "0.4", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing = "0.1"
|
||||
|
||||
[badges]
|
||||
|
23
tracing-subscriber/examples/filter.rs
Normal file
23
tracing-subscriber/examples/filter.rs
Normal file
@ -0,0 +1,23 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
#[path = "fmt/yak_shave.rs"]
|
||||
mod yak_shave;
|
||||
|
||||
fn main() {
|
||||
use tracing_subscriber::{fmt, Filter};
|
||||
|
||||
let subscriber = fmt::Subscriber::builder()
|
||||
.with_filter(Filter::from_default_env())
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
let number_of_yaks = 3;
|
||||
tracing::debug!("preparing to shave {} yaks", number_of_yaks);
|
||||
|
||||
let number_shaved = yak_shave::shave_all(number_of_yaks);
|
||||
|
||||
tracing::debug!(
|
||||
message = "yak shaving completed.",
|
||||
all_yaks_shaved = number_shaved == number_of_yaks,
|
||||
);
|
||||
});
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
#[tracing::instrument]
|
||||
fn shave(yak: usize) -> bool {
|
||||
tracing::debug!(
|
||||
message = "hello! I'm gonna shave a yak.",
|
||||
excitement = "yay!"
|
||||
);
|
||||
if yak == 3 {
|
||||
tracing::warn!(target: "yak_events", "could not locate yak!");
|
||||
false
|
||||
} else {
|
||||
tracing::trace!(target: "yak_events", "yak shaved successfully");
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn shave_all(yaks: usize) -> usize {
|
||||
let span = tracing::span!(Level::TRACE, "shaving_yaks", yaks_to_shave = yaks);
|
||||
let _enter = span.enter();
|
||||
|
||||
tracing::info!("shaving yaks");
|
||||
|
||||
let mut num_shaved = 0;
|
||||
for yak in 1..=yaks {
|
||||
let shaved = shave(yak);
|
||||
tracing::trace!(target: "yak_events", yak, shaved);
|
||||
|
||||
if !shaved {
|
||||
tracing::error!(message = "failed to shave yak!", yak);
|
||||
} else {
|
||||
num_shaved += 1;
|
||||
}
|
||||
|
||||
tracing::trace!(target: "yak_events", yaks_shaved = num_shaved);
|
||||
}
|
||||
|
||||
num_shaved
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
// Disable `tracing-fmt`'s filter implementation...
|
||||
.with_filter(tracing_fmt::filter::none())
|
||||
.finish()
|
||||
// and use `tracing-subscriber`'s filter layer instead.
|
||||
.with(tracing_subscriber::filter::Filter::from_default_env());
|
||||
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
let number_of_yaks = 3;
|
||||
tracing::debug!("preparing to shave {} yaks", number_of_yaks);
|
||||
|
||||
let number_shaved = shave_all(number_of_yaks);
|
||||
|
||||
tracing::debug!(
|
||||
message = "yak shaving completed.",
|
||||
all_yaks_shaved = number_shaved == number_of_yaks,
|
||||
);
|
||||
});
|
||||
}
|
20
tracing-subscriber/examples/fmt/fmt.rs
Normal file
20
tracing-subscriber/examples/fmt/fmt.rs
Normal file
@ -0,0 +1,20 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
use tracing::debug;
|
||||
|
||||
mod yak_shave;
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::new();
|
||||
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
let number_of_yaks = 3;
|
||||
debug!("preparing to shave {} yaks", number_of_yaks);
|
||||
|
||||
let number_shaved = yak_shave::shave_all(number_of_yaks);
|
||||
|
||||
debug!(
|
||||
message = "yak shaving completed.",
|
||||
all_yaks_shaved = number_shaved == number_of_yaks,
|
||||
);
|
||||
});
|
||||
}
|
92
tracing-subscriber/examples/fmt/yak_shave.rs
Normal file
92
tracing-subscriber/examples/fmt/yak_shave.rs
Normal file
@ -0,0 +1,92 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
use tracing::{debug, error, info, span, trace, warn, Level};
|
||||
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn shave(yak: usize) -> Result<(), Box<dyn Error + 'static>> {
|
||||
debug!(
|
||||
message = "hello! I'm gonna shave a yak.",
|
||||
excitement = "yay!"
|
||||
);
|
||||
if yak == 3 {
|
||||
warn!(target: "yak_events", "could not locate yak!");
|
||||
Err(ShaveError::new(yak, YakError::new("could not locate yak")))?;
|
||||
} else {
|
||||
trace!(target: "yak_events", "yak shaved successfully");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn shave_all(yaks: usize) -> usize {
|
||||
let span = span!(Level::TRACE, "shaving_yaks", yaks_to_shave = yaks);
|
||||
let _enter = span.enter();
|
||||
|
||||
info!("shaving yaks");
|
||||
|
||||
let mut num_shaved = 0;
|
||||
for yak in 1..=yaks {
|
||||
let res = shave(yak);
|
||||
trace!(target: "yak_events", yak, shaved = res.is_ok());
|
||||
|
||||
if let Err(ref error) = res {
|
||||
error!(
|
||||
message = "failed to shave yak!",
|
||||
yak,
|
||||
error = error.as_ref()
|
||||
);
|
||||
} else {
|
||||
num_shaved += 1;
|
||||
}
|
||||
|
||||
trace!(target: "yak_events", yaks_shaved = num_shaved);
|
||||
}
|
||||
|
||||
num_shaved
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ShaveError {
|
||||
source: Box<dyn Error + 'static>,
|
||||
yak: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for ShaveError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "shaving yak #{} failed!", self.yak)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ShaveError {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self.source.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl ShaveError {
|
||||
fn new(yak: usize, source: impl Into<Box<dyn Error + 'static>>) -> Self {
|
||||
Self {
|
||||
source: source.into(),
|
||||
yak,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct YakError {
|
||||
description: &'static str,
|
||||
}
|
||||
|
||||
impl fmt::Display for YakError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.description)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for YakError {}
|
||||
|
||||
impl YakError {
|
||||
fn new(description: &'static str) -> Self {
|
||||
Self { description }
|
||||
}
|
||||
}
|
13
tracing-subscriber/examples/fmt_stderr.rs
Normal file
13
tracing-subscriber/examples/fmt_stderr.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
use std::io;
|
||||
use tracing::error;
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_writer(io::stderr)
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::with_default(subscriber, || {
|
||||
error!("This event will be printed to `stderr`.");
|
||||
});
|
||||
}
|
@ -222,6 +222,15 @@ impl FromStr for Filter {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<S> for Filter
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn from(s: S) -> Self {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Filter {
|
||||
fn default() -> Self {
|
||||
Self::from_directives(std::iter::empty())
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Formatters for logging `tracing` events.
|
||||
use crate::span;
|
||||
use crate::time::{self, FormatTime, SystemTime};
|
||||
use super::span;
|
||||
use super::time::{self, FormatTime, SystemTime};
|
||||
#[cfg(feature = "tracing-log")]
|
||||
use tracing_log::NormalizeEvent;
|
||||
|
||||
@ -131,7 +131,7 @@ impl<F, T> Format<F, T> {
|
||||
|
||||
impl<N, T> FormatEvent<N> for Format<Full, T>
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
T: FormatTime,
|
||||
{
|
||||
fn format_event(
|
||||
@ -168,7 +168,7 @@ where
|
||||
|
||||
impl<N, T> FormatEvent<N> for Format<Compact, T>
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
T: FormatTime,
|
||||
{
|
||||
fn format_event(
|
||||
@ -226,7 +226,7 @@ impl<'a> Recorder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::NewVisitor<'a> for NewRecorder {
|
||||
impl<'a> super::NewVisitor<'a> for NewRecorder {
|
||||
type Visitor = Recorder<'a>;
|
||||
|
||||
#[inline]
|
||||
@ -282,7 +282,7 @@ impl<'a, N: 'a> FmtCtx<'a, N> {
|
||||
#[cfg(feature = "ansi")]
|
||||
impl<'a, N> fmt::Display for FmtCtx<'a, N>
|
||||
where
|
||||
N: crate::NewVisitor<'a>,
|
||||
N: super::NewVisitor<'a>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut seen = false;
|
||||
@ -337,7 +337,7 @@ impl<'a, N: 'a> FullCtx<'a, N> {
|
||||
#[cfg(feature = "ansi")]
|
||||
impl<'a, N> fmt::Display for FullCtx<'a, N>
|
||||
where
|
||||
N: crate::NewVisitor<'a>,
|
||||
N: super::NewVisitor<'a>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut seen = false;
|
475
tracing-subscriber/src/fmt/mod.rs
Normal file
475
tracing-subscriber/src/fmt/mod.rs
Normal file
@ -0,0 +1,475 @@
|
||||
//! A `Subscriber` for formatting and logging `tracing` data.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! [`tracing`] is a framework for instrumenting Rust programs with context-aware,
|
||||
//! structured, event-based diagnostic information. This crate provides an
|
||||
//! implementation of the [`Subscriber`] trait that records `tracing`'s `Event`s
|
||||
//! and `Span`s by formatting them as text and logging them to stdout.
|
||||
//!
|
||||
//!
|
||||
//! [`tracing`]: https://crates.io/crates/tracing
|
||||
//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
|
||||
use tracing_core::{field, subscriber::Interest, Event, Metadata};
|
||||
|
||||
use std::{any::TypeId, cell::RefCell, fmt, io};
|
||||
pub mod format;
|
||||
mod span;
|
||||
pub mod time;
|
||||
pub mod writer;
|
||||
|
||||
use crate::layer::{self, Layer};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{format::FormatEvent, span::Context, writer::MakeWriter};
|
||||
|
||||
/// A `Subscriber` that logs formatted representations of `tracing` events.
|
||||
#[derive(Debug)]
|
||||
pub struct Subscriber<
|
||||
N = format::NewRecorder,
|
||||
E = format::Format<format::Full>,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
new_visitor: N,
|
||||
fmt_event: E,
|
||||
spans: span::Store,
|
||||
settings: Settings,
|
||||
make_writer: W,
|
||||
}
|
||||
|
||||
/// Configures and constructs `Subscriber`s.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Builder<
|
||||
N = format::NewRecorder,
|
||||
E = format::Format<format::Full>,
|
||||
F = layer::Identity,
|
||||
W = fn() -> io::Stdout,
|
||||
> {
|
||||
filter: F,
|
||||
new_visitor: N,
|
||||
fmt_event: E,
|
||||
settings: Settings,
|
||||
make_writer: W,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Settings {
|
||||
inherit_fields: bool,
|
||||
}
|
||||
|
||||
impl Subscriber {
|
||||
pub fn builder() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Subscriber {
|
||||
fn default() -> Self {
|
||||
Builder::default().finish().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for layer::Layered<layer::Identity, Subscriber> {
|
||||
fn default() -> Self {
|
||||
Builder::default().finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "filter")]
|
||||
impl Default for layer::Layered<crate::Filter, Subscriber> {
|
||||
fn default() -> Self {
|
||||
Builder::default()
|
||||
.with_filter(crate::filter::Filter::from_default_env())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, W> Subscriber<N, E, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a>,
|
||||
{
|
||||
#[inline]
|
||||
fn ctx(&self) -> span::Context<'_, N> {
|
||||
span::Context::new(&self.spans, &self.new_visitor)
|
||||
}
|
||||
}
|
||||
|
||||
// impl<N, E, W> Subscriber<N, E, filter::ReloadFilter<F, N>, W>
|
||||
// where
|
||||
// F: Filter<N> + 'static,
|
||||
// {
|
||||
// /// Returns a `Handle` that may be used to reload this subscriber's
|
||||
// /// filter.
|
||||
// pub fn reload_handle(&self) -> filter::reload::Handle<F, N> {
|
||||
// self.filter.handle()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<N, E, W> tracing_core::Subscriber for Subscriber<N, E, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
{
|
||||
fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest {
|
||||
Interest::always()
|
||||
}
|
||||
|
||||
fn enabled(&self, _: &Metadata<'_>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id {
|
||||
self.spans.new_span(attrs, &self.new_visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
|
||||
self.spans.record(span, values, &self.new_visitor)
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {
|
||||
// TODO: implement this please
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event<'_>) {
|
||||
thread_local! {
|
||||
static BUF: RefCell<String> = RefCell::new(String::new());
|
||||
}
|
||||
|
||||
BUF.with(|buf| {
|
||||
let borrow = buf.try_borrow_mut();
|
||||
let mut a;
|
||||
let mut b;
|
||||
let buf = match borrow {
|
||||
Ok(buf) => {
|
||||
a = buf;
|
||||
&mut *a
|
||||
}
|
||||
_ => {
|
||||
b = String::new();
|
||||
&mut b
|
||||
}
|
||||
};
|
||||
|
||||
if self.fmt_event.format_event(&self.ctx(), buf, event).is_ok() {
|
||||
let mut writer = self.make_writer.make_writer();
|
||||
let _ = io::Write::write_all(&mut writer, buf.as_bytes());
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
});
|
||||
}
|
||||
|
||||
fn enter(&self, id: &span::Id) {
|
||||
// TODO: add on_enter hook
|
||||
self.spans.push(id);
|
||||
}
|
||||
|
||||
fn exit(&self, id: &span::Id) {
|
||||
self.spans.pop(id);
|
||||
}
|
||||
|
||||
fn current_span(&self) -> span::Current {
|
||||
if let Some(id) = self.spans.current() {
|
||||
if let Some(meta) = self.spans.get(&id).map(|span| span.metadata()) {
|
||||
return span::Current::new(id, meta);
|
||||
}
|
||||
}
|
||||
span::Current::none()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_span(&self, id: &span::Id) -> span::Id {
|
||||
self.spans.clone_span(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_close(&self, id: span::Id) -> bool {
|
||||
self.spans.drop_span(id)
|
||||
}
|
||||
|
||||
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
|
||||
match () {
|
||||
_ if id == TypeId::of::<Self>() => Some(self as *const Self as *const ()),
|
||||
// _ if id == TypeId::of::<F>() => Some(&self.filter as *const F as *const ()),
|
||||
_ if id == TypeId::of::<E>() => Some(&self.fmt_event as *const E as *const ()),
|
||||
_ if id == TypeId::of::<N>() => Some(&self.new_visitor as *const N as *const ()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NewVisitor<'a> {
|
||||
type Visitor: field::Visit + 'a;
|
||||
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor;
|
||||
}
|
||||
|
||||
impl<'a, F, R> NewVisitor<'a> for F
|
||||
where
|
||||
F: Fn(&'a mut dyn fmt::Write, bool) -> R,
|
||||
R: field::Visit + 'a,
|
||||
{
|
||||
type Visitor = R;
|
||||
|
||||
#[inline]
|
||||
fn make(&self, writer: &'a mut dyn fmt::Write, is_empty: bool) -> Self::Visitor {
|
||||
(self)(writer, is_empty)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Builder =====
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder {
|
||||
filter: layer::identity(),
|
||||
new_visitor: format::NewRecorder,
|
||||
fmt_event: format::Format::default(),
|
||||
settings: Settings::default(),
|
||||
make_writer: io::stdout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
E: FormatEvent<N> + 'static,
|
||||
W: MakeWriter + 'static,
|
||||
F: Layer<Subscriber<N, E, W>> + 'static,
|
||||
{
|
||||
pub fn finish(self) -> layer::Layered<F, Subscriber<N, E, W>> {
|
||||
let subscriber = Subscriber {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
spans: span::Store::with_capacity(32),
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
};
|
||||
self.filter.with_subscriber(subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, L, T, F, W> Builder<N, format::Format<L, T>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
{
|
||||
/// Use the given `timer` for log message timestamps.
|
||||
pub fn with_timer<T2>(self, timer: T2) -> Builder<N, format::Format<L, T2>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event.with_timer(timer),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not emit timestamps with log messages.
|
||||
pub fn without_time(self) -> Builder<N, format::Format<L, ()>, F, W> {
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event.without_time(),
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable ANSI encoding for formatted events.
|
||||
#[cfg(feature = "ansi")]
|
||||
pub fn with_ansi(self, ansi: bool) -> Builder<N, format::Format<L, T>, F, W> {
|
||||
Builder {
|
||||
fmt_event: self.fmt_event.with_ansi(ansi),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether or not an event's target is displayed.
|
||||
pub fn with_target(self, display_target: bool) -> Builder<N, format::Format<L, T>, F, W> {
|
||||
Builder {
|
||||
fmt_event: self.fmt_event.with_target(display_target),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "filter")]
|
||||
impl<N, E, W> Builder<N, E, crate::Filter, W>
|
||||
where
|
||||
Subscriber<N, E, W>: tracing_core::Subscriber + 'static,
|
||||
{
|
||||
/// Configures the subscriber being built to allow filter reloading at
|
||||
/// runtime.
|
||||
pub fn with_filter_reloading(
|
||||
self,
|
||||
) -> Builder<N, E, crate::reload::Layer<crate::Filter, Subscriber<N, E, W>>, W> {
|
||||
let (filter, _) = crate::reload::Layer::new(self.filter);
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "filter")]
|
||||
impl<N, E, W> Builder<N, E, crate::reload::Layer<crate::Filter, Subscriber<N, E, W>>, W>
|
||||
where
|
||||
Subscriber<N, E, W>: tracing_core::Subscriber + 'static,
|
||||
{
|
||||
/// Returns a `Handle` that may be used to reload the constructed subscriber's
|
||||
/// filter.
|
||||
pub fn reload_handle(&self) -> crate::reload::Handle<crate::Filter, Subscriber<N, E, W>> {
|
||||
self.filter.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// Sets the Visitor that the subscriber being built will use to record
|
||||
/// fields.
|
||||
pub fn with_visitor<N2>(self, new_visitor: N2) -> Builder<N2, E, F, W>
|
||||
where
|
||||
N2: for<'a> NewVisitor<'a> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the filter that the subscriber being built will use to determine if
|
||||
/// a span or event is enabled.
|
||||
#[cfg(feature = "filter")]
|
||||
pub fn with_filter(self, filter: impl Into<crate::Filter>) -> Builder<N, E, crate::Filter, W>
|
||||
where
|
||||
Subscriber<N, E, W>: tracing_core::Subscriber + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: filter.into(),
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the subscriber being built to use a less verbose formatter.
|
||||
///
|
||||
/// See [`format::Compact`].
|
||||
pub fn compact(self) -> Builder<N, format::Format<format::Compact>, F, W>
|
||||
where
|
||||
N: for<'a> NewVisitor<'a> + 'static,
|
||||
{
|
||||
Builder {
|
||||
fmt_event: format::Format::default().compact(),
|
||||
filter: self.filter,
|
||||
new_visitor: self.new_visitor,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the function that the subscriber being built should use to format
|
||||
/// events that occur.
|
||||
pub fn on_event<E2>(self, fmt_event: E2) -> Builder<N, E2, F, W>
|
||||
where
|
||||
E2: FormatEvent<N> + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer: self.make_writer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether or not spans inherit their parents' field values (disabled
|
||||
/// by default).
|
||||
pub fn inherit_fields(self, inherit_fields: bool) -> Self {
|
||||
Builder {
|
||||
settings: Settings { inherit_fields },
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, E, F, W> Builder<N, E, F, W> {
|
||||
/// Sets the [`MakeWriter`] that the subscriber being built will use to write events.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Using `stderr` rather than `stdout`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io;
|
||||
///
|
||||
/// let subscriber = tracing_subscriber::fmt::Subscriber::builder()
|
||||
/// .with_writer(io::stderr)
|
||||
/// .finish();
|
||||
/// ```
|
||||
///
|
||||
/// [`MakeWriter`]: trait.MakeWriter.html
|
||||
pub fn with_writer<W2>(self, make_writer: W2) -> Builder<N, E, F, W2>
|
||||
where
|
||||
W2: MakeWriter + 'static,
|
||||
{
|
||||
Builder {
|
||||
new_visitor: self.new_visitor,
|
||||
fmt_event: self.fmt_event,
|
||||
filter: self.filter,
|
||||
settings: self.settings,
|
||||
make_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use tracing_core::dispatcher::Dispatch;
|
||||
|
||||
#[test]
|
||||
fn impls() {
|
||||
let f = format::Format::default().with_timer(time::Uptime::default());
|
||||
let subscriber = Subscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
|
||||
let f = format::Format::default();
|
||||
let subscriber = Subscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
|
||||
let f = format::Format::default().compact();
|
||||
let subscriber = Subscriber::builder().on_event(f).finish();
|
||||
let _dispatch = Dispatch::new(subscriber);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscriber_downcasts() {
|
||||
let subscriber = Subscriber::new();
|
||||
let dispatch = Dispatch::new(subscriber);
|
||||
assert!(dispatch.downcast_ref::<Subscriber>().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscriber_downcasts_to_parts() {
|
||||
let subscriber = Subscriber::builder().finish();
|
||||
let dispatch = Dispatch::new(subscriber);
|
||||
assert!(dispatch.downcast_ref::<format::NewRecorder>().is_some());
|
||||
assert!(dispatch.downcast_ref::<crate::layer::Identity>().is_some());
|
||||
assert!(dispatch.downcast_ref::<format::Format>().is_some())
|
||||
}
|
||||
}
|
@ -192,7 +192,7 @@ impl<'a, N> Context<'a, N> {
|
||||
is_empty: bool,
|
||||
) -> N::Visitor
|
||||
where
|
||||
N: crate::NewVisitor<'writer>,
|
||||
N: super::NewVisitor<'writer>,
|
||||
{
|
||||
self.new_visitor.make(writer, is_empty)
|
||||
}
|
||||
@ -263,7 +263,7 @@ impl Store {
|
||||
#[inline]
|
||||
pub fn new_span<N>(&self, attrs: &Attributes<'_>, new_visitor: &N) -> Id
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
{
|
||||
let mut span = Some(Data::new(attrs, self));
|
||||
|
||||
@ -339,7 +339,7 @@ impl Store {
|
||||
#[inline]
|
||||
pub fn record<N>(&self, id: &Id, fields: &Record<'_>, new_recorder: &N)
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
{
|
||||
let slab = self.inner.read();
|
||||
let slot = slab.write_slot(id_to_idx(id));
|
||||
@ -429,7 +429,7 @@ impl Drop for Data {
|
||||
impl Slot {
|
||||
fn new<N>(mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> Self
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
{
|
||||
let mut fields = String::new();
|
||||
{
|
||||
@ -454,7 +454,7 @@ impl Slot {
|
||||
|
||||
fn fill<N>(&mut self, mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> usize
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
{
|
||||
let fields = &mut self.fields;
|
||||
{
|
||||
@ -472,7 +472,7 @@ impl Slot {
|
||||
|
||||
fn record<N>(&mut self, fields: &Record<'_>, new_visitor: &N)
|
||||
where
|
||||
N: for<'a> crate::NewVisitor<'a>,
|
||||
N: for<'a> super::NewVisitor<'a>,
|
||||
{
|
||||
let state = &mut self.span;
|
||||
let buf = &mut self.fields;
|
@ -54,7 +54,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{FmtSubscriber, MakeWriter};
|
||||
use super::MakeWriter;
|
||||
use crate::fmt::Subscriber;
|
||||
use lazy_static::lazy_static;
|
||||
use std::io;
|
||||
use std::sync::{Mutex, MutexGuard, TryLockError};
|
||||
@ -65,7 +66,7 @@ mod test {
|
||||
where
|
||||
T: MakeWriter + Send + Sync + 'static,
|
||||
{
|
||||
let subscriber = FmtSubscriber::builder()
|
||||
let subscriber = Subscriber::builder()
|
||||
.with_writer(make_writer)
|
||||
.without_time()
|
||||
.with_ansi(false)
|
||||
@ -76,7 +77,7 @@ mod test {
|
||||
error!("{}", msg);
|
||||
});
|
||||
|
||||
let expected = format!("ERROR tracing_fmt::writer::test: {}\n", msg);
|
||||
let expected = format!("ERROR {}: {}\n", module_path!(), msg);
|
||||
let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap();
|
||||
assert!(actual.contains(expected.as_str()));
|
||||
}
|
@ -403,6 +403,12 @@ pub struct Layered<L, I, S = I> {
|
||||
_s: PhantomData<fn(S)>,
|
||||
}
|
||||
|
||||
/// A layer that does nothing.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Identity {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
// === impl Layered ===
|
||||
|
||||
impl<L, S> Subscriber for Layered<L, S>
|
||||
@ -603,6 +609,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, S> Layered<L, S> {
|
||||
// TODO(eliza): is there a compelling use-case for this being public?
|
||||
pub(crate) fn into_inner(self) -> S {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
// === impl SubscriberExt ===
|
||||
|
||||
impl<S: Subscriber> crate::sealed::Sealed for S {}
|
||||
@ -678,6 +691,20 @@ impl<'a, S> Clone for Context<'a, S> {
|
||||
}
|
||||
}
|
||||
|
||||
// === impl Identity ===
|
||||
//
|
||||
impl<S: Subscriber> Layer<S> for Identity {}
|
||||
|
||||
impl Identity {
|
||||
pub fn new() -> Self {
|
||||
Self { _p: () }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identity() -> Identity {
|
||||
Identity::new()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
@ -30,6 +30,8 @@ macro_rules! try_lock {
|
||||
|
||||
#[cfg(feature = "filter")]
|
||||
pub mod filter;
|
||||
#[cfg(feature = "fmt")]
|
||||
pub mod fmt;
|
||||
pub mod layer;
|
||||
pub mod prelude;
|
||||
pub mod reload;
|
||||
@ -39,6 +41,9 @@ pub(crate) mod thread;
|
||||
pub use filter::Filter;
|
||||
pub use layer::Layer;
|
||||
|
||||
#[cfg(feature = "fmt")]
|
||||
pub use fmt::Subscriber as FmtSubscriber;
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
#[deprecated(since = "0.0.1-alpha.2", note = "renamed to `CurrentSpan`")]
|
||||
|
@ -33,8 +33,7 @@ string = { git = "https://github.com/carllerche/string" }
|
||||
tokio = "0.1"
|
||||
tokio-current-thread = "0.1.1"
|
||||
tokio-connect = { git = "https://github.com/carllerche/tokio-connect" }
|
||||
tracing-subscriber = { path = "../tracing-subscriber" }
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
tokio-io = "0.1"
|
||||
ansi_term = "0.11"
|
||||
humantime = "1.1.1"
|
||||
|
@ -92,10 +92,8 @@ impl tower_service::Service<()> for NewSvc {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from(
|
||||
"tower_h2_server=trace",
|
||||
))
|
||||
let subscriber = tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_filter("tower_h2_server=trace")
|
||||
.finish();
|
||||
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
|
@ -22,8 +22,7 @@ default = ["tower-layer", "tower-util", "http"]
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
tracing-futures = { path = "../tracing-futures" }
|
||||
tracing-subscriber = { path = "../tracing-subscriber" }
|
||||
tracing-futures = { version = "0.0.1-alpha.1", path = "../tracing-futures" }
|
||||
futures = "0.1"
|
||||
tower-service = "0.2"
|
||||
tower-layer = { version = "0.1", optional = true }
|
||||
@ -46,7 +45,7 @@ tokio-buf = "0.1"
|
||||
tower = "0.1"
|
||||
tower-hyper = "0.1"
|
||||
tower-http-util = "0.1"
|
||||
tracing-fmt = "0.0.1-alpha.3"
|
||||
tracing-subscriber = { version = "0.0.1-alpha.3", path = "../tracing-subscriber" }
|
||||
rand = "0.7"
|
||||
|
||||
[badges]
|
||||
|
@ -20,10 +20,8 @@ pub struct Conn(SocketAddr);
|
||||
fn main() {
|
||||
// Set the default subscriber to record all traces emitted by this example
|
||||
// and by the `tracing_tower` library's helpers.
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from(
|
||||
"h2_client=trace,tracing_tower=trace",
|
||||
))
|
||||
let subscriber = tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_filter("h2_client=trace,tracing_tower=trace")
|
||||
.finish();
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
|
||||
|
@ -95,10 +95,8 @@ impl tower_service::Service<()> for NewSvc {
|
||||
fn main() {
|
||||
// Set the default subscriber to record all traces emitted by this example
|
||||
// and by the `tracing_tower` library's helpers.
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::EnvFilter::from(
|
||||
"h2_server=trace,tracing_tower=trace",
|
||||
))
|
||||
let subscriber = tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_filter("h2_server=trace,tracing_tower=trace")
|
||||
.finish();
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
|
||||
|
@ -33,16 +33,15 @@ use std::{error::Error, fmt, net::SocketAddr};
|
||||
use tracing;
|
||||
use tracing_futures::Instrument;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
fn main() {
|
||||
let filter = tracing_subscriber::filter::Filter::new("info,load=debug");
|
||||
let (filter, handle) = tracing_subscriber::reload::Layer::new(filter);
|
||||
let subscriber = tracing_fmt::FmtSubscriber::builder()
|
||||
.with_filter(tracing_fmt::filter::none())
|
||||
.finish()
|
||||
.with(filter);
|
||||
let builder = FmtSubscriber::builder()
|
||||
.with_filter("info,load=debug")
|
||||
.with_filter_reloading();
|
||||
let handle = builder.reload_handle();
|
||||
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
let _ = tracing::subscriber::set_global_default(builder.finish());
|
||||
|
||||
let addr = "[::1]:3000".parse().unwrap();
|
||||
let admin_addr = "[::1]:3001".parse().unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user