diff --git a/nightly-examples/Cargo.toml b/nightly-examples/Cargo.toml index 0a323e89..a27e4a52 100644 --- a/nightly-examples/Cargo.toml +++ b/nightly-examples/Cargo.toml @@ -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" } diff --git a/nightly-examples/examples/async_fn.rs b/nightly-examples/examples/async_fn.rs index 960e23dc..e9576804 100644 --- a/nightly-examples/examples/async_fn.rs +++ b/nightly-examples/examples/async_fn.rs @@ -43,8 +43,8 @@ async fn write(stream: &mut TcpStream) -> io::Result { pub async fn main() -> Result<(), Box> { 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(); diff --git a/nightly-examples/examples/echo.rs b/nightly-examples/examples/echo.rs index eac951e1..32739559 100644 --- a/nightly-examples/examples/echo.rs +++ b/nightly-examples/examples/echo.rs @@ -37,8 +37,8 @@ use tracing_futures::Instrument; #[tokio::main] async fn main() -> Result<(), Box> { - 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)?; diff --git a/nightly-examples/examples/proxy_server.rs b/nightly-examples/examples/proxy_server.rs index fbc920ba..dace33d3 100644 --- a/nightly-examples/examples/proxy_server.rs +++ b/nightly-examples/examples/proxy_server.rs @@ -38,10 +38,10 @@ use std::{env, net::SocketAddr}; #[instrument] async fn transfer( - inbound: TcpStream, + mut inbound: TcpStream, proxy_addr: SocketAddr, ) -> Result<(), Box> { - 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> { - 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)?; diff --git a/nightly-examples/examples/spawny_thing.rs b/nightly-examples/examples/spawny_thing.rs index bc5fc52c..5e99aaed 100644 --- a/nightly-examples/examples/spawny_thing.rs +++ b/nightly-examples/examples/spawny_thing.rs @@ -38,9 +38,7 @@ async fn subtask(number: usize) -> usize { #[tokio::main] async fn main() -> Result<(), Box> { - let subscriber = tracing_fmt::FmtSubscriber::builder() - .with_filter("trace".parse::()?) - .finish(); + let subscriber = tracing_subscriber::fmt::Subscriber::new(); tracing::subscriber::set_global_default(subscriber)?; parent_task(10).await; Ok(()) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 3b6973bc..b6cc5f1e 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -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" } diff --git a/tracing-attributes/examples/args.rs b/tracing-attributes/examples/args.rs index 9f74e6ef..71e136e1 100644 --- a/tracing-attributes/examples/args.rs +++ b/tracing-attributes/examples/args.rs @@ -27,8 +27,9 @@ fn fibonacci_seq(to: u64) -> Vec { } 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, || { diff --git a/tracing-attributes/examples/basic.rs b/tracing-attributes/examples/basic.rs index 4c9e128e..1e0318f6 100644 --- a/tracing-attributes/examples/basic.rs +++ b/tracing-attributes/examples/basic.rs @@ -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; diff --git a/tracing-env-logger/Cargo.toml b/tracing-env-logger/Cargo.toml index 6e401eac..b8f313ed 100644 --- a/tracing-env-logger/Cargo.toml +++ b/tracing-env-logger/Cargo.toml @@ -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" diff --git a/tracing-env-logger/examples/hyper-echo.rs b/tracing-env-logger/examples/hyper-echo.rs index bba9fd27..f5dcfc8f 100644 --- a/tracing-env-logger/examples/hyper-echo.rs +++ b/tracing-env-logger/examples/hyper-echo.rs @@ -107,7 +107,7 @@ fn echo(req: Request) -> Instrumented { } fn main() -> Result<(), Box> { - 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) diff --git a/tracing-fmt/Cargo.toml b/tracing-fmt/Cargo.toml index 91b0d7c2..7b28772d 100644 --- a/tracing-fmt/Cargo.toml +++ b/tracing-fmt/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "tracing-fmt" -version = "0.0.1-alpha.3" +version = "0.0.1" authors = ["Eliza Weisman "] 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" } diff --git a/tracing-fmt/README.md b/tracing-fmt/README.md index f79a63d3..64f4581b 100644 --- a/tracing-fmt/README.md +++ b/tracing-fmt/README.md @@ -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] +![deprecated](https://img.shields.io/badge/maintenance-deprecated-red.svg) + [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/ diff --git a/tracing-fmt/src/filter/env.rs b/tracing-fmt/src/filter/env.rs deleted file mode 100644 index 93ec7404..00000000 --- a/tracing-fmt/src/filter/env.rs +++ /dev/null @@ -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, - 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, - in_span: Option, - // TODO: this can probably be a `SmallVec` someday, since a span won't have - // over 32 fields. - fields: Vec, - 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>(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>(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>(dirs: S) -> Result { - 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::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>(env: A) -> Result { - env::var(env.as_ref())?.parse().map_err(Into::into) - } - - fn new2(mut directives: Vec) -> 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 + '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 From for EnvFilter -where - S: AsRef, -{ - fn from(s: S) -> Self { - Self::new(s) - } -} - -impl FromStr for EnvFilter { - type Err = ParseError; - fn from_str(s: &str) -> Result { - Self::try_new(s) - } -} - -impl Default for EnvFilter { - fn default() -> Self { - Self::new2(Default::default()) - } -} - -impl Filter 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 { - 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, 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 { - lazy_static! { - static ref DIRECTIVE_RE: Regex = Regex::new( - r"(?x) - ^(?Ptrace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF[0-5])$ | - ^ - (?: # target name or span name - (?P[\w:]+)|(?P\[[^\]]*\]) - ){1,2} - (?: # level or nothing - =(?Ptrace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF[0-5])? - )? - $ - " - ) - .unwrap(); - static ref SPAN_PART_RE: Regex = - Regex::new(r#"(?P\w+)?(?:\{(?P[^\}]*)\})?"#).unwrap(); - static ref FIELD_FILTER_RE: Regex = - Regex::new(r#"([\w_0-9]+(?:=(?:[\w0-9]+|".+"))?)(?: |$)"#).unwrap(); - } - - fn parse_level(from: &str) -> Option { - // TODO: maybe this whole function ought to be replaced by a - // `FromStr` impl for `Level` in `tracing_core`...? - from.parse::() - .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::>() - }) - .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 for FromEnvError { - fn from(p: ParseError) -> Self { - Self { - kind: ErrorKind::Parse(p), - } - } -} - -impl From 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 for LevelFilter { - fn eq(&self, other: &Level) -> bool { - match self { - LevelFilter::Off => false, - LevelFilter::Level(l) => l == other, - } - } -} - -impl PartialOrd for LevelFilter { - fn partial_cmp(&self, other: &Level) -> Option { - 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(); - } -} diff --git a/tracing-fmt/src/filter/mod.rs b/tracing-fmt/src/filter/mod.rs deleted file mode 100644 index 4bd20585..00000000 --- a/tracing-fmt/src/filter/mod.rs +++ /dev/null @@ -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 { - 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 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 Filter for NoFilter { - fn enabled(&self, _: &Metadata<'_>, _: &span::Context<'_, N>) -> bool { - true - } -} diff --git a/tracing-fmt/src/filter/reload.rs b/tracing-fmt/src/filter/reload.rs deleted file mode 100644 index 8958aa51..00000000 --- a/tracing-fmt/src/filter/reload.rs +++ /dev/null @@ -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 -where - F: Filter, -{ - inner: Arc>, - _f: PhantomData, -} - -#[derive(Debug)] -pub struct Handle -where - F: Filter, -{ - inner: Weak>, - _f: PhantomData, -} - -#[derive(Debug)] -pub struct Error { - kind: ErrorKind, -} - -#[derive(Debug)] -enum ErrorKind { - SubscriberGone, -} - -// ===== impl ReloadFilter ===== - -impl Filter for ReloadFilter -where - F: Filter, -{ - 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 ReloadFilter -where - F: Filter + 'static, -{ - pub fn new(f: F) -> Self - where - F: Filter, - { - Self { - inner: Arc::new(RwLock::new(f)), - _f: PhantomData, - } - } - - pub fn handle(&self) -> Handle { - Handle { - inner: Arc::downgrade(&self.inner), - _f: PhantomData, - } - } -} - -// ===== impl Handle ===== - -impl Handle -where - F: Filter + 'static, -{ - pub fn reload(&self, new_filter: impl Into) -> 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 - 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(&self, f: impl FnOnce(&F) -> T) -> Result { - let inner = self.inner.upgrade().ok_or(Error { - kind: ErrorKind::SubscriberGone, - })?; - let inner = inner.read(); - Ok(f(&*inner)) - } -} - -impl Clone for Handle -where - F: Filter, -{ - 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 filter::Filter 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"); - } -} diff --git a/tracing-fmt/src/lib.rs b/tracing-fmt/src/lib.rs index 598f5307..d6d60ddb 100644 --- a/tracing-fmt/src/lib.rs +++ b/tracing-fmt/src/lib.rs @@ -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, - 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, - 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 FmtSubscriber -where - N: for<'a> NewVisitor<'a>, -{ - #[inline] - fn ctx(&self) -> span::Context<'_, N> { - span::Context::new(&self.spans, &self.new_visitor) - } -} - -impl FmtSubscriber, W> -where - F: Filter + 'static, -{ - /// Returns a `Handle` that may be used to reload this subscriber's - /// filter. - pub fn reload_handle(&self) -> filter::reload::Handle { - self.filter.handle() - } -} - -impl tracing_core::Subscriber for FmtSubscriber -where - N: for<'a> NewVisitor<'a> + 'static, - E: FormatEvent + 'static, - F: Filter + '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 = 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::() => Some(self as *const Self as *const ()), - _ if id == TypeId::of::() => Some(&self.filter as *const F as *const ()), - _ if id == TypeId::of::() => Some(&self.fmt_event as *const E as *const ()), - _ if id == TypeId::of::() => 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 Builder -where - N: for<'a> NewVisitor<'a> + 'static, - E: FormatEvent + 'static, - F: Filter + 'static, - W: MakeWriter + 'static, -{ - pub fn finish(self) -> FmtSubscriber { - 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 Builder, F, W> -where - N: for<'a> NewVisitor<'a> + 'static, - F: Filter + 'static, -{ - /// Use the given `timer` for log message timestamps. - pub fn with_timer(self, timer: T2) -> Builder, 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, 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, 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, F, W> { - Builder { - fmt_event: self.fmt_event.with_target(display_target), - ..self - } - } -} - -impl Builder -where - F: Filter + 'static, -{ - /// Configures the subscriber being built to allow filter reloading at - /// runtime. - pub fn with_filter_reloading(self) -> Builder, 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 Builder, W> -where - F: Filter + 'static, -{ - /// Returns a `Handle` that may be used to reload the constructed subscriber's - /// filter. - pub fn reload_handle(&self) -> filter::reload::Handle { - self.filter.handle() - } -} - -impl Builder { - /// Sets the Visitor that the subscriber being built will use to record - /// fields. - pub fn with_visitor(self, new_visitor: N2) -> Builder - 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(self, filter: F2) -> Builder - where - F2: Filter + '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, 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(self, fmt_event: E2) -> Builder - where - E2: FormatEvent + '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 Builder { - /// 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(self, make_writer: W2) -> Builder - 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::().is_some()); - } - - #[test] - fn subscriber_downcasts_to_parts() { - let subscriber = FmtSubscriber::new(); - let dispatch = Dispatch::new(subscriber); - assert!(dispatch.downcast_ref::().is_some()); - assert!(dispatch.downcast_ref::().is_some()); - assert!(dispatch.downcast_ref::().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; } diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index b4421717..28ad6e9d 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -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] diff --git a/tracing-futures/examples/proxy_server.rs b/tracing-futures/examples/proxy_server.rs index 46b06608..9ed88ea3 100644 --- a/tracing-futures/examples/proxy_server.rs +++ b/tracing-futures/examples/proxy_server.rs @@ -17,7 +17,11 @@ use tokio::net::{TcpListener, TcpStream}; use tokio::prelude::*; fn main() -> Result<(), Box> { - 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() diff --git a/tracing-futures/examples/spawny-thing.rs b/tracing-futures/examples/spawny-thing.rs index c959f4eb..9e433999 100644 --- a/tracing-futures/examples/spawny-thing.rs +++ b/tracing-futures/examples/spawny-thing.rs @@ -32,9 +32,8 @@ fn subtask(number: usize) -> impl Future { } fn main() { - let subscriber = tracing_fmt::FmtSubscriber::builder() - .with_filter("trace".parse::().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)); } diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 8dc158dd..02dfac95 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.0.1-alpha.2" +version = "0.0.1-alpha.3" authors = ["Eliza Weisman ", "Tokio Contributors "] 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] diff --git a/tracing-subscriber/examples/filter.rs b/tracing-subscriber/examples/filter.rs new file mode 100644 index 00000000..67fccb88 --- /dev/null +++ b/tracing-subscriber/examples/filter.rs @@ -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, + ); + }); +} diff --git a/tracing-subscriber/examples/filter_yakshave.rs b/tracing-subscriber/examples/filter_yakshave.rs deleted file mode 100644 index 922120ee..00000000 --- a/tracing-subscriber/examples/filter_yakshave.rs +++ /dev/null @@ -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, - ); - }); -} diff --git a/tracing-subscriber/examples/fmt/fmt.rs b/tracing-subscriber/examples/fmt/fmt.rs new file mode 100644 index 00000000..e2b91a38 --- /dev/null +++ b/tracing-subscriber/examples/fmt/fmt.rs @@ -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, + ); + }); +} diff --git a/tracing-subscriber/examples/fmt/yak_shave.rs b/tracing-subscriber/examples/fmt/yak_shave.rs new file mode 100644 index 00000000..d9225996 --- /dev/null +++ b/tracing-subscriber/examples/fmt/yak_shave.rs @@ -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> { + 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, + 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>) -> 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 } + } +} diff --git a/tracing-subscriber/examples/fmt_stderr.rs b/tracing-subscriber/examples/fmt_stderr.rs new file mode 100644 index 00000000..d7683bcb --- /dev/null +++ b/tracing-subscriber/examples/fmt_stderr.rs @@ -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`."); + }); +} diff --git a/tracing-subscriber/src/filter/mod.rs b/tracing-subscriber/src/filter/mod.rs index 89565c6e..78ee1c18 100644 --- a/tracing-subscriber/src/filter/mod.rs +++ b/tracing-subscriber/src/filter/mod.rs @@ -222,6 +222,15 @@ impl FromStr for Filter { } } +impl From for Filter +where + S: AsRef, +{ + fn from(s: S) -> Self { + Self::new(s) + } +} + impl Default for Filter { fn default() -> Self { Self::from_directives(std::iter::empty()) diff --git a/tracing-fmt/src/format.rs b/tracing-subscriber/src/fmt/format.rs similarity index 97% rename from tracing-fmt/src/format.rs rename to tracing-subscriber/src/fmt/format.rs index 00d54d40..b9ba7986 100644 --- a/tracing-fmt/src/format.rs +++ b/tracing-subscriber/src/fmt/format.rs @@ -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 Format { impl FormatEvent for Format where - N: for<'a> crate::NewVisitor<'a>, + N: for<'a> super::NewVisitor<'a>, T: FormatTime, { fn format_event( @@ -168,7 +168,7 @@ where impl FormatEvent for Format 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; diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs new file mode 100644 index 00000000..dc9b6ca0 --- /dev/null +++ b/tracing-subscriber/src/fmt/mod.rs @@ -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, + 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, + 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 { + fn default() -> Self { + Builder::default().finish() + } +} + +#[cfg(feature = "filter")] +impl Default for layer::Layered { + fn default() -> Self { + Builder::default() + .with_filter(crate::filter::Filter::from_default_env()) + .finish() + } +} + +impl Subscriber +where + N: for<'a> NewVisitor<'a>, +{ + #[inline] + fn ctx(&self) -> span::Context<'_, N> { + span::Context::new(&self.spans, &self.new_visitor) + } +} + +// impl Subscriber, W> +// where +// F: Filter + 'static, +// { +// /// Returns a `Handle` that may be used to reload this subscriber's +// /// filter. +// pub fn reload_handle(&self) -> filter::reload::Handle { +// self.filter.handle() +// } +// } + +impl tracing_core::Subscriber for Subscriber +where + N: for<'a> NewVisitor<'a> + 'static, + E: FormatEvent + '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 = 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::() => Some(self as *const Self as *const ()), + // _ if id == TypeId::of::() => Some(&self.filter as *const F as *const ()), + _ if id == TypeId::of::() => Some(&self.fmt_event as *const E as *const ()), + _ if id == TypeId::of::() => 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 Builder +where + N: for<'a> NewVisitor<'a> + 'static, + E: FormatEvent + 'static, + W: MakeWriter + 'static, + F: Layer> + 'static, +{ + pub fn finish(self) -> layer::Layered> { + 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 Builder, F, W> +where + N: for<'a> NewVisitor<'a> + 'static, +{ + /// Use the given `timer` for log message timestamps. + pub fn with_timer(self, timer: T2) -> Builder, 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, 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, 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, F, W> { + Builder { + fmt_event: self.fmt_event.with_target(display_target), + ..self + } + } +} + +#[cfg(feature = "filter")] +impl Builder +where + Subscriber: tracing_core::Subscriber + 'static, +{ + /// Configures the subscriber being built to allow filter reloading at + /// runtime. + pub fn with_filter_reloading( + self, + ) -> Builder>, 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 Builder>, W> +where + Subscriber: 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> { + self.filter.handle() + } +} + +impl Builder { + /// Sets the Visitor that the subscriber being built will use to record + /// fields. + pub fn with_visitor(self, new_visitor: N2) -> Builder + 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) -> Builder + where + Subscriber: 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, 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(self, fmt_event: E2) -> Builder + where + E2: FormatEvent + '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 Builder { + /// 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(self, make_writer: W2) -> Builder + 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::().is_some()); + } + + #[test] + fn subscriber_downcasts_to_parts() { + let subscriber = Subscriber::builder().finish(); + let dispatch = Dispatch::new(subscriber); + assert!(dispatch.downcast_ref::().is_some()); + assert!(dispatch.downcast_ref::().is_some()); + assert!(dispatch.downcast_ref::().is_some()) + } +} diff --git a/tracing-fmt/src/span.rs b/tracing-subscriber/src/fmt/span.rs similarity index 98% rename from tracing-fmt/src/span.rs rename to tracing-subscriber/src/fmt/span.rs index ca475cde..cab8b066 100644 --- a/tracing-fmt/src/span.rs +++ b/tracing-subscriber/src/fmt/span.rs @@ -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(&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(&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(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(&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(&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; diff --git a/tracing-fmt/src/time.rs b/tracing-subscriber/src/fmt/time.rs similarity index 100% rename from tracing-fmt/src/time.rs rename to tracing-subscriber/src/fmt/time.rs diff --git a/tracing-fmt/src/writer.rs b/tracing-subscriber/src/fmt/writer.rs similarity index 96% rename from tracing-fmt/src/writer.rs rename to tracing-subscriber/src/fmt/writer.rs index 2f6b3996..7e460f20 100644 --- a/tracing-fmt/src/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -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())); } diff --git a/tracing-subscriber/src/layer.rs b/tracing-subscriber/src/layer.rs index bfacafae..c4de62c7 100644 --- a/tracing-subscriber/src/layer.rs +++ b/tracing-subscriber/src/layer.rs @@ -403,6 +403,12 @@ pub struct Layered { _s: PhantomData, } +/// A layer that does nothing. +#[derive(Clone, Debug)] +pub struct Identity { + _p: (), +} + // === impl Layered === impl Subscriber for Layered @@ -603,6 +609,13 @@ where } } +impl Layered { + // TODO(eliza): is there a compelling use-case for this being public? + pub(crate) fn into_inner(self) -> S { + self.inner + } +} + // === impl SubscriberExt === impl crate::sealed::Sealed for S {} @@ -678,6 +691,20 @@ impl<'a, S> Clone for Context<'a, S> { } } +// === impl Identity === +// +impl Layer for Identity {} + +impl Identity { + pub fn new() -> Self { + Self { _p: () } + } +} + +pub fn identity() -> Identity { + Identity::new() +} + #[cfg(test)] pub(crate) mod tests { use super::*; diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 8ec131f4..c23571f4 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -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`")] diff --git a/tracing-tower-http/Cargo.toml b/tracing-tower-http/Cargo.toml index 5eafba87..8390edf4 100644 --- a/tracing-tower-http/Cargo.toml +++ b/tracing-tower-http/Cargo.toml @@ -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" diff --git a/tracing-tower-http/examples/tower-h2-server.rs b/tracing-tower-http/examples/tower-h2-server.rs index a3de6f11..a477002c 100644 --- a/tracing-tower-http/examples/tower-h2-server.rs +++ b/tracing-tower-http/examples/tower-h2-server.rs @@ -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); diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml index add407f6..ca0f4cd3 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -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] diff --git a/tracing-tower/examples/h2-client.rs b/tracing-tower/examples/h2-client.rs index a693026f..60816947 100644 --- a/tracing-tower/examples/h2-client.rs +++ b/tracing-tower/examples/h2-client.rs @@ -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); diff --git a/tracing-tower/examples/h2-server.rs b/tracing-tower/examples/h2-server.rs index acdde873..bdbba991 100644 --- a/tracing-tower/examples/h2-server.rs +++ b/tracing-tower/examples/h2-server.rs @@ -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); diff --git a/tracing-tower/examples/load.rs b/tracing-tower/examples/load.rs index 22d3ded7..2737d767 100644 --- a/tracing-tower/examples/load.rs +++ b/tracing-tower/examples/load.rs @@ -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();