-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Introduce tokio-trace
#827
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks pretty good; the only comments are around documentation and examples.
(I think I've reviewed this code before, and signed off on it.)
c78785d
to
2251a90
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making a review checkpoint here, just before tokio-trace/src/span.rs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good 👍 Almost there, so close...
Left thoughts / questions inline. Also:
-
Should there be a
debug_span!
macro and aDebugSpan
type? When compiled w/o debug_assertions, it would be a unit struct. -
The public API of
tokio-trace
should probably be pruned down to focus on instrumentation (not subscribers). Any API that isn't needed should be removed or marked as#[doc(hidden)]
.
/// span will silently do nothing. Thus, the handle can be used in the same | ||
/// manner regardless of whether or not the trace is currently being collected. | ||
#[derive(Clone, PartialEq, Hash)] | ||
pub struct Span<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lifetime is going to be a bit annoying when storing the span in a future, which will be the common case. Is it necessary to have in this lib?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had wondered about that when the lifetime was added (hawkw/tokio-trace-prototype#157 (comment)), too, and I'm still not sold on it.
In the tokio-trace-futures
lib (in tokio-trace-nursery
), IIRC, we just have hardcoded everything as Span<'static>
. I thought it might be better to be more flexible here, and have the -futures
crate introduce the static requirement. But, it does add more noise. I'd be happy to change this to always be 'static
.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This picks up upstream changes from hawkw/tokio-trace-prototype#168. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Apparently we were missing this. I wrote a doc example that used it and was surprised when it didn't work. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Looks like this was overlooked in hawkw/tokio-trace-prototype#168. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
I didn't want these to be covered only by doctests. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
The new tests revealed these. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This lets us completely skip `Span::new` in this case. This brings the benchmarks for the disabled case back into parity with `log`. Before: ``` Running target/release/deps/no_subscriber-1064195d321c8640 running 5 tests test bench_1_atomic_load ... bench: 0 ns/iter (+/- 0) test bench_costly_field_no_subscriber ... bench: 6 ns/iter (+/- 0) test bench_log_no_logger ... bench: 0 ns/iter (+/- 0) test bench_no_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_span_no_subscriber ... bench: 6 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 5 measured; 0 filtered out Running target/release/deps/subscriber-7319775649c47f41 running 4 tests test span_no_fields ... bench: 37 ns/iter (+/- 10) test span_repeatedly ... bench: 5,626 ns/iter (+/- 812) test span_with_fields ... bench: 101 ns/iter (+/- 20) test span_with_fields_record ... bench: 1,035 ns/iter (+/- 171) test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out ``` After: ``` Running target/release/deps/no_subscriber-1064195d321c8640 running 5 tests test bench_1_atomic_load ... bench: 0 ns/iter (+/- 0) test bench_costly_field_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_log_no_logger ... bench: 0 ns/iter (+/- 0) test bench_no_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 5 measured; 0 filtered out Running target/release/deps/subscriber-7319775649c47f41 running 4 tests test span_no_fields ... bench: 36 ns/iter (+/- 8) test span_repeatedly ... bench: 5,381 ns/iter (+/- 1,075) test span_with_fields ... bench: 110 ns/iter (+/- 19) test span_with_fields_record ... bench: 1,019 ns/iter (+/- 247) test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out ``` Signed-off-by: Eliza Weisman <eliza@buoyant.io>
bdeea40
to
d1195ef
Compare
Ugh, code duplication :/
We should set a max number of span fields. 32 is a good start. This ensures we can use stack arrays for passing fields around. |
module_path: Option<&'a str>, | ||
file: Option<&'a str>, | ||
line: Option<u32>, | ||
field_names: &'static [&'static str], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
field_names
should not be exposed as &[&str]
. We probably want an opaque type for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory, I agree. In practice, since we can't use const fn
s yet, the opaque type would need to have another constructor macro, so it just feels like a bunch of additional complexity to me. I can make that change regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(also, I think FieldSet
is essentially the opaque type for this, but it also has a callsite ID. we can't construct that here since we need to create the metadata before the callsite)
Having some arbitrary limit on the number of fields or the nested depth of complex objects is also, in my opinion, a defensible philosophical decision. It makes you stop and think about what it is you're actually collecting. |
This branch reworks `tokio-trace`'s field recording APIs to improve performance in cases where field values are provided in batches. `tokio-trace` supports recording values for span fields either when the span is constructed, or at any point during its lifetime. In order to support this, the current implementation represents each field value recorded with a separate call to a `Subscriber::record_$TYPE` function. This can incur significant overhead --- for example, if the span data is stored behind a mutex, every call to `record` must reacquire the lock. Providing most or all of the fields on a span up-front as it is constructed is likely to be the common case, but in the current design, this use-case has to pay performance costs in order to support the less common case of adding values post-initialization. Even in situations where some field values are unknown, if a majority are recorded at construction-time, each individual value takes a separate trip through the `record` pipeline. This branch rewrites this API so that all field recording is done by a `ValueSet` type, which groups together multiple values. A subscriber may pass an implementation of `Record` to the `ValueSet`'s `record` function, which will record all the provided values with that recorder. When a span is constructed, the `Subscriber::new_span` method is called with a `ValueSet` containing all the fields provided to the `span!` macro, and additional fields may be recorded either individually or in batches. In addition, the previous implementation of `Event`s on top of `Span`s has been removed and replaced with a new `Event` type which goes through a separate `Subscriber::event` function. Events are always known to have all their values at construction time, since the event cannot live long enough to have more values added. Thus, an `Event` also contains a `ValueSet`. I've added new test-support code for expecting certain fields to be recorded by spans and events, and written new tests to cover this behaviour. I've also updated the example code, and my experience doing so suggests that the new API is also somewhat more ergonomic to use. These changes result in significant improvement on the benchmark that simulates recording multiple fields on a span (`bench_span_with_fields_record`). Before: ``` Running target/release/deps/no_subscriber-a6af9f5aa4934184 running 5 tests test bench_1_atomic_load ... bench: 0 ns/iter (+/- 0) test bench_costly_field_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_log_no_logger ... bench: 0 ns/iter (+/- 0) test bench_no_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 5 measured; 0 filtered out Running target/release/deps/subscriber-ba753dc1d87e480d running 4 tests test span_no_fields ... bench: 41 ns/iter (+/- 5) test span_repeatedly ... bench: 5,418 ns/iter (+/- 658) test span_with_fields ... bench: 110 ns/iter (+/- 42) test span_with_fields_record ... bench: 1,047 ns/iter (+/- 240) test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out ``` After: ``` Running target/release/deps/no_subscriber-611d60b262fbbcfb running 5 tests test bench_1_atomic_load ... bench: 0 ns/iter (+/- 0) test bench_costly_field_no_subscriber ... bench: 1 ns/iter (+/- 0) test bench_log_no_logger ... bench: 0 ns/iter (+/- 0) test bench_no_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test bench_span_no_subscriber ... bench: 0 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 5 measured; 0 filtered out Running target/release/deps/subscriber-7a1279c84c0d50ff running 4 tests test span_no_fields ... bench: 37 ns/iter (+/- 9) test span_repeatedly ... bench: 4,502 ns/iter (+/- 712) test span_with_fields ... bench: 54 ns/iter (+/- 17) test span_with_fields_record ... bench: 364 ns/iter (+/- 38) test result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out ``` Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
9ca409d
to
fc2fa65
Compare
These have to be in `-core` due to the orphan rules, but seem very handy. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
The `trace_dbg!` macro requires this. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
See also tokio-rs/tracing@5296a4c Signed-off-by: Eliza Weisman <eliza@buoyant.io>
FWIW I believe the AppVeyor CI build for f2c8354 failed due to an unrelated issue. |
Trailing commas are optional when fields are included, and when fields are delimited from the format string for the message. Before these would error: ``` event!(tokio_trace::Level::DEBUG, foo = 3, bar = 3,); event!(tokio_trace::Level::DEBUG, { foo = 2, bar = 78, }, "baz"); ``` Now they do not. Tests have been extended. Signed-off-by: Kevin Leimkuhler <kevinl@buoyant.io>
* tokio-trace: Extend `span!` rules Add the ability to define a target and log level for individual spans. `level: ...` must be included since both `$lvl` and `$name` are `expr`. The macro tests and documentation tests have been extended. Signed-off-by: Kevin Leimkuhler <kevinl@buoyant.io> * Add additional `span!` test Change default log level of `span!` to `TRACE` to reflect the default level used in `callsite!`. Signed-off-by: Kevin Leimkuhler <kevinl@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Looks like RLS missed a couple uses Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
<!-- Thank you for your Pull Request. Please provide a description above and review the requirements below. Bug fixes and new features should include tests. Contributors guide: https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md --> ## Motivation In asynchronous systems like Tokio, interpreting traditional log messages can often be quite challenging. Since individual tasks are multiplexed on the same thread, associated events and log lines are intermixed making it difficult to trace the logic flow. Currently, none of the available logging frameworks or libraries in Rust offer the ability to trace logical paths through a futures-based program. There also are complementary goals that can be accomplished with such a system. For example, metrics / instrumentation can be tracked by observing emitted events, or trace data can be exported to a distributed tracing or event processing system. In addition, it can often be useful to generate this diagnostic data in a structured manner that can be consumed programmatically. While prior art for structured logging in Rust exists, it is not currently standardized, and is not "Tokio-friendly". ## Solution This branch adds a new library to the tokio project, `tokio-trace`. `tokio-trace` expands upon logging-style diagnostics by allowing libraries and applications to record structured events with additional information about *temporality* and *causality* --- unlike a log message, a span in `tokio-trace` has a beginning and end time, may be entered and exited by the flow of execution, and may exist within a nested tree of similar spans. In addition, `tokio-trace` spans are *structured*, with the ability to record typed data as well as textual messages. The `tokio-trace-core` crate contains the core primitives for this system, which are expected to remain stable, while `tokio-trace` crate provides a more "batteries-included" API. In particular, it provides macros which are a superset of the `log` crate's `error!`, `warn!`, `info!`, `debug!`, and `trace!` macros, allowing users to begin the process of adopting `tokio-trace` by performing a drop-in replacement. ## Notes Work on this project had previously been carried out in the [tokio-trace-prototype] repository. In addition to the `tokio-trace` and `tokio-trace-core` crates, the `tokio-trace-prototype` repo also contains prototypes or sketches of adapter, compatibility, and utility crates which provide useful functionality for `tokio-trace`, but these crates are not yet ready for a release. When this branch is merged, that repository will be archived, and the remaining unstable crates will be moved to a new `tokio-trace-nursery` repository. Remaining issues on the `tokio-trace-prototype` repo will be moved to the appropriate new repo. The crates added in this branch are not _identical_ to the current head of the `tokio-trace-prototype` repo, as I did some final clean-up and docs polish in this branch prior to merging this PR. [tokio-trace-prototype]: https://github.com/hawkw/tokio-trace-prototype Closes: #561 Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Motivation
In asynchronous systems like Tokio, interpreting traditional log
messages can often be quite challenging. Since individual tasks are
multiplexed on the same thread, associated events and log lines are
intermixed making it difficult to trace the logic flow. Currently, none
of the available logging frameworks or libraries in Rust offer the
ability to trace logical paths through a futures-based program.
There also are complementary goals that can be accomplished with such a
system. For example, metrics / instrumentation can be tracked by
observing emitted events, or trace data can be exported to a distributed
tracing or event processing system.
In addition, it can often be useful to generate this diagnostic data in
a structured manner that can be consumed programmatically. While prior
art for structured logging in Rust exists, it is not currently
standardized, and is not "Tokio-friendly".
Solution
This branch adds a new library to the tokio project,
tokio-trace
.tokio-trace
expands upon logging-style diagnostics by allowinglibraries and applications to record structured events with additional
information about temporality and causality --- unlike a log
message, a span in
tokio-trace
has a beginning and end time, may beentered and exited by the flow of execution, and may exist within a
nested tree of similar spans. In addition,
tokio-trace
spans arestructured, with the ability to record typed data as well as textual
messages.
The
tokio-trace-core
crate contains the core primitives for thissystem, which are expected to remain stable, while
tokio-trace
crateprovides a more "batteries-included" API. In particular, it provides
macros which are a superset of the
log
crate'serror!
,warn!
,info!
,debug!
, andtrace!
macros, allowing users to begin theprocess of adopting
tokio-trace
by performing a drop-in replacement.Notes
Work on this project had previously been carried out in the
tokio-trace-prototype repository. In addition to the
tokio-trace
andtokio-trace-core
crates, thetokio-trace-prototype
repo alsocontains prototypes or sketches of adapter, compatibility, and utility
crates which provide useful functionality for
tokio-trace
, but thesecrates are not yet ready for a release. When this branch is merged, that
repository will be archived, and the remaining unstable crates will be
moved to a new
tokio-trace-nursery
repository. Remaining issues on thetokio-trace-prototype
repo will be moved to the appropriate new repo.The crates added in this branch are not identical to the current head
of the
tokio-trace-prototype
repo, as I did some final clean-up and docspolish in this branch prior to opening this PR.
Refs: #561
Signed-off-by: Eliza Weisman eliza@buoyant.io