Skip to content
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

Impl filter for EnvFilter #1983

Merged
merged 20 commits into from
Mar 25, 2022

Conversation

tfreiberg-fastly
Copy link
Contributor

Fixes #1868
Follow-up on #1973

Motivation

Filtering by span and field requires using EnvFilter. Per-layer filtering requires the Filter trait.

Solution

Implement the Filter trait for EnvFilter

@tfreiberg-fastly tfreiberg-fastly requested review from hawkw and a team as code owners March 10, 2022 16:23
@tfreiberg-fastly tfreiberg-fastly marked this pull request as draft March 10, 2022 16:24
@tfreiberg-fastly
Copy link
Contributor Author

tfreiberg-fastly commented Mar 10, 2022

I'm not sure about this implementation (especially if callsite_enabled should be implemented the same way as register_callsite), but I thought maybe this PR could save some work.
I tested it locally, and it doesn't seem to work properly - I guess the Interest needs to be different for Filter than for Layer...?

@tfreiberg-fastly tfreiberg-fastly marked this pull request as ready for review March 10, 2022 16:35
@tfreiberg-fastly tfreiberg-fastly marked this pull request as draft March 10, 2022 16:35
@davidbarsky davidbarsky self-requested a review March 10, 2022 16:46
@hawkw
Copy link
Member

hawkw commented Mar 10, 2022

I tested it locally, and it doesn't seem to work properly - I guess the Interest needs to be different for Filter than for Layer...?

Can you share the way you've been testing this?

Copy link
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

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

At a glance, this seems correct. I'd like to figure out why it's not working for you in your tests.

Ideally, can we add integration tests for EnvFilter's Filter implementation? We can probably do that by copying these tests: https://github.com/tokio-rs/tracing/blob/v0.1.x/tracing-subscriber/tests/filter.rs and modifying the subscriber to use a mock Layer wrapped in Filtered, rather than using a mock Subscriber. The layer filter tests show how the mock layers are used:

let (trace_layer, trace_handle) = layer::named("trace")
.event(event::mock().at_level(Level::TRACE))
.event(event::mock().at_level(Level::DEBUG))
.event(event::mock().at_level(Level::INFO))
.done()
.run_with_handle();
let (debug_layer, debug_handle) = layer::named("debug")
.event(event::mock().at_level(Level::DEBUG))
.event(event::mock().at_level(Level::INFO))
.done()
.run_with_handle();
let (info_layer, info_handle) = layer::named("info")
.event(event::mock().at_level(Level::INFO))
.done()
.run_with_handle();
let _subscriber = tracing_subscriber::registry()
.with(trace_layer.with_filter(LevelFilter::TRACE))
.with(debug_layer.with_filter(LevelFilter::DEBUG))
.with(info_layer.with_filter(LevelFilter::INFO))
.set_default();

Comment on lines 540 to 555
self.on_new_span(attrs, id)
}

#[inline]
fn on_enter(&self, id: &span::Id, _: Context<'_, S>) {
self.on_enter(id);
}

#[inline]
fn on_exit(&self, id: &span::Id, _: Context<'_, S>) {
self.on_exit(id);
}

#[inline]
fn on_close(&self, id: span::Id, _: Context<'_, S>) {
self.on_close(id);
Copy link
Member

Choose a reason for hiding this comment

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

It took me a minute to realize that these are not actually recursive because the context isn't passed through. Looks correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, I think I'd also be on board with writing all the delegating calls like EnvFilter::method(self, ...) for consistency

tracing-subscriber/src/filter/env/mod.rs Outdated Show resolved Hide resolved
Co-authored-by: Eliza Weisman <eliza@buoyant.io>
@tfreiberg-fastly
Copy link
Contributor Author

The test is here: https://github.com/tfreiberg-fastly/tracing-envfilter-bug/blob/main/src/main.rs

cargo run with per-layer filtering:

2022-03-10T22:01:34.603414Z TRACE span1{field1="val1"}: target1: should be logged
2022-03-10T22:01:34.603456Z TRACE target2: should be ignored (wrong target, no field)
2022-03-10T22:01:34.603479Z TRACE span2{field2="val1"}: target2: should be ignored (correct field, but wrong target)

cargo run with global filter:

2022-03-10T22:01:29.120669Z TRACE span1{field1="val1"}: target1: should be logged

@hawkw
Copy link
Member

hawkw commented Mar 10, 2022

@tfreiberg-fastly
Copy link
Contributor Author

Oops, created as private by accident - public now

@tfreiberg-fastly
Copy link
Contributor Author

I ported the tests to use EnvFilter as a Filter, and had to change the mock configuration for span_name_filter_is_dynamic a bit. The changes are in the last commit.

I had to add .in_scope(..) to the MockEvents which wasn't in the original test. I'm honestly more confused that those aren't necessary in the original test tbh.

Also, I'm confused that the first two events in "uncool_span" aren't in both "uncool_span" and "cool_span", is that correct?
This means with line 349 (configuring the first event triggered after "uncool_span" is entered while "cool_span" hasn't been exited yet) like this, the test succeeds:

.in_scope(vec![span::mock().named("uncool_span")])`

but like that it fails:

.in_scope(vec![span::mock().named("uncool_span"), span::mock().named("cool_span")])

)

@hawkw
Copy link
Member

hawkw commented Mar 17, 2022

I had to add .in_scope(..) to the MockEvents which wasn't in the original test. I'm honestly more confused that those aren't necessary in the original test tbh.

I think that's just be a difference in behavior between the mock Subscriber and the mock Layer. It looks like the mock Layer always asserts that an event is in the expected scope:

let expected_scope = expected.scope_mut();
let mut i = 0;
for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
println!(
"[{}] event_scope[{}] actual={} ({:?}); expected={}",
self.name,
i,
actual.name(),
actual.id(),
expected
);
self.check_span_ref(
expected,
&actual,
format_args!("the {}th span in the event's scope to be", i),
);
i += 1;
}
let remaining_expected = &expected_scope[i..];
assert!(
remaining_expected.is_empty(),
"\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
self.name,
self.name,
remaining_expected,
);
assert!(
current_scope.next().is_none(),
"\n[{}] did not expect all spans in the actual event scope!",
self.name,
);
}

on the other hand, the test Subscriber implementation doesn't make assertions about the event's expected scope, only the immediate parent span:
pub fn check(
&mut self,
event: &tracing::Event<'_>,
get_parent_name: impl FnOnce() -> Option<String>,
subscriber_name: &str,
) {
let meta = event.metadata();
let name = meta.name();
self.metadata
.check(meta, format_args!("event \"{}\"", name), subscriber_name);
assert!(
meta.is_event(),
"[{}] expected {}, but got {:?}",
subscriber_name,
self,
event
);
if let Some(ref mut expected_fields) = self.fields {
let mut checker = expected_fields.checker(name, subscriber_name);
event.record(&mut checker);
checker.finish();
}
if let Some(ref expected_parent) = self.parent {
let actual_parent = get_parent_name();
expected_parent.check_parent_name(
actual_parent.as_deref(),
event.parent().cloned(),
event.metadata().name(),
subscriber_name,
)
}
}

We may want to change this behavior, but I think that, ideally, we would have a way to differentiate between "asserting that there are NO spans in scope" and "not caring about what spans are in scope" --- I believe some of the other per-layer filtering tests actually depend on an empty in_scope asserting that there are no spans in scope.

@tfreiberg-fastly
Copy link
Contributor Author

Alright, that makes sense. I totally agree with your ideas about the different kinds of assertions.

I've added another test that currently fails - it seems to fail differently than this one, I am a bit confused about the situation tbh.

Alright I read through the "cares about metadata" code and phew, I didn't know that spans aren't recorded if the span target doesn't match the directive. I think I saw a discussion about that in discord once... eh, I guess I'll sleep on it and look at the test with fresh eyes tomorrow :)

@hawkw
Copy link
Member

hawkw commented Mar 22, 2022

Thanks for adding the failing tests, I'll take a closer look at the code and see if I can figure out what's up!

@hawkw
Copy link
Member

hawkw commented Mar 22, 2022

I've added another test that currently fails - it seems to fail differently than this one, I am a bit confused about the situation tbh.

...oh, the test is wrong. The directive stuff[cool_span]=debug means "enable the DEBUG level if inside a span with the target stuff and the name cool_span", but the test seems to expect the behavior to be "enable the DEBUG level for events with the target stuff if inside a span named cool_span, where the span can have any target". I'm pretty sure we'd see the exact same behavior if we were using EnvFilter as a global filter.

@tfreiberg-fastly
Copy link
Contributor Author

yeah, that seriously confused me 😬 thanks for the explanation!
after fixing that, I found a real unexpected failure in the test (which passes when using a global filter), and it was specific enough for me to find the issue - Filtered's on_exit called the inner filters on_enter... Yeah so that is fixed now

hawkw added a commit that referenced this pull request Mar 29, 2022
## Motivation

Filtering by span and field requires using `EnvFilter` rather than
`Targets`. Per-subscriber filtering requires the `Filter` trait, which
`EnvFilter` does not implement.

## Solution

Implement the `Filter` trait for `EnvFilter`. PR #1973 adds additiional
methods to the `Filter` trait, which are necessary for `EnvFilter` to
implement dynamic span filtering. Now that those methods are added, we
can provide a `Filter` impl for `EnvFilter`.

In addition, we changed the globally-scoped `thread_local!` macro to use
a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as
per-subscriber filters don't share a single span scope.

Fixes #1868
Follow-up on #1973

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Mar 29, 2022
## Motivation

Filtering by span and field requires using `EnvFilter` rather than
`Targets`. Per-subscriber filtering requires the `Filter` trait, which
`EnvFilter` does not implement.

## Solution

Implement the `Filter` trait for `EnvFilter`. PR #1973 adds additiional
methods to the `Filter` trait, which are necessary for `EnvFilter` to
implement dynamic span filtering. Now that those methods are added, we
can provide a `Filter` impl for `EnvFilter`.

In addition, we changed the globally-scoped `thread_local!` macro to use
a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as
per-subscriber filters don't share a single span scope.

Fixes #1868
Follow-up on #1973

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Mar 29, 2022
## Motivation

Filtering by span and field requires using `EnvFilter` rather than
`Targets`. Per-subscriber filtering requires the `Filter` trait, which
`EnvFilter` does not implement.

## Solution

Implement the `Filter` trait for `EnvFilter`. PR #1973 adds additiional
methods to the `Filter` trait, which are necessary for `EnvFilter` to
implement dynamic span filtering. Now that those methods are added, we
can provide a `Filter` impl for `EnvFilter`.

In addition, we changed the globally-scoped `thread_local!` macro to use
a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as
per-subscriber filters don't share a single span scope.

Fixes #1868
Follow-up on #1973

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Mar 29, 2022
)

## Motivation

Currently, the `Filtered` type's `Subscribe` impl accidentally calls the
inner `Filter`'s `on_enter` hook in the `on_exit` hook. This
is...obviously wrong, lol, and means that filters like `EnvFilter` will
never consider spans to have exited, breaking filtering behavior.

@tfreiberg-fastly originally noticed this bug (see
#1983 (comment)),
but I wanted to make the change separately from PR #1983 so that we can
fix it on the main branch without waiting for #1983 to merge first.

## Solution

This branch, uh, you know... fixes that.
hawkw added a commit that referenced this pull request Mar 29, 2022
## Motivation

Filtering by span and field requires using `EnvFilter` rather than
`Targets`. Per-subscriber filtering requires the `Filter` trait, which
`EnvFilter` does not implement.

## Solution

Implement the `Filter` trait for `EnvFilter`. PR #1973 adds additiional
methods to the `Filter` trait, which are necessary for `EnvFilter` to
implement dynamic span filtering. Now that those methods are added, we
can provide a `Filter` impl for `EnvFilter`.

In addition, we changed the globally-scoped `thread_local!` macro to use
a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as
per-subscriber filters don't share a single span scope.

Fixes #1868
Follow-up on #1973

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Apr 1, 2022
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([#1983])
  - **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([#1973],
  [#2017], [#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([#2035])
- Several documentation fixes and improvements ([#1972], [#1971],
  [#2023], [#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[#1983]: #1983
[#1973]: #1973
[#2017]: #2017
[#2031]: #2031
[#1959]: #1959
[#2034]: #2034
[#2027]: #2027
[#2026]: #2026
[#2035]: #2035
[#1972]: #1972
[#1971]: #1971
[#2023]: #2023
hawkw added a commit that referenced this pull request Apr 1, 2022
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([#1983])
  - **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([#1973],
  [#2017], [#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([#2035])
- Several documentation fixes and improvements ([#1972], [#1971],
  [#2023], [#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[#1983]: #1983
[#1973]: #1973
[#2017]: #2017
[#2031]: #2031
[#1959]: #1959
[#2034]: #2034
[#2027]: #2027
[#2026]: #2026
[#2035]: #2035
[#1972]: #1972
[#1971]: #1971
[#2023]: #2023
hawkw added a commit that referenced this pull request Apr 1, 2022
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([#1983])
  - **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([#1973],
  [#2017], [#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([#2035])
- Several documentation fixes and improvements ([#1972], [#1971],
  [#2023], [#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[#1983]: #1983
[#1973]: #1973
[#2017]: #2017
[#2031]: #2031
[#1959]: #1959
[#2034]: #2034
[#2027]: #2027
[#2026]: #2026
[#2035]: #2035
[#1972]: #1972
[#1971]: #1971
[#2023]: #2023
hawkw added a commit that referenced this pull request Apr 1, 2022
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([#1983])
  - **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([#1973],
  [#2017], [#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([#2035])
- Several documentation fixes and improvements ([#1972], [#1971],
  [#2023], [#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[#1983]: #1983
[#1973]: #1973
[#2017]: #2017
[#2031]: #2031
[#1959]: #1959
[#2034]: #2034
[#2027]: #2027
[#2026]: #2026
[#2035]: #2035
[#1972]: #1972
[#1971]: #1971
[#2023]: #2023
hawkw added a commit that referenced this pull request Apr 1, 2022
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([#1983])
- **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([#1973],
  [#2017], [#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([#2035])
- Several documentation fixes and improvements ([#1972], [#1971],
  [#2023], [#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([#2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[#1983]: #1983
[#1973]: #1973
[#2017]: #2017
[#2031]: #2031
[#1959]: #1959
[#2034]: #2034
[#2027]: #2027
[#2026]: #2026
[#2035]: #2035
[#1972]: #1972
[#1971]: #1971
[#2023]: #2023
[#2025]: #2025
[#2029]: #2029
@Veetaha
Copy link
Contributor

Veetaha commented Apr 5, 2022

@tfreiberg-fastly, @hawkw

Just FYI, I don't think it really requires any action, just taking this into account would be fine.
This update has broken our code.

TLDR: it turns out adding a new trait impl in tracing-subscriber for the preexisting type that also implements other trait that contains the method of the same name is a breaking change because it introduces symbol ambiguity.

Our code lives in a private repo, so I can provide you with only a snippet:

This code compiled fine for us with tracing-subscriber 0.3.9, but it doesn't compile with tracing-subscriber 0.3.10

    use tracing::*;
    use tracing_subscriber::Layer;
    
    // A quirk of tracing-subscriber is that `EnvFilter` is itself a `Layer` impl which filters
    // events that match the env filter.  Unfortunately these act as global filters, not filters
    // specific to each layer, so we have to use this `with_filter` combinator instead
    struct EnvFilterAsLayerFilter(tracing_subscriber::EnvFilter);
    impl<S: tracing::Subscriber> tracing_subscriber::layer::Filter<S> for EnvFilterAsLayerFilter {
        fn enabled(
            &self,
            meta: &Metadata<'_>,
            cx: &tracing_subscriber::layer::Context<'_, S>,
        ) -> bool {
            self.0.enabled(meta, cx.clone())
        }
    }

    let console_layer = tracing_subscriber::fmt::Layer::new()
        .json()
        .with_writer(std::io::stderr)
        .with_filter(EnvFilterAsLayerFilter(console_filter));

The compile error is the following one:

error[E0034]: multiple applicable items in scope
    --> /home/vvv/.cargo/registry/src/{redacted}/cheburashka-4.3.0/src/logging/mod.rs:1511:20
     |
1511 |             self.0.enabled(meta, cx.clone())
     |                    ^^^^^^^ multiple `enabled` found
     |
     = note: candidate #1 is defined in an impl of the trait `__tracing_subscriber_Layer` for the type `tracing_subscriber::EnvFilter`
     = note: candidate #2 is defined in an impl of the trait `tracing_subscriber::layer::Filter` for the type `tracing_subscriber::EnvFilter`
note: candidate #3 is defined in the trait `tracing::Subscriber`
    --> /home/vvv/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-core-0.1.24/src/subscriber.rs:172:5
     |
172  |     fn enabled(&self, metadata: &Metadata<'_>) -> bool;
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
     |
1511 |             __tracing_subscriber_Layer::enabled(&self.0, meta, cx.clone())
     |
help: disambiguate the associated function for candidate #2
     |
1511 |             tracing_subscriber::layer::Filter::enabled(&self.0, meta, cx.clone())
     |
help: disambiguate the associated function for candidate #3
     |
1511 |             tracing::Subscriber::enabled(self.0, meta, cx.clone())
     |

@tfreiberg-fastly
Copy link
Contributor Author

Oh, sorry for that :/

In case you haven't already found that out: to keep using the same functionality with minimal changes, you can use both Layer::enabled as well as Filter::enabled - they use the exact same implementation.
(I'd use Filter::enabled because the suggested path looks nicer 🤷 )

Also, you don't have to use this wrapper anymore, .with_filter(console_filter) should work now.

Still, sorry to cause additional work.

@Veetaha
Copy link
Contributor

Veetaha commented Apr 5, 2022

Sure, I understand, that wasn't difficult to fix on our side, and the patch made the code cleaner anyway, thanks for the contribution though! =)

@hawkw
Copy link
Member

hawkw commented Apr 6, 2022

@Veetaha hmm, the breaking change is certainly unfortunate, I hadn't considered that. Normally, we would consider yanking 0.3.10, publishing a new release with the accidentally breaking change removed, and saving this change for an 0.4. However, in this case, removing the Filter impl is also a breaking change, and removing it may break more code than the name collision does. So, I'm not totally sure what's best here. Sorry for the inconvenience, though!

@CAD97
Copy link
Contributor

CAD97 commented Apr 6, 2022

This falls under known allowed method resolution breakage.

(When the methods have the same content, it would be nice if they could actually be the same item, and not have a conflict...)

There is a way to manually prevent an ambiguity error, though: add an inherent impl.

hawkw added a commit that referenced this pull request Apr 8, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Subscribe` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Subscribe` are in scope, the method resolution is
ambiguous.

See #1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.
hawkw added a commit that referenced this pull request Apr 8, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Subscribe` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Subscribe` are in scope, the method resolution is
ambiguous.

See #1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.
hawkw added a commit that referenced this pull request Apr 8, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Subscribe` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Subscribe` are in scope, the method resolution is
ambiguous.

See #1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Apr 8, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Layer` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Layer` are in scope, the method resolution is
ambiguous.

See #1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Apr 9, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Layer` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Layer` are in scope, the method resolution is
ambiguous.

See #1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
liuhaozhan added a commit to liuhaozhan/tracing that referenced this pull request Nov 17, 2022
…018)

## Motivation

Currently, the `Filtered` type's `Subscribe` impl accidentally calls the
inner `Filter`'s `on_enter` hook in the `on_exit` hook. This
is...obviously wrong, lol, and means that filters like `EnvFilter` will
never consider spans to have exited, breaking filtering behavior.

@tfreiberg-fastly originally noticed this bug (see
tokio-rs/tracing#1983 (comment)),
but I wanted to make the change separately from PR #1983 so that we can
fix it on the main branch without waiting for #1983 to merge first.

## Solution

This branch, uh, you know... fixes that.
liuhaozhan added a commit to liuhaozhan/tracing that referenced this pull request Nov 17, 2022
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Subscribe` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Subscribe` are in scope, the method resolution is
ambiguous.

See tokio-rs/tracing#1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
…kio-rs#2018)

## Motivation

Currently, the `Filtered` type's `Layer` impl accidentally calls the
inner `Filter`'s `on_enter` hook in the `on_exit` hook. This
is...obviously wrong, lol, and means that filters like `EnvFilter` will
never consider spans to have exited, breaking filtering behavior.

@tfreiberg-fastly originally noticed this bug (see
tokio-rs#1983 (comment)),
but I wanted to make the change separately from PR tokio-rs#1983 so that we can
fix it on the main branch without waiting for tokio-rs#1983 to merge first.

## Solution

This branch, uh, you know... fixes that.
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
Filtering by span and field requires using `EnvFilter` rather than
`Targets`. Per-layer filtering requires the `Filter` trait, which
`EnvFilter` does not implement.

Implement the `Filter` trait for `EnvFilter`. PR tokio-rs#1973 adds additiional
methods to the `Filter` trait, which are necessary for `EnvFilter` to
implement dynamic span filtering. Now that those methods are added, we
can provide a `Filter` impl for `EnvFilter`.

In addition, we changed the globally-scoped `thread_local!` macro to use
a `ThreadLocal` struct as a field, so that multiple `EnvFilter`s used as
per-layer filters don't share a single span scope.

Fixes tokio-rs#1868
Follow-up on tokio-rs#1973

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
# 0.3.10 (Apr 1, 2022)

This release adds several new features, including a `Filter`
implementation and new builder API for `EnvFilter`, support for using a
`Vec<L> where L: Layer` as a `Layer`, and a number of smaller API
improvements to make working with dynamic and reloadable layers easier.

### Added

- **registry**: Implement `Filter` for `EnvFilter`, allowing it to be
  used with per-layer filtering ([tokio-rs#1983])
- **registry**: `Filter::on_new_span`, `Filter::on_enter`,
  `Filter::on_exit`, `Filter::on_close` and `Filter::on_record`
  callbacks to allow `Filter`s to track span states internally ([tokio-rs#1973],
  [tokio-rs#2017], [tokio-rs#2031])
- **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors
  ([tokio-rs#1959])
- **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to
  borrow the wrapped `Layer` ([tokio-rs#2034])
- **layer**: Implement `Layer` for `Vec<L: Layer>`, to allow composing
  together a dynamically sized list of `Layer`s ([tokio-rs#2027])
- **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier
  ([tokio-rs#2026])
- **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors
  ([tokio-rs#2034])
- **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI
  formatting configuration at runtime ([tokio-rs#2034])
- **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter`
  prior to parsing it ([tokio-rs#2035])
- Several documentation fixes and improvements ([tokio-rs#1972], [tokio-rs#1971],
  [tokio-rs#2023], [tokio-rs#2023])

### Fixed

- **fmt**: `fmt::Layer`'s auto traits no longer depend on the
  `Subscriber` type parameter's auto traits ([tokio-rs#2025])
- **env-filter**: Fixed missing help text when the `ansi` feature is
  disabled ([tokio-rs#2029])

Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97
for contributing to this release!

[tokio-rs#1983]: tokio-rs#1983
[tokio-rs#1973]: tokio-rs#1973
[tokio-rs#2017]: tokio-rs#2017
[tokio-rs#2031]: tokio-rs#2031
[tokio-rs#1959]: tokio-rs#1959
[tokio-rs#2034]: tokio-rs#2034
[tokio-rs#2027]: tokio-rs#2027
[tokio-rs#2026]: tokio-rs#2026
[tokio-rs#2035]: tokio-rs#2035
[tokio-rs#1972]: tokio-rs#1972
[tokio-rs#1971]: tokio-rs#1971
[tokio-rs#2023]: tokio-rs#2023
[tokio-rs#2025]: tokio-rs#2025
[tokio-rs#2029]: tokio-rs#2029
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
## Motivation

Currently, there is a potential namespace resolution issue when calling
`EnvFilter` methods that have the same name in the `Filter` and
`Layer` traits (such as `enabled` and `max_level_hint`). When both
`Filter` and `Layer` are in scope, the method resolution is
ambiguous.

See tokio-rs#1983 (comment)

## Solution

This commit solves the problem by making the inherent method versions of
these methods public. When the traits are in scope, name resolution will
always select the inherent method prefer`entially, preventing the name
clash.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
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.

None yet

4 participants