-
Notifications
You must be signed in to change notification settings - Fork 48
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
Feature: minitrace::Span::current() #117
Comments
Long story short, when you want to spawn threads, or record within a function/span, the ergonomics are not as great as they might be. Ideally, one would like to land at this, assuming the user has adopted the Cargo features convention (or whatever they like): #[cfg(feature = "debug")]
fn a(s: String){
#[cfg(feature="traceable")]
span= Span::current();
#[cfg(feature="info")]
span.record("some.useful", "information");
#[cfg(feature="debug")]
span.record("some.useful.data", timestamp);
...
} |
According to my understanding of your example, your example is trying to filter the spans by static feature flag of logging levels ( If my understanding is right, // record property by condition
let _g = LocalSpan::enter_with_local_parent();
#[cfg(feature="debug")]
_g.add_property("some.useful", "information"); // record span by condition
fn main() {
let root = Span::root();
let _g = root.set_local_parent();
do_stuff();
}
fn do_stuff() {
#[cfg(feature="debug")]
let _g = LocalSpan::enter_with_local_parent();
} // record span across threads by condition
fn main() {
let root = Span::root();
#[cfg(feature="debug")]
let span = Span::enter_with_parent("spawn", &root);
std::thread::spawn(move || {
#[cfg(feature="debug")]
let _g = span.set_local_parent();
do_stuff()
});
}
fn do_stuff() {
#[cfg(feature="debug")]
let _g = LocalSpan::enter_with_local_parent();
} |
Indeed, too many // record property by condition
let _g = LocalSpan::enter_with_local_parent();
_g.add_property::<DEBUG>::("some.useful", "information"); // record span by condition
fn main() {
let root = Span::root();
let _g = root.set_local_parent();
do_stuff();
}
fn do_stuff() {
let _g = LocalSpan::<DEBUG>::enter_with_local_parent();
} // record span across threads by condition
fn main() {
let root = Span::root();
let span = Span::enter_with_parent::<DEBUG>::("spawn", &root);
std::thread::spawn(move || {
let _g = span.set_local_parent();
do_stuff()
});
}
fn do_stuff() {
let _g = LocalSpan::enter_with_local_parent::<DEBUG>::();
} and then specify the logging level in minitrace = { version = "0.4", features = [ "debug" ] } |
Thanks for the feedback. Initially I'd also thought about Also, we are in the Rust community, and the "empowering everyone" goal resonates with me. Generics, etc in user facing code makes it look more complicated than, IMO, it needs to be. One of the reasons I'm switching from Tracing is because minitrace is so elegant/simple in addition to the performance (simplicity/elegance, I think, is related to maintaining that performance). I was able to better trace some code in a couple of hours than I'd been able to after days of reading and wrestling with First the unnecessary: Next the out of scope: Is related to the last point - how a user names their levels, and even how many they have is out out scope of mintrace. Leave users free do do what they need. If levels: In terms of ergonomics, given the power and flexibility using Cargo provides I don't think this is too high a price to pay: #[cfg(features = "verbose")]
span.record(...) Compared to being told. no we won't implement LocalSpan::enter_with_local_parent::<ALLTHETHINGS>::(); Hopefully I haven't mis-understood. But I think "separation of concerns" suggest how I name them and how many levels of trace output I need is not relevant to producing traceoutput in the most efficient way possible. If I want to be able to compile-out all tracing, I have to use I need to think a little more about the examples you've given. I'm leaning towards closing this or changing to a documentation/examples issue. |
OK, I think I understand things a little better. Parking workarounds, and levels, because neither is strictly the subject of this feature request - I'll open a separate issue for level functionality. I'll take your three use cases as a starting point - thanks for drawing them up, they are useful. I'll open a separate issue to address ergonomics of managing spans, and I'll address the three use-case you identify there. IIUC, you accept there is a need for |
My only question is: what will the user do with the span returned from |
The use case is: The span is created by Example: #[cfg_attr(feature = "debug", trace("example"))]
fn a(s: String){
#[cfg(feature="debug")]
{
span= Span::current();
span.record("some.useful", "information");
}
...
} |
Apologies, @andylokandy I overlooked this:
The use case is to avoid spawning a new span. Rather, it returns the span created by |
Considering property recording, we may be able to solve it in another way: #[cfg_attr(feature = "debug", trace("example"))]
fn a(s: String){
#[cfg(feature="debug")]
minitrace::event("some.useful", "information"); // a new API which, internally, adds property to the local parent
...
} |
Without knowing the impl details I assume the performance impact/cost of implementing One idea that might eliminate any performance impact is to add a parameter to the proc-macro-attribute: #[trace("my-trace", recorder=my_spanr)]
fn a(s: String){
my_spanr.record("key","value");
} Thoughts? If so, I would amend the feature to be a change to the proc-macro-attribute - this is the only use case I am aware of. |
Currently, And my concern of |
I am not sure whether introducing a local variable to the generated span is a good design. @zhongzc What do you think? |
My reservation with I could be wrong but I think the OTel specs would need to be checked. Per my previous comments, possibly in another issue, so I'll note them here: |
Ack the concern, that did make me think a little harder, and you'd probably want to actually do this: #[trace("this-span", recorder_target = "other-span", recorder=my_spanr)]
fn a(s: String){
my_spanr.record("key","value")
}
//OR
#[trace("this-span", recorder_target = "this-span", recorder=my_spanr)]
fn a(s: String){
my_spanr.record("key","value")
} where |
@andylokandy not sure I understand your thread workaround example here. As best I can tell it is not possible to move the |
Do you want this: fn main() {
let root = Span::root();
let span = Span::enter_with_parent("spawn", &root);
std::thread::spawn(move || {
let _g = span.set_local_parent();
do_stuff()
});
}
fn do_stuff() {
let span = enter_with_local_parent("spawn in spwan");
std::thread::spawn(move || {
let _g = span.set_local_parent();
do_stuff2();
....
});
} |
|
I can't find clear documentation or examples showing the following Tracing functionality is possible in minitrace.
Specifically, in Tracing the
tracing::Span::current()
involves a thread local lookup, so you only want to do this once for all therecord(...)
actions within the function. Hence, you end up with something like the following:In case it helps anyone else coming down this path...
As best I can tell, the minitrace workaround for not having
minitrace::Span::current()
is to use Cargo features and this function signature "trick" to pass the last parentspan
around.This workaround is required, as best I know, when:
The feature of this workaround is that it becomes redundant to use the
#[trace(...)]
attribute, because we have to create a child of the parent span that is passed in via the 'trick'Where the Cargo features in your crate are:
In fact thinking about it some more.... I'm beginning to wonder if this "workaround" is not where the end state might be?
I believe it will come down to the relative 'cost/expense' of
mintrace::Span::current()
compared toSpan::enter_with_parent("A child span", &span);
Hopefully,
mintrace::Span::current()
is feasible, because the workaround does end up creating a child span just for the record actions.Hopefully that makes sense?
The text was updated successfully, but these errors were encountered: