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

subscriber: Consider making event formatters own field formatters #658

Open
hawkw opened this issue Apr 2, 2020 · 5 comments
Open

subscriber: Consider making event formatters own field formatters #658

hawkw opened this issue Apr 2, 2020 · 5 comments
Labels
crate/subscriber Related to the `tracing-subscriber` crate kind/feature New feature or request meta/breaking This is a breaking change, and should wait until the next breaking release.

Comments

@hawkw
Copy link
Member

hawkw commented Apr 2, 2020

Feature Request

Crates

  • tracing-subscriber

Motivation

Currently, the tracing-subscriber fmt module has separate traits for event and field formatters. This allows users to override just the way fields are formatted while keeping the overall event format, or vise versa. This is good.

However, in some cases, event and field formatters are more tightly coupled. For example, the JSON event formatter requires the use of the JSON field formatter, and won't compile without it. That's good, but the compiler error doesn't make it immediately obvious that the JSON field formatter is also needed --- users will call .event_format(Json::default()) and expect it to work. This can be confusing.

Ideally, we should consider changes so that the event and field formatters remain decoupled, but event formatters which require a specific field formatter can set both.

Proposal

I think we should replace the separate fields for event and field formatters on the fmt::Layer with a single type which is required to implement both. The default event formatter would own a field formatter, which it would expose for modification, and it would implement FormatFields by delegating to that field formatter. That way, when using an event formatter that does't care about how fields are formatted, it would be possible to override one or the other. However, when using an event formatter that does require a specific field formatter, like JSON, switching to the JSON event formatter would automatically also switch to the JSON field formatter.

Unfortunately, I don't think it's possible to do this without a breaking change, but we should consider it in tracing-subscriber 0.3.

Alternatives

We could also not do this.

@hawkw hawkw added crate/subscriber Related to the `tracing-subscriber` crate kind/feature New feature or request meta/breaking This is a breaking change, and should wait until the next breaking release. labels Apr 2, 2020
@hawkw hawkw added this to the tracing-subscriber 0.3 milestone Apr 2, 2020
@hawkw
Copy link
Member Author

hawkw commented Apr 2, 2020

@samschlegel I think this should fix the issue you hit yesterday.

@davidbarsky
Copy link
Member

Maybe somewhat related to this, but someone gave me this suggestion:

It seems like one convenience inside the tracing_subscriber::fmt stuff would be to allow for fmt strings like the standard logging fmt strings that make reference to span variables [such that] the following is supported:

{d} {spans[].name} [{spans.0.target}] [{spans.-1.request_id}] - {L} - {m}{n}

This might tie in with nicely with a rewrittten filtering syntax, such that some filters could be written construtively in terms of their grammar. I'm not sure everything can be presented in that way, but there might be a nice symmetry there.

@hawkw
Copy link
Member Author

hawkw commented May 28, 2020

Maybe somewhat related to this, but someone gave me this suggestion:

It seems like one convenience inside the tracing_subscriber::fmt stuff would be to allow for fmt strings like the standard logging fmt strings that make reference to span variables [such that] the following is supported:

{d} {spans[].name} [{spans.0.target}] [{spans.-1.request_id}] - {L} - {m}{n}

This might tie in with nicely with a rewrittten filtering syntax, such that some filters could be written construtively in terms of their grammar. I'm not sure everything can be presented in that way, but there might be a nice symmetry there.

That seems like a great idea, but is not really related to this issue at all — maybe open a new feature request to propose it?

@hawkw
Copy link
Member Author

hawkw commented Jul 2, 2020

