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:
Eliza Weisman 2019-09-02 08:53:58 -07:00 committed by GitHub
parent 0852c82641
commit 2520f97964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 786 additions and 1663 deletions

View File

@ -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" }

View File

@ -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();

View File

@ -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)?;

View File

@ -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)?;

View File

@ -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(())

View File

@ -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" }

View File

@ -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, || {

View File

@ -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;

View File

@ -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"

View File

@ -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)

View File

@ -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" }

View File

@ -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/

View File

@ -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();
}
}

View File

@ -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
}
}

View File

@ -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");
}
}

View File

@ -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;
}

View File

@ -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]

View File

@ -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()

View File

@ -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));
}

View File

@ -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]

View 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,
);
});
}

View File

@ -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,
);
});
}

View 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,
);
});
}

View 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 }
}
}

View 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`.");
});
}

View File

@ -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())

View File

@ -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;

View 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())
}
}

View File

@ -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;

View File

@ -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()));
}

View File

@ -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::*;

View File

@ -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`")]

View File

@ -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"

View File

@ -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);

View File

@ -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]

View File

@ -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);

View File

@ -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);

View File

@ -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();