-
Notifications
You must be signed in to change notification settings - Fork 742
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
Add tracing-flame
crate for generating flamegraphs/flamecharts
#631
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 is awesome, thank you so much! i left some notes, hopefully they're helpful!
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.
One problem right now is that the test / example both create files in the current directory whenever run, I'm not sure how best to resolve this.
Co-Authored-By: Eliza Weisman <[email protected]> Co-Authored-By: David Barsky <[email protected]>
Co-Authored-By: David Barsky <[email protected]>
@yaahc: I haven't tried this yet, but for:
Would taking a development dependency on |
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.
some docs nits. otherwise, if you think this API seems right, I'm good with it.
Co-Authored-By: Eliza Weisman <[email protected]>
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 noticed a potential problem with the way we record the START
timestamp. Besides that, I think this is looking good. I left a few more nits, and some docs edits, but I think this ought to be ready to merge fairly soon.
tracing-flame/src/lib.rs
Outdated
//! as they were emitted by `tracing-flame`, so that it is clear when each | ||
//! span is entered relative to others and get an accurate visual trace of | ||
//! the execution of your program. This representation is best created with a | ||
//! `flamechart`, which _does not_ sort or collapse identical stack frames. |
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 should probably end by explaining how to make a flamechart rather than a flamegraph?
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 can't yet :S, its a feature I intend to add to inferno once this is released
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.
ah, okay, nevermind then. it might be worth mentioning that so we don't incorrectly imply that you can make flamecharts in inferno?
tracing-flame/src/lib.rs
Outdated
where | ||
S: Subscriber, | ||
W: Write + 'static, |
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.
Is the where
clause necessary here, or can these just be present on the impls for the type?
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.
No, but I was under the impression that we'd get more useful compiler errors with the bounds on the type.
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 users can't write out literals of the type, putting the bounds on the impl block with new
in it should give them equally useful errors.
Co-Authored-By: Eliza Weisman <[email protected]>
re: the start time thing, we should probably at least spot-check the output from a multithreaded program... we could take the existing example, spawn a couple threads that run the same code, and make sure the output isn't "totally weird" |
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 noticed a panic on CI, and I think I know how to fix it.
Once that's fixed (and we have a test for multithreaded code), I think we can merge this.
tracing-flame/Cargo.toml
Outdated
"Eliza Weisman <[email protected]>", | ||
"David Barsky <[email protected]>", |
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.
take it or leave it: i don't personally feel the need to be listed as an author for this crate...you did all the work :)
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.
Same here; I just reviewed it :)
Co-Authored-By: Eliza Weisman <[email protected]>
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 feel pretty good about this, especially since we can update this later. No need to let this collect dust.
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.
A couple last non-blocking nits, but this looks pretty good to me. It would be good to add a README and stuff before publishing this, but that can be done in a follow-up branch.
Motivation ---------- Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). Proposal -------- This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
Motivation ---------- Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). Proposal -------- This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
Motivation ---------- Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). Proposal -------- This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
Motivation ---------- Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). Proposal -------- This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
## Motivation Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). ## Proposal This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
This backports PR #781 from `master`. ## Motivation Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). ## Proposal This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
This backports PR #781 from `master`. ## Motivation Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in #631 (comment). ## Proposal This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes #675. Signed-off-by: Eliza Weisman <[email protected]>
…kio-rs#1654) This backports PR tokio-rs#781 from `master`. ## Motivation Currently, the `tracing-subscriber` crate has the `MakeWriter` trait for customizing the io writer used by `fmt`. This trait is necessary (rather than simply using a `Write` instance) because the default implementation performs the IO on the thread where an event was recorded, meaning that a separate writer needs to be acquired by each thread (either by calling a function like `io::stdout`, by locking a shared `Write` instance, etc). Right now there is a blanket impl for `Fn() -> T where T: Write`. This works fine with functions like `io::stdout`. However, the _other_ common case for this trait is locking a shared writer. Therefore, it makes sense to see an implementation like this: ``` rust impl<'a, W: io::Write> MakeWriter for Mutex<W> where W: io::Write, { type Writer = MutexWriter<'a, W>; fn make_writer(&self) -> Self::Writer { MutexWriter(self.lock().unwrap()) } } pub struct MutexWriter<'a, W>(MutexGuard<'a, W>); impl<W: io::Write> io::Write for MutexWriter<'_, W> { // write to the shared writer in the `MutexGuard`... } ``` Unfortunately, it's impossible to write this. Since `MakeWriter` always takes an `&self` parameter and returns `Self::Writer`, the generic parameter is unbounded: ``` Checking tracing-subscriber v0.2.4 (/home/eliza/code/tracing/tracing-subscriber) error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> tracing-subscriber/src/fmt/writer.rs:61:6 | 61 | impl<'a, W: io::Write> MakeWriter for Mutex<W> | ^^ unconstrained lifetime parameter error: aborting due to previous error ``` This essentially precludes any `MakeWriter` impl where the writer is borrowed from the type implementing `MakeWriter`. This is a significant blow to the usefulness of the trait. For example, it prevented the use of `MakeWriter` in `tracing-flame` as suggested in tokio-rs#631 (comment). ## Proposal This PR changes `MakeWriter` to be generic over a lifetime `'a`: ```rust pub trait MakeWriter<'a> { type Writer: io::Write; fn make_writer(&'a self) -> Self::Writer; } ``` The `self` parameter is now borrowed for the `&'a` lifetime, so it is okay to return a writer borrowed from `self`, such as in the `Mutex` case. I've also added an impl of `MakeWriter` for `Mutex<T> where T: Writer`. Unfortunately, this is a breaking change and will need to wait until we release `tracing-subscriber` 0.3. Fixes tokio-rs#675. Signed-off-by: Eliza Weisman <[email protected]>
No description provided.