-
-
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
tracing: add tokio::task::Builder #3881
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.
Looks good to me. Some basic tests using the Builder would be nice. We don't have any testing infrastructure to validate tracing spaces yet, so that doesn't matter so much. However, we could duplicate some of the existing "spawn" tests to go via the builder.
/// | ||
/// See [`task::spawn_local`][crate::task::spawn_local] | ||
/// for more details | ||
#[cfg_attr(tokio_track_caller, track_caller)] |
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.
For the caller location to be tracked, do we need to call Location::caller()
in the method proper or is the location passed to inner calls?
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.
My read of this is that it just makes the annotated function "transparent" to the walked Location, so inner calls don't report this function, but its caller. I'm new to this interface though — did I misunderstand it?
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.
Not sure what you mean exactly, but here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9fe91c5452a8add4f3b9a45e688ec3c7
Without #[track_caller]
on fn two()
, Location::caller()
isn't what we want. If fn two()
does have #[track_caller]
then it is passed through one()
and reports the line within fn main()
.
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.
So it looks like you are correct.
Once this lands, it would be nice to see it piped through the console. I think it would be fine to switch the console git repo to point to the latest tokio git. |
Currently the console doesn't do anything special with In general, I wasn't quite sure how to test this. Does CI run tracing tests? Would I register a subscriber, spawn a task, and then check that the right task.name was registered? Another note: I wasn't sure how to handle tracing spawn_blocking, since |
tokio/tokio/src/runtime/handle.rs Lines 179 to 200 in e7d74b3
So, AFAICT, I think using the builder will currently wrap |
I wouldn't worry too much about writing tests for this; you're right that we don't currently have any way of writing tests for trace output, and I'm not sure if it's really worth spending a lot of time on. Instead, I would just run some of the examples with |
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.
overall, this definitely looks like the right direction to go in --- i think this is close to ready! the main things that i noticed are
- i think using the builder's
spawn_blocking
will currently produce duplicate spans --- i left a comment explaining that in more detail - it would be nice to have a way to spawn named tasks using a
Handle
. i left a comment on that as well.
function = %std::any::type_name::<F>(), | ||
); | ||
fut.instrument(span) | ||
}; | ||
|
||
#[cfg(not(all(tokio_unstable, feature = "tracing")))] | ||
let _ = name; |
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.
clever
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.
Anyway, this looks good to me. Because it is an unstable API, I won't block on a test.
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 good to me!
Running CI since it needed permissions to do so. |
tokio/src/task/builder.rs
Outdated
#[derive(Default, Debug)] | ||
pub struct Builder<'a> { | ||
name: Option<&'a str>, | ||
} | ||
|
||
impl Builder<'_> { | ||
/// Creates a new task builder. | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
/// Assigns a name to the task which will be spawned. | ||
pub fn name<'b>(&self, name: &'b str) -> Builder<'b> { | ||
Builder { name: Some(name) } | ||
} |
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 seems dangerous. If we add another option that also has a lifetime in the future, then this signature will not work because that other option's lifetime will continue to be 'a
, not 'b
. To handle that, we would need two lifetime parameters, but afaik adding a new lifetime parameter is a breaking change.
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.
Wouldn't adding a second lifetime to a public struct already be a breaking change?
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.
Yes. My point is that you also can't reuse the same lifetime for both fields.
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.
What other lifetime are we discussing here, and what is the proposed change? Only accepting &'static str
(or Into<Cow<'static, str>>
in order to not lose support for dynamic names, which we established we didn't want to enforce against)? I'm not sure what you're proposing
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.
We couldn't add another lifetime once this is stable so I don't understand the concern. Are you proposing that we design for hypothetical future borrows to necessarily have the same lifetime as the 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.
It's more that this introduces a convention for having borrowed options for the builder. If we add any other string arguments, then we would be in trouble if the convention is to store them as a reference.
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.
If the signature is changed to this, then reusing the 'a
for all parameters that are by reference would not be a problem.
impl<'a> Builder<'a> {
/// Assigns a name to the task which will be spawned.
pub fn name(&self, name: &'a str) -> Builder<'a> {
Builder { name: Some(name) }
}
}
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.
[deleted comment]
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.
Thanks for the suggestion, addressed in 183d556
Now that Tokio supports optional user-provided task names (in tokio-rs/tokio#3881), the task spans it generates may contain empty fields (if the user does not provide a name for a spawned task). However, the console always displays the `task.name` field, even when they are empty. This wastes space, and looks kind of ugly. This branch updates the console to skip displaying fields whose value is the empty string. ### Before: ![image](https://user-images.githubusercontent.com/2796466/128227118-7ee3b4d7-634f-410f-a1d0-4e5afd423566.png) ### After: ![image](https://user-images.githubusercontent.com/2796466/128227203-248bd059-839a-4254-8470-2761f726c885.png) Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Depends on #81 (since it touches the same code and would cause merge conflicts). * feat(console): don't display fields with "" values Now that Tokio supports optional user-provided task names (in tokio-rs/tokio#3881), the task spans it generates may contain empty fields (if the user does not provide a name for a spawned task). However, the console always displays the `task.name` field, even when they are empty. This wastes space, and looks kind of ugly. This branch updates the console to skip displaying fields whose value is the empty string. ### Before: ![image](https://user-images.githubusercontent.com/2796466/128227118-7ee3b4d7-634f-410f-a1d0-4e5afd423566.png) ### After: ![image](https://user-images.githubusercontent.com/2796466/128227203-248bd059-839a-4254-8470-2761f726c885.png) Signed-off-by: Eliza Weisman <eliza@buoyant.io> * refac(console): factor out field from pb conversion This factors out the protobuf `Field` -> internal `Field` conversion into a method, and cleans it up a bit. No functional change, except that we now take the proto `Field` type by value, so we don't have to clone strings out of it like we did previously (due to taking it by reference). I also added the metadata ID to the `Metadata` type, so that we can continue asserting that the metadata's ID matches the expected one for the field, without having to pass that in separately. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Depends on #81 (since it touches the same code and would cause merge conflicts). * feat(console): don't display fields with "" values Now that Tokio supports optional user-provided task names (in tokio-rs/tokio#3881), the task spans it generates may contain empty fields (if the user does not provide a name for a spawned task). However, the console always displays the `task.name` field, even when they are empty. This wastes space, and looks kind of ugly. This branch updates the console to skip displaying fields whose value is the empty string. ### Before: ![image](https://user-images.githubusercontent.com/2796466/128227118-7ee3b4d7-634f-410f-a1d0-4e5afd423566.png) ### After: ![image](https://user-images.githubusercontent.com/2796466/128227203-248bd059-839a-4254-8470-2761f726c885.png) Signed-off-by: Eliza Weisman <eliza@buoyant.io> * refac(console): factor out field from pb conversion This factors out the protobuf `Field` -> internal `Field` conversion into a method, and cleans it up a bit. No functional change, except that we now take the proto `Field` type by value, so we don't have to clone strings out of it like we did previously (due to taking it by reference). I also added the metadata ID to the `Metadata` type, so that we can continue asserting that the metadata's ID matches the expected one for the field, without having to pass that in separately. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
PR #3881 factored out the spawning of local tasks on a `LocalSet` into a function `spawn_local_inner`, so that the implementation could be shared with the `task::Builder` API. But, that PR neglected to add a `#[track_caller]` attribute to `spawn_local_inner`, so the `tracing` spans for local tasks are all generated with `spawn_local_inner` as their spawn location, rather than forwarding the actual spawn location from the calling function. This causes pretty useless results when using `tokio-console` with code that spawns a number of local tasks, such as Actix (https://old.reddit.com/r/rust/comments/snt5fq/can_tokioconsole_profile_actixrt/) This commit fixes the issue by adding the missing `#[track_caller]` attribute.
PR #3881 factored out the spawning of local tasks on a `LocalSet` into a function `spawn_local_inner`, so that the implementation could be shared with the `task::Builder` API. But, that PR neglected to add a `#[track_caller]` attribute to `spawn_local_inner`, so the `tracing` spans for local tasks are all generated with `spawn_local_inner` as their spawn location, rather than forwarding the actual spawn location from the calling function. This causes pretty useless results when using `tokio-console` with code that spawns a number of local tasks, such as Actix (https://reddit.com/r/rust/comments/snt5fq/can_tokioconsole_profile_actixrt/) This commit fixes the issue by adding the missing `#[track_caller]` attribute.
Motivation
Task name annotations are helpful in order to better understand what is happening in an async application
Solution
This PR adds a cfg_trace-gated unstable builder tokio::task::Builder, which allows authors to annotate a task at runtime with an appropriate name. It includes
task.name
instrumentation for Builder::spawn, Builder::spawn_local, and Builder::spawn_blocking