Skip to content

Allow thread name and id to be displayed in traces #818

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

Merged
merged 18 commits into from
Jul 17, 2020

Conversation

Pothulapati
Copy link
Contributor

Motivation

Fixes #809

Solution

This PR adds two new methods i.e with_thread_ids and
with_thread_names to the fmt Layer which passed till
the pre-configured Format type, where the check is performed
and these the above values are formatted based on the flags.

The formatting is done by creating to new Types that impl Display
for Id and Name. For Thread Name, we keep track of the max length
of the thread name that occured till now using a thread safe static
type and pad accordingly. For Thread Id, we pad directly with 2
as in most cases we would see a double digit number of threads.

Signed-off-by: Tarun Pothulapati [email protected]

This PR adds two new methods i.e `with_thread_ids` and
`with_thread_names` to the fmt Layer which passed till
the pre-configured Format type, where the check is performed
and these the above values are formatted based on the flags.

The formatting is done by creating to new Types that impl Display
for Id and Name. For Thread Name, we keep track of the max length
of the thread name that occured till now using a thread safe static
type and pad accordingly. For Thread Id, we pad directly with 2
as in most cases we would see a double digit number of threads.

Signed-off-by: Tarun Pothulapati <[email protected]>
@Pothulapati Pothulapati requested review from hawkw and a team as code owners July 14, 2020 03:08
Signed-off-by: Tarun Pothulapati <[email protected]>
Signed-off-by: Tarun Pothulapati <[email protected]>
Signed-off-by: Tarun Pothulapati <[email protected]>
@hawkw hawkw requested a review from davidbarsky July 14, 2020 17:13
Copy link
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

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

This definitely looks like the right overall approach. I had some notes on the specifics of the implementation.

Also, I'm ccing @davidbarsky, since I imagine he has formatting opinions...

Comment on lines 2 to 3
/// This is a example showing how thread info can be enabled
/// to be attached with events
Copy link
Member

Choose a reason for hiding this comment

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

How about:

Suggested change
/// This is a example showing how thread info can be enabled
/// to be attached with events
/// This is a example showing how thread info can be displayed when
/// formatting events with `tracing_subscriber::fmt`.

Copy link
Member

Choose a reason for hiding this comment

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

I believe that this may have been incorrectly resolved; I like @hawkw's proposed change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahhh, I had this locally but did not save it. Thank you! :)

// fall-back to thread id when name is absent and id's are not enabled
if !self.display_thread_id {
serializer.serialize_entry(
"threadId",
Copy link
Member

Choose a reason for hiding this comment

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

I think that in this case, the key should maybe still be threadName --- if you enable the thread name option, you might reasonably expect all the output JSON to have a field named threadName, and might write parsing code that expects that...

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, Makes sense!

if !self.display_thread_id {
serializer.serialize_entry(
"threadId",
&format!("{:?}", std::thread::current().id()),
Copy link
Member

Choose a reason for hiding this comment

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

Using format! here allocates, since format! constructs a new String. It would be nice if that was not necessary.

After looking at the serde API docs, though, it seems like it might be --- there's no way to serialize something's Debug implementation as a string value except by turning it into a String and borrowing it. That seems like a deficiency in serde (or, I'm missing something in the serde API).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I did try searching a way for serde to directly use Debug impl but could not find how. So, had to use format!

Comment on lines 406 to 413
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
}
}
}
}

if self.display_thread_id {
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the FmtThreadId newtype is really necessary just to add a couple characters of padding...we could just write

Suggested change
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
}
}
}
}
if self.display_thread_id {
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
write!(writer, "{:>2?} ", std::thread::current().id())?;
}
}
}
}
if self.display_thread_id {
write!(writer, "{:>2?} ", std::thread::current().id())?;

instead, which is a bit simpler...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, That makes sense! Just thought it would be better to have a single place which knows how to format threadId's 👍

Comment on lines 473 to 489
if self.display_thread_name {
match std::thread::current().name() {
Some(name) => {
write!(writer, "{} ", FmtThreadName::new(name))?;
}
None => {
// fall-back to thread id when name is absent and id's are not enabled
if !self.display_thread_id {
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
}
}
}
}

if self.display_thread_id {
write!(writer, "{} ", FmtThreadId::new(std::thread::current().id()))?;
}
Copy link
Member

Choose a reason for hiding this comment

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

same suggestions as the FullCtx suggestions above.

Copy link
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

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

A couple additional suggestions, but overall, this looks great.

I'd still love to hear @davidbarsky's thoughts on the formatting.

Comment on lines 17 to 18
.with_thread_ids(true)
.with_thread_names(true)
Copy link
Member

Choose a reason for hiding this comment

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

It could be good to add comments on these lines explaining what they're doing, since it's the main thing the example is intended to demonstrate.

Copy link
Member

Choose a reason for hiding this comment

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

Also, take it or leave it: might be nice if the example had CLI arguments to control thread IDs and thread names separately, so that users can demo all the possible combinations? But, adding argument-parsing would add a bunch of code not directly related to the things we're trying to show in this example, so I am not sure whether or not this is a good idea. What do you think?

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.

This does look very good, thank you! Just a few notes on documentation.

Comment on lines 2 to 3
/// This is a example showing how thread info can be enabled
/// to be attached with events
Copy link
Member

Choose a reason for hiding this comment

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

I believe that this may have been incorrectly resolved; I like @hawkw's proposed change.

Comment on lines 842 to 843

static MAX_LEN: AtomicUsize = AtomicUsize::new(0);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: can you add a comment explaining why this is necessary? If I'm following along correctly, this sets the maximum length a thread name can be, to ensure better formatted logs? If so, can you add a comment saying that?

Also, what is the scope of MAX_LEN? It doesn't get dropped once this fmt call ends, does it?

Copy link
Member

Choose a reason for hiding this comment

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

Also, what is the scope of MAX_LEN? It doesn't get dropped once this fmt call ends, does it?

Statics don't get dropped. The only difference between a static inside a function and a static outside of a function is visibility; the function-local static is only visible to that function.

Copy link
Member

Choose a reason for hiding this comment

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

re:

Nit: can you add a comment explaining why this is necessary?

@Pothulapati, IIRC, when I suggested we do this, I think I sent a version of this code with comments explaining what it was doing; if you like, please feel free to reuse that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done! :)

/// when formatting events
///
/// [thread ID]: https://doc.rust-lang.org/stable/std/thread/struct.ThreadId.html
pub fn with_thread_id(self, display_thread_id: bool) -> Format<F, T> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should these fn names also be plural here? My reason for keeping them singular is that they deal with a single event being part of Format. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

IMO, I think it's better for them to be consistent --- it's annoying to call a method on one type, and then try to call it on a different type, and get an error because the name is slightly different. Also, the formatter will almost certainly be used to format more than one event, too.

But, I could definitely be convinced otherwise!

Signed-off-by: Tarun Pothulapati <[email protected]>
@hawkw
Copy link
Member

hawkw commented Jul 17, 2020

Okay, this looks great to me! I'm going to go ahead and merge it.

Thanks again for working on this!

@hawkw hawkw merged commit c109197 into tokio-rs:master Jul 17, 2020
hawkw added a commit that referenced this pull request Jul 17, 2020
Changed

- **fmt**: When the `chrono` dependency is enabled, the `SystemTime`
  timestamp now emits human-readable timestamps rather than using
  `SystemTime`'s fmt::Debug`implementation (`chrono` is still required
  for customized timestamp formatting) (#807)
- **ansi**: Updated `ansi_term` dependency to 0.12 (#816)

Added

- **json**: `with_span_list` method to configure the JSON formatter to
  include a list of all spans in the current trace in formatting events
  (similarly to the text formatter) (#741)
- **json**: `with_current_span` method to configure the JSON formatter
  to include a field for the _current_ span (the leaf of the trace) in
  formatted events (#741)
- **fmt**: `with_thread_names` and `with_thread_ids` methods to
  configure `fmt::Subscriber`s and `fmt::Layer`s to include the thread
  name and/or thread ID of the current thread when formatting events
  (#818)

Thanks to new contributors @mockersf, @keetonian, and @Pothulapati for
contributing to this release!
hawkw added a commit that referenced this pull request Jul 17, 2020
### Changed

- **fmt**: When the `chrono` dependency is enabled, the `SystemTime`
  timestamp now emits human-readable timestamps rather than using
  `SystemTime`'s fmt::Debug`implementation (`chrono` is still required
  for customized timestamp formatting) (#807)
- **ansi**: Updated `ansi_term` dependency to 0.12 (#816)

### Added

- **json**: `with_span_list` method to configure the JSON formatter to
  include a list of all spans in the current trace in formatting events
  (similarly to the text formatter) (#741)
- **json**: `with_current_span` method to configure the JSON formatter
  to include a field for the _current_ span (the leaf of the trace) in
  formatted events (#741)
- **fmt**: `with_thread_names` and `with_thread_ids` methods to
  configure `fmt::Subscriber`s and `fmt::Layer`s to include the thread
  name and/or thread ID of the current thread when formatting events
  (#818)

Thanks to new contributors @mockersf, @keetonian, and @Pothulapati 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.

subscriber: option to add thread names/IDs in fmt log lines
3 participants