Skip to content

Commit 001fc49

Browse files
hdshawkw
andauthored
feat(console): use tokio task ids in task views (#403)
Tokio Console generates its own sequential Id for internal tracking and indexing of objects (tasks, resources, etc.). However, this Id will be recreated if Console is restarted. In order to provide more useful information to the user, the task Id generated by Tokio can be used in the task list and task details screens instead. If used in this way, the ID field in the task list and task detail views will be stable across restarts of Console (assuming the monitored application is not restarted). This also frees up horizontal space, as the `task.id` attribute doesn't need to be displayed separately. The disadvantage of using Tokio's task Id is that it is not guaranteed to be present by the type system. To avoid problems with missing task Ids, the Tokio task Id is store in addition to the `span::Id` (used to communicate with the `console-subscriber`) and the sequential console `Id` (used in the `store`). If a task is missing the `task.id` field for whatever reason it will still appear, but with an empty ID. If multiple runtimes are active, then duplicate ID values will appear. Fixes: #385 Co-authored-by: Eliza Weisman <[email protected]>
1 parent 4ec13da commit 001fc49

File tree

4 files changed

+41
-7
lines changed

4 files changed

+41
-7
lines changed

console-api/src/common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl From<&dyn std::fmt::Debug> for field::Value {
210210
// or vice versa. However, this is unavoidable here, because `prost` generates
211211
// a struct with `#[derive(PartialEq)]`, but we cannot add`#[derive(Hash)]` to the
212212
// generated code.
213-
#[allow(clippy::derive_hash_xor_eq)]
213+
#[allow(clippy::derived_hash_with_manual_eq)]
214214
impl Hash for field::Name {
215215
fn hash<H: Hasher>(&self, state: &mut H) {
216216
match self {

tokio-console/src/state/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ impl Metadata {
271271
impl Field {
272272
const SPAWN_LOCATION: &'static str = "spawn.location";
273273
const NAME: &'static str = "task.name";
274+
const TASK_ID: &'static str = "task.id";
274275

275276
/// Converts a wire-format `Field` into an internal `Field` representation,
276277
/// using the provided `Metadata` for the task span that the field came

tokio-console/src/state/tasks.rs

+38-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
histogram::DurationHistogram,
66
pb_duration,
77
store::{self, Id, SpanId, Store},
8-
Field, Metadata, Visibility,
8+
Field, FieldValue, Metadata, Visibility,
99
},
1010
util::Percentage,
1111
view,
@@ -58,17 +58,32 @@ pub(crate) enum TaskState {
5858

5959
pub(crate) type TaskRef = store::Ref<Task>;
6060

61+
/// The Id for a Tokio task.
62+
///
63+
/// This should be equivalent to [`tokio::task::Id`], which can't be
64+
/// used because it's not possible to construct outside the `tokio`
65+
/// crate.
66+
///
67+
/// Within the context of `tokio-console`, we don't depend on it
68+
/// being the same as Tokio's own type, as the task id is recorded
69+
/// as a `u64` in tracing and then sent via the wire protocol as such.
70+
pub(crate) type TaskId = u64;
71+
6172
#[derive(Debug)]
6273
pub(crate) struct Task {
6374
/// The task's pretty (console-generated, sequential) task ID.
6475
///
6576
/// This is NOT the `tracing::span::Id` for the task's tracing span on the
6677
/// remote.
6778
id: Id<Task>,
79+
/// The `tokio::task::Id` in the remote tokio runtime.
80+
task_id: Option<TaskId>,
6881
/// The `tracing::span::Id` on the remote process for this task's span.
6982
///
7083
/// This is used when requesting a task details stream.
7184
span_id: SpanId,
85+
/// A cached string representation of the Id for display purposes.
86+
id_str: String,
7287
short_desc: InternedStr,
7388
formatted_fields: Vec<Vec<Span<'static>>>,
7489
stats: TaskStats,
@@ -147,6 +162,7 @@ impl TasksState {
147162
}
148163
};
149164
let mut name = None;
165+
let mut task_id = None;
150166
let mut fields = task
151167
.fields
152168
.drain(..)
@@ -157,6 +173,13 @@ impl TasksState {
157173
name = Some(strings.string(field.value.to_string()));
158174
return None;
159175
}
176+
if &*field.name == Field::TASK_ID {
177+
task_id = match field.value {
178+
FieldValue::U64(id) => Some(id as TaskId),
179+
_ => None,
180+
};
181+
return None;
182+
}
160183
Some(field)
161184
})
162185
.collect::<Vec<_>>();
@@ -170,15 +193,19 @@ impl TasksState {
170193
// remap the server's ID to a pretty, sequential task ID
171194
let id = ids.id_for(span_id);
172195

173-
let short_desc = strings.string(match name.as_ref() {
174-
Some(name) => format!("{} ({})", id, name),
175-
None => format!("{}", id),
196+
let short_desc = strings.string(match (task_id, name.as_ref()) {
197+
(Some(task_id), Some(name)) => format!("{task_id} ({name})"),
198+
(Some(task_id), None) => task_id.to_string(),
199+
(None, Some(name)) => name.as_ref().to_owned(),
200+
(None, None) => "".to_owned(),
176201
});
177202

178203
let mut task = Task {
179204
name,
180205
id,
206+
task_id,
181207
span_id,
208+
id_str: task_id.map(|id| id.to_string()).unwrap_or_default(),
182209
short_desc,
183210
formatted_fields,
184211
stats,
@@ -245,6 +272,10 @@ impl Task {
245272
self.span_id
246273
}
247274

275+
pub(crate) fn id_str(&self) -> &str {
276+
&self.id_str
277+
}
278+
248279
pub(crate) fn target(&self) -> &str {
249280
&self.target
250281
}
@@ -426,7 +457,9 @@ impl Default for SortBy {
426457
impl SortBy {
427458
pub fn sort(&self, now: SystemTime, tasks: &mut [Weak<RefCell<Task>>]) {
428459
match self {
429-
Self::Tid => tasks.sort_unstable_by_key(|task| task.upgrade().map(|t| t.borrow().id)),
460+
Self::Tid => {
461+
tasks.sort_unstable_by_key(|task| task.upgrade().map(|t| t.borrow().task_id))
462+
}
430463
Self::Name => {
431464
tasks.sort_unstable_by_key(|task| task.upgrade().map(|t| t.borrow().name.clone()))
432465
}

tokio-console/src/view/tasks.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl TableList<11> for TasksTable {
122122
warnings,
123123
Cell::from(id_width.update_str(format!(
124124
"{:>width$}",
125-
task.id(),
125+
task.id_str(),
126126
width = id_width.chars() as usize
127127
))),
128128
Cell::from(task.state().render(styles)),

0 commit comments

Comments
 (0)