Some more notes:

  • A few people (including @nagisa and other folks on discord) have asked about using the fmt::format traits in other Layers. Currently, the FormatEvent trait cannot be used outside of tracing_subscriber::fmt, since the FmtContext type doesn't have a public constructor. I'd really like to change this so that the existing {matters and the traits are usable in other Layers. The main reason that FmtContext exists as a separate type is to pass in the field formatter to the FormatEvent::format_event method. If we change these methods to take a regular layer::Context, they would be much easier to use elsewhere.
  • Currently, there's an impl of FormatEvent for fns:
    impl<S, N> FormatEvent<S, N>
    for fn(ctx: &FmtContext<'_, S, N>, &mut dyn fmt::Write, &Event<'_>) -> fmt::Result
    where
    S: Subscriber + for<'a> LookupSpan<'a>,
    N: for<'a> FormatFields<'a> + 'static,

    This makes it hard to add new methods to the trait (such as for getting/setting a field formatter), since a bare fn won't have those methods. However, since this is a breaking change, we can probably introduce a wrapper type format_fn or something, instead, and it could also be aware of a formatter (and pass it into the wrapped function!).
    • This would also allow us to take big-F Fns as well as little-f fns. Accepting only little-f fns as we do currently is a bit of a pain, because non-capturing closures often need to be cast (i believe @davidbarsky hit this a couple days ago), and capturing closures cannot be used at all...
  • If we do a big rewrite of this API, it might be worth trying to replace more of the generics with trait objects --- a lot of people have had issues with the number of generics in this API making it difficult to represent dynamic subscriber configurations. We should do before/after benchmarking to see if this hurts performance enough to matter.

cratelyn pushed a commit to cratelyn/tracing that referenced this issue Feb 16, 2021
 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
cratelyn pushed a commit to cratelyn/tracing that referenced this issue Feb 16, 2021
 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
cratelyn pushed a commit to cratelyn/tracing that referenced this issue Mar 9, 2021
 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
cratelyn pushed a commit to cratelyn/tracing that referenced this issue Mar 11, 2021
 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
cratelyn pushed a commit to cratelyn/tracing that referenced this issue Mar 11, 2021
 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
hawkw pushed a commit that referenced this issue Mar 11, 2021
* subscriber: update pretty formatter for no ansi

 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Implement `LevelNames` for `Pretty`, copying `Full`'s
      implementation.

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, #658 is worth investigating further.

Refs: #658
hawkw pushed a commit that referenced this issue Mar 12, 2021
This backports #1240 from `master`.

* subscriber: update pretty formatter for no ansi

 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, #658 is worth investigating further.

Refs: #658
hawkw pushed a commit that referenced this issue Mar 12, 2021
This backports #1240 from `master`.

* subscriber: update pretty formatter for no ansi

 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, #658 is worth investigating further.

Refs: #658
@hawkw
Copy link
Member Author

hawkw commented Oct 21, 2021

#1651 should also help with this

kaffarell pushed a commit to kaffarell/tracing that referenced this issue May 22, 2024
This backports tokio-rs#1240 from `master`.

* subscriber: update pretty formatter for no ansi

 ## Background

    Currently, when the `Pretty` event formatter is being used, it does
    not change its output when the `with_ansi` flag is set to false by
    the `CollectorBuilder`.

 ## Overview

    While this formatter is generally used in situations such as local
    development, where ANSI escape codes are more often acceptable,
    there are some situations in which this can lead to mangled output.

    This commit makes some minor changes to account for this `ansi` flag
    when formatting events using `Pretty`.

    Becuase ANSI codes were previously used to imply the event level
    using colors, this commit additionally modifies `Pretty` so that
    it respects `display_level` when formatting an event.

 ## Changes

    * Changes to `<Format<Pretty, T> as FormatEvent<C, N>>::format_event`

    * Add a `PrettyVisitor::ansi` boolean flag, used in its `Visit`
      implementation.

        * Add a new `PrettyVisitor::with_ansi` builder pattern method to
          facilitate this.

 ## Out of Scope

    One detail worth nothing is that this does not solve the problem of
    *fields* being formatted without ANSI codes. Configuring a
    subscriber using this snippet would still lead to bolded fields in
    parent spans.

```rust
tracing_subscriber::fmt()
    .pretty()
    .with_ansi(false)
    .with_level(false)
    .with_max_level(tracing::Level::TRACE)
    .init();
```

    This can be worked around by using a different field formatter, via
    `.fmt_fields(tracing_subscriber::fmt::format::DefaultFields::new())`
    in the short-term.

    In the long-term, tokio-rs#658 is worth investigating further.

Refs: tokio-rs#658
github-merge-queue bot pushed a commit to awslabs/mountpoint-s3 that referenced this issue Nov 8, 2024
<!--
The title and description of pull requests will be used when creating a
squash commit to the base branch (usually `main`).
Please keep them both up-to-date as the code change evolves, to ensure
that the commit message is useful for future readers.
-->

## Description of change

Before this change, log files written on macOS would include ANSI escape
codes (#1050). It's unclear why this is not reproducible on Linux.

This change reorders the logging layers such that the console layer
(with ANSI) is evaluated last, and so the mutations to add ANSI escapes
is not applied when writing log files. This issue appears related:
tokio-rs/tracing#658.

Relevant issues: #1050 

## Does this change impact existing behavior?

This fixes log files written on macOS (which is an unsupported
platform).

## Does this change need a changelog entry in any of the crates?

This is a minor bug fix on an unsupported platform, so no changelog
entry needed.

---

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and I agree to the terms of
the [Developer Certificate of Origin
(DCO)](https://developercertificate.org/).

Signed-off-by: Daniel Carl Jones <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crate/subscriber Related to the `tracing-subscriber` crate kind/feature New feature or request meta/breaking This is a breaking change, and should wait until the next breaking release.
Projects
None yet
Development

No branches or pull requests

2 participants