Skip to content

Add env-filter-explorer example #3233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

joshka
Copy link
Contributor

@joshka joshka commented Mar 12, 2025

Motivation

On discord, Conrad suggested a website to experiment with env-filter syntax. I think a TUI in tree makes a pretty decent replacement, especially as the output can exactly match the output that a real user might see if they're configuring tracing_subscriber with the default values.

Solution

This example demonstrates how to use the tracing-subscriber crate's
EnvFilter type to filter log messages based on their metadata. The
example provides a text area where users can input an environment filter
string, and displays the log messages that would be captured by that
filter.

There are preset filters that can be selected that show off the syntax.

image

joshka added 2 commits March 12, 2025 12:16
The transitive dependency on itoa 0.4 does not compiles with stable
rust. Bump inferno to 0.12.1 to fix this.

```
❯ cargo check --examples
    Checking serde v1.0.157
    Checking tokio v1.43.0
    Checking itoa v0.4.0
    Checking tracing-macros v0.1.0 (/Users/joshka/local/tracing/tracing-macros)
error[E0554]: `#![feature]` may not be used on the stable release channel
  --> /Users/joshka/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/itoa-0.4.0/src/lib.rs:13:31
   |
13 | #![cfg_attr(feature = "i128", feature(i128_type, i128))]
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: the feature `i128_type` has been stable since `1.26.0` and no longer requires an attribute to enable

```

```
❯ cargo tree -i itoa@0.4.0
itoa v0.4.0
└── num-format v0.4.0
    └── inferno v0.11.6
        [dev-dependencies]
        └── tracing-examples v0.0.0 (/Users/joshka/local/tracing/examples)
```
This example demonstrates how to use the `tracing-subscriber` crate's
`EnvFilter` type to filter log messages based on their metadata. The
example provides a text area where users can input an environment filter
string, and displays the log messages that would be captured by that
filter.
@joshka joshka requested review from hawkw, davidbarsky and a team as code owners March 12, 2025 20:59
Copy link
Contributor

@kaffarell kaffarell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is very nice! Did not yet look at the code in-depth but noticed one issue: When the envfilter is empty, no event is displayed (this is not correct, every event should be shown). Same on bogus input, e.g., when writing "abc" every event should be visible.

@joshka
Copy link
Contributor Author

joshka commented Apr 10, 2025

This is the result of calling EnvFilterBuilder::parse(). There's nothing more to it than that.

@hds
Copy link
Contributor

hds commented Apr 11, 2025

EDIT: I think I'm wrong about the implementation of this (but right about the docs), that should probably be fixed.

@kaffarell It depends on the default directive, if none is given then ERROR level is set (see https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#method.new).

(This is different from if the env-filter feature isn't enabled at all and fmt::init() is called, in which case all traces are shown)

Copy link
Contributor

@hds hds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome! If we could avoid reformatting unrelated lines in Cargo.toml that would be good though.

tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["futures-01"] }
tracing-attributes = { path = "../tracing-attributes", version = "0.2"}
tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] }
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good if we could avoid reformatting. I think this isn't "standard" TOML formatting, it comes from a specific extension.

@hds
Copy link
Contributor

hds commented Apr 11, 2025

OK, I wrote a test for the case as per the documentation, and it fails:

#[test]
fn empty_string() {
    let filter: EnvFilter = ""
        .parse()
        .expect("filter should parse");
    let (collector, finished) = collector::mock()
        .event(expect::event().at_level(Level::ERROR))
        .only()
        .run_with_handle();
    let subscriber = collector.with(filter);

    with_default(subscriber, || {
        tracing::trace!("this should be disabled");
        tracing::debug!("this should be disabled");
        tracing::info!("this should be disabled");
        tracing::warn!("this should be disabled");
        tracing::error!("this shouldn't be disabled");
    });

    finished.assert_finished();
}

But it fails because nothing is received by the collector, it's interpreting an empty string as OFF.

@joshka
Copy link
Contributor Author

joshka commented Apr 11, 2025

Intuitively (to me at least), empty should effectively be a noop, which for a filter probably means just default. That said, I have a vague recollection there's a bit of weirdness where if the envfilter feature is not enabled you get info level logs by default, but it changes to error level if enabled. That also seems a bit counterintuitive. (I may be misremembering and I'm not near my computer for a few days to check)

@kaffarell
Copy link
Contributor

kaffarell commented Apr 11, 2025

This is the result of calling EnvFilterBuilder::parse(). There's nothing more to it than that.

Oh you're right, my bad, I tested with EnvFilter::new().

This behavior is kind weird though. We set a default_directive (with min level ERROR) on the new() method, but nowhere else... I think we should remove the default_directive from the new() method – that will make everything more predictable and easy to use. That will be a breaking change though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants