Feature (sync): #[trace(recurse=all)]
Need to evaluate the defaults in #133 in light of the following behavior - are those defaults the least restrictive? Here we'll need to have the test suite be more comprehensive.
The model phase of #113 is taking shape.
The following two cases should be possible (initially sync only):
Tracing inner functions
#[trace(recurse=all)]
pub fn outer() {
defined_later();
fn defined_later() {
println!("I'm traced too"):
}
}
Where all setting from the outer trace percolate down. It is the users responsibility to ensure the settings are appropriate for the child functions.
Tracing a module
More tricky, but likely possible, and empowering for novice-users when debugging:
#[trace(recurse=all)]
mod traced {
fn outer() {
println!("Look Dad, I'm traced!");
defined_later();
pub fn defined_later() {
println!("I'm traced too");
}
}
}
async and entry on poll traces should also be possible, but can be second phase.
A natural extension is to have option settings:
#[trace(recurse=all)]
#[trace(recurse=public)]
#[trace(recurse=private)]
Thoughts?
Just recording reasoning around hierarchy/precedence, more likely to arise when tracing a mod:
#[trace("a", recurse=all)]
pub fn outer() {
defined_later();
defined_later_too();
#trace["b", recurse=false]
fn defined_later_too() {
println!("I'm traced with 'b' options/settings"):
fn defined_lastish(){
println!("I'm traced with 'a' options/settings"):
}
}
fn defined_later() {
println!("I'm traced with 'a' options/settings too"):
}
}
Also, with #[trace("a", recurse=all)] the "a" becomes a prefix added to child trace names, which default to the function name, so names in the above are:
-
a_outer -
b_defined_later_too -
a_defined_last -
a_defined_later
When using #[trace(recurse=all|private|public)], names would be:
-
outer -
defined_later_too -
outer_defined_lastish -
outer_defined_later
Whenever this is implemented... we will need to explicitly document that when recurse=all|private|public then nested #[trace(...)] settings are not cumulative.
Specifically, for example:
#[trace(recurse=all, option_a="some")]
fn f() {
#[trace(option_b="thing")]
fn g(){
fn x() {
// not traced
}
}
fn h(){
fn y(){
// is traced
}
}
}
is not equivalent to the second macro being#[trace(option_a="some", option_b="thing")].
In this case:
-
handywould be traced with settings#[trace(recurse=all, option_a="some")] -
gis traced with settings#[trace(option_b="thing")] -
xis not traced.
I feel uncomfortable adding implicit tracing because:
- tracing has significant overhead, even though minitrace has minimized it as much as possible.
- most functions have no value to be traced
What's the scenario is recurse for?
What's the scenario is recurse for?
Pure convenience when debugging the scenario is you have a bug of unknown provenance in a non-trivial async context. Without such a switch to 'inject' tracing in chunks (per module) or globally, the only alternative is the time consuming and painful process of manually adding them until you discover the root cause.
Not for prod or even dev - unless of course you're disciplined and place all you traceable functions in a single module.
Ack the costs of tracing. As always - Caveat emptor.
I believe incorporating this feature could be beneficial for debugging and should integrate well with minitrace. However, a question arises: isn't this feature sufficiently general to warrant its own crate? For instance, we can recursively annotate an entire module using log_derive, can't we? Consequently, minitrace could potentially leverage this capability at no additional effort!
Closing it for now since it's not entirely related to minitrace.