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: add MakeWriter::make_writer_for #1141

Merged
merged 8 commits into from
May 14, 2021

Conversation

hawkw
Copy link
Member

@hawkw hawkw commented Dec 15, 2020

subscriber: add MakeWriter::make_writer_for

Motivation

In some cases, it might be desirable to configure the writer used for
writing out trace data based on the metadata of the span or event being
written. For example, we might want to send different levels to
different outputs, write logs from different targets to separate files,
or wrap formatted output in ANSI color codes based on levels. Currently,
it's not possible for the MakeWriter trait to model this kind of
behavior --- it has one method, make_writer, which is completely
unaware of where the data being written came from.

In particular, this came up in PR #1137, when discussing a proposal that
writing to syslog could be implemented as a MakeWriter implementation
rather than as a Subscribe implementation, so that all the formatting
logic from tracing_subscriber::fmt could be reused. See here for
details.

Solution

This branch adds a new make_writer_for method to MakeWriter, taking
a Metadata. Implementations can opt in to metadata-specific behavior
by implementing this method. The method has a default implementation
that just calls self.make_writer() and ignores the metadata, so it's
only necessary to implement this when per-metadata behavior is required.
This isn't a breaking change to existing implementations.

There are a couple downsides to this approach: it's possible for callers
to skip the metadata-specific behavior by calling make_writer rather
than make_writer_for, and the impls for closures can't easily provide
metadata-specific behavior.

Since the upcoming release is going to be a breaking change anyway, we
may want to just make the breaking change of having
MakeWriter::make_writer always take a Metadata, which solves these
problems. However, that can't be backported to v0.1.x as easily. Additionally,
that would mean that functions like io::stdout no longer implement
MakeWriter; they would have to be wrapped in a wrapper type or closure
that ignores metadata.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
@hawkw hawkw requested a review from a team as a code owner December 15, 2020 18:54
@hawkw hawkw self-assigned this Dec 15, 2020
@hawkw hawkw mentioned this pull request Dec 15, 2020
@max-heller
Copy link

Additionally, that would mean that functions like io::stdout no longer implement MakeWriter; they would have to be wrapped in a wrapper type or closure that ignores metadata.

This would just mean writing something like |_| io::stdout(), right?

/// might write some values to the writer before or after providing it to
/// the caller.
///
/// For example, we ight want to write data from spans and events at the

Choose a reason for hiding this comment

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

Typo (ight instead of might)

/// Stderr(StderrLock<'a>),
/// }
///
/// impl io::Write for StdioLock {

Choose a reason for hiding this comment

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

StdioLock needs a lifetime parameter

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
@hawkw
Copy link
Member Author

hawkw commented Mar 1, 2021

@davidbarsky et al. mind giving this a review? i have some ideas for subsequent things we can add based on this...

Copy link
Member

@davidbarsky davidbarsky left a comment

Choose a reason for hiding this comment

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

I genuinely thought i reviewed this thing. Sorry! Looks good to me.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
@raftario
Copy link

raftario commented May 13, 2021

Currently looking into hooking tracing into Android's logging system, which has dedicated interfaces for levels and source locations, and this would be amazing.

Somewhat related and useful in similar usecases would be a way to reliably tell that two written events are in fact two distinct events. Currently it's only possible by depending on internal implementation details and either

  1. assuming MakeWriter::make_writer is called exactly once per event and flushing the event in the returned writer's drop impl, or
  2. assuming the buffer passed to std::io::Write::write_all contains exactly one event

where 1 definitely seems like the more reasonable thing to depend on and potentially guarantee as part of the stable API.

Looks like I'm absolutely awful at reading docs and after double checking this is already a thing. My bad, sorry for the noise.

@davidbarsky davidbarsky merged commit b3af7e1 into master May 14, 2021
@davidbarsky davidbarsky deleted the eliza/make-for-metadata branch May 14, 2021 22:55
hawkw added a commit that referenced this pull request Jun 25, 2021
Depends on #1141.

This branch adds a `MakeWriterExt` trait which adds a number of
combinators for composing multiple types implementing `MakeWriter`.
`MakeWriter`s can now be teed together, filtered with minimum and
maximum levels, filtered with a `Metadata` predicate, and combined with
a fallback for when a writer is _not_ made for a particular `Metadata`.

I've also added a `MakeWriter` impl for `std::fs::File`, because `&File`
implements `Write`. Ideally, we'd have a default impl of `MakeWriter`
for `T where &T: Write`, but that's not possible due to the impl of
`MakeWriter` for `F: Fn() -> T: Write`. We could add a generic `by_ref`
adapter for any `T where &T: Write`, though...

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Jun 25, 2021
This backports PR #1141 from `master`.

subscriber: add `MakeWriter::make_writer_for`

## Motivation

In some cases, it might be desirable to configure the writer used for
writing out trace data based on the metadata of the span or event being
written. For example, we might want to send different levels to
different outputs, write logs from different targets to separate files,
or wrap formatted output in ANSI color codes based on levels. Currently,
it's not possible for the `MakeWriter` trait to model this kind of
behavior --- it has one method, `make_writer`, which is completely
unaware of *where* the data being written came from.

In particular, this came up in PR #1137, when discussing a proposal that
writing to syslog could be implemented as a `MakeWriter` implementation
rather than as a `Subscribe` implementation, so that all the formatting
logic from `tracing_subscriber::fmt` could be reused. See [here][1] for
details.

## Solution

This branch adds a new `make_writer_for` method to `MakeWriter`, taking
a `Metadata`. Implementations can opt in to metadata-specific behavior
by implementing this method. The method has a default implementation
that just calls `self.make_writer()` and ignores the metadata, so it's
only necessary to implement this when per-metadata behavior is required.
This isn't a breaking change to existing implementations.

There are a couple downsides to this approach: it's possible for callers
to skip the metadata-specific behavior by calling `make_writer` rather
than `make_writer_for`, and the impls for closures can't easily provide
metadata-specific behavior.

Since the upcoming release is going to be a breaking change anyway, we
may want to just make the breaking change of having
`MakeWriter::make_writer` _always_ take a `Metadata`, which solves these
problems. However, that can't be backported to v0.1.x as easily. Additionally,
that would mean that functions like `io::stdout` no longer implement
`MakeWriter`; they would have to be wrapped in a wrapper type or closure
that ignores metadata.

[1]: #1137 (comment)

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Jun 25, 2021
This backports #1274 from `master`.

Depends on #1141.

This branch adds a `MakeWriterExt` trait which adds a number of
combinators for composing multiple types implementing `MakeWriter`.
`MakeWriter`s can now be teed together, filtered with minimum and
maximum levels, filtered with a `Metadata` predicate, and combined with
a fallback for when a writer is _not_ made for a particular `Metadata`.

I've also added a `MakeWriter` impl for `Arc<W>` when `&W` implements
`Write`. This enables `File`s to be used as `MakeWriter`s similarly to
how we will be able to in 0.3, with the additional overhead of having to
do ref-counting because we can't return a reference from `MakeWriter`
until the next breaking change.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Jun 26, 2021
This backports PR #1141 from `master`.

subscriber: add `MakeWriter::make_writer_for`

## Motivation

In some cases, it might be desirable to configure the writer used for
writing out trace data based on the metadata of the span or event being
written. For example, we might want to send different levels to
different outputs, write logs from different targets to separate files,
or wrap formatted output in ANSI color codes based on levels. Currently,
it's not possible for the `MakeWriter` trait to model this kind of
behavior --- it has one method, `make_writer`, which is completely
unaware of *where* the data being written came from.

In particular, this came up in PR #1137, when discussing a proposal that
writing to syslog could be implemented as a `MakeWriter` implementation
rather than as a `Subscribe` implementation, so that all the formatting
logic from `tracing_subscriber::fmt` could be reused. See [here][1] for
details.

## Solution

This branch adds a new `make_writer_for` method to `MakeWriter`, taking
a `Metadata`. Implementations can opt in to metadata-specific behavior
by implementing this method. The method has a default implementation
that just calls `self.make_writer()` and ignores the metadata, so it's
only necessary to implement this when per-metadata behavior is required.
This isn't a breaking change to existing implementations.

There are a couple downsides to this approach: it's possible for callers
to skip the metadata-specific behavior by calling `make_writer` rather
than `make_writer_for`, and the impls for closures can't easily provide
metadata-specific behavior.

Since the upcoming release is going to be a breaking change anyway, we
may want to just make the breaking change of having
`MakeWriter::make_writer` _always_ take a `Metadata`, which solves these
problems. However, that can't be backported to v0.1.x as easily. Additionally,
that would mean that functions like `io::stdout` no longer implement
`MakeWriter`; they would have to be wrapped in a wrapper type or closure
that ignores metadata.

[1]: #1137 (comment)

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Jun 26, 2021
This backports #1274 from `master`.

Depends on #1141.

This branch adds a `MakeWriterExt` trait which adds a number of
combinators for composing multiple types implementing `MakeWriter`.
`MakeWriter`s can now be teed together, filtered with minimum and
maximum levels, filtered with a `Metadata` predicate, and combined with
a fallback for when a writer is _not_ made for a particular `Metadata`.

I've also added a `MakeWriter` impl for `Arc<W>` when `&W` implements
`Write`. This enables `File`s to be used as `MakeWriter`s similarly to
how we will be able to in 0.3, with the additional overhead of having to
do ref-counting because we can't return a reference from `MakeWriter`
until the next breaking change.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw added a commit that referenced this pull request Jun 26, 2021
# 0.2.19 (June 25, 2021)

### Deprecated

- **registry**: `SpanRef::parents`, `SpanRef::from_root`, and
  `Context::scope` iterators, which are replaced by new `SpanRef::scope`
  and `Scope::from_root` iterators (#1413)

### Added

- **registry**: `SpanRef::scope` method, which returns a leaf-to-root
  `Iterator` including the leaf span (#1413)
- **registry**: `Scope::from_root` method, which reverses the `scope`
  iterator to iterate root-to-leaf (#1413)
- **registry**: `Context::event_span` method, which looks up the parent
  span of an event (#1434)
- **registry**: `Context::event_scope` method, returning a `Scope`
  iterator over the span scope of an event (#1434)
- **fmt**: `MakeWriter::make_writer_for` method, which allows returning
  a different writer based on a span or event's metadata (#1141)
- **fmt**: `MakeWriterExt` trait, with `with_max_level`,
  `with_min_level`, `with_filter`, `and`, and `or_else` combinators
  (#1274)
- **fmt**: `MakeWriter` implementation for `Arc<W> where &W: io::Write`
  (#1274)

Thanks to @teozkr and @Folyd for contributing to this release!
hawkw added a commit that referenced this pull request Jun 26, 2021
# 0.2.19 (June 25, 2021)

### Deprecated

- **registry**: `SpanRef::parents`, `SpanRef::from_root`, and
  `Context::scope` iterators, which are replaced by new `SpanRef::scope`
  and `Scope::from_root` iterators (#1413)

### Added

- **registry**: `SpanRef::scope` method, which returns a leaf-to-root
  `Iterator` including the leaf span (#1413)
- **registry**: `Scope::from_root` method, which reverses the `scope`
  iterator to iterate root-to-leaf (#1413)
- **registry**: `Context::event_span` method, which looks up the parent
  span of an event (#1434)
- **registry**: `Context::event_scope` method, returning a `Scope`
  iterator over the span scope of an event (#1434)
- **fmt**: `MakeWriter::make_writer_for` method, which allows returning
  a different writer based on a span or event's metadata (#1141)
- **fmt**: `MakeWriterExt` trait, with `with_max_level`,
  `with_min_level`, `with_filter`, `and`, and `or_else` combinators
  (#1274)
- **fmt**: `MakeWriter` implementation for `Arc<W> where &W: io::Write`
  (#1274)

Thanks to @teozkr and @Folyd for contributing to this release!
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
This backports PR tokio-rs#1141 from `master`.

subscriber: add `MakeWriter::make_writer_for`

## Motivation

In some cases, it might be desirable to configure the writer used for
writing out trace data based on the metadata of the span or event being
written. For example, we might want to send different levels to
different outputs, write logs from different targets to separate files,
or wrap formatted output in ANSI color codes based on levels. Currently,
it's not possible for the `MakeWriter` trait to model this kind of
behavior --- it has one method, `make_writer`, which is completely
unaware of *where* the data being written came from.

In particular, this came up in PR tokio-rs#1137, when discussing a proposal that
writing to syslog could be implemented as a `MakeWriter` implementation
rather than as a `Subscribe` implementation, so that all the formatting
logic from `tracing_subscriber::fmt` could be reused. See [here][1] for
details.

## Solution

This branch adds a new `make_writer_for` method to `MakeWriter`, taking
a `Metadata`. Implementations can opt in to metadata-specific behavior
by implementing this method. The method has a default implementation
that just calls `self.make_writer()` and ignores the metadata, so it's
only necessary to implement this when per-metadata behavior is required.
This isn't a breaking change to existing implementations.

There are a couple downsides to this approach: it's possible for callers
to skip the metadata-specific behavior by calling `make_writer` rather
than `make_writer_for`, and the impls for closures can't easily provide
metadata-specific behavior.

Since the upcoming release is going to be a breaking change anyway, we
may want to just make the breaking change of having
`MakeWriter::make_writer` _always_ take a `Metadata`, which solves these
problems. However, that can't be backported to v0.1.x as easily. Additionally,
that would mean that functions like `io::stdout` no longer implement
`MakeWriter`; they would have to be wrapped in a wrapper type or closure
that ignores metadata.

[1]: tokio-rs#1137 (comment)

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
This backports tokio-rs#1274 from `master`.

Depends on tokio-rs#1141.

This branch adds a `MakeWriterExt` trait which adds a number of
combinators for composing multiple types implementing `MakeWriter`.
`MakeWriter`s can now be teed together, filtered with minimum and
maximum levels, filtered with a `Metadata` predicate, and combined with
a fallback for when a writer is _not_ made for a particular `Metadata`.

I've also added a `MakeWriter` impl for `Arc<W>` when `&W` implements
`Write`. This enables `File`s to be used as `MakeWriter`s similarly to
how we will be able to in 0.3, with the additional overhead of having to
do ref-counting because we can't return a reference from `MakeWriter`
until the next breaking change.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
kaffarell pushed a commit to kaffarell/tracing that referenced this pull request May 22, 2024
# 0.2.19 (June 25, 2021)

### Deprecated

- **registry**: `SpanRef::parents`, `SpanRef::from_root`, and
  `Context::scope` iterators, which are replaced by new `SpanRef::scope`
  and `Scope::from_root` iterators (tokio-rs#1413)

### Added

- **registry**: `SpanRef::scope` method, which returns a leaf-to-root
  `Iterator` including the leaf span (tokio-rs#1413)
- **registry**: `Scope::from_root` method, which reverses the `scope`
  iterator to iterate root-to-leaf (tokio-rs#1413)
- **registry**: `Context::event_span` method, which looks up the parent
  span of an event (tokio-rs#1434)
- **registry**: `Context::event_scope` method, returning a `Scope`
  iterator over the span scope of an event (tokio-rs#1434)
- **fmt**: `MakeWriter::make_writer_for` method, which allows returning
  a different writer based on a span or event's metadata (tokio-rs#1141)
- **fmt**: `MakeWriterExt` trait, with `with_max_level`,
  `with_min_level`, `with_filter`, `and`, and `or_else` combinators
  (tokio-rs#1274)
- **fmt**: `MakeWriter` implementation for `Arc<W> where &W: io::Write`
  (tokio-rs#1274)

Thanks to @teozkr and @Folyd for contributing to this release!
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.

4 participants