-
Notifications
You must be signed in to change notification settings - Fork 853
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
basic-tracer initial implementation #99
basic-tracer initial implementation #99
Conversation
My GitHub is linked to a CNCF account I don't have anymore, so fixing the CLA will take some time :P |
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 a good start, added a few comments in the first iteration.
export class Tracer implements types.Tracer { | ||
static INVALID_ID = '0'; | ||
|
||
private logger!: types.Logger; |
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.
Given that this is initialized in the constructor, can we remove the !
here?
@mayurkale22 @draffensperger thanks for the review. I addressed your comments. |
fn: T | ||
): ReturnType<T> { | ||
if (!this.scopeManager) { | ||
this.logger.warn('withSpan(...) has no effect without a scopeManager'); |
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 don't think there should be a warning for this as it's a perfectly valid use case, especially if you use the low level construct.
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.
My thinking was that base tracer has no ScopeManager
by default so calling ScopeManager related functions like withSpan
is not valid. As it won't do what you would expect. To suppress the warning the user can pass a NoopScopeManager
. I think it's okay as in the automatic tracer we will ship OT with CLSScopeManager
and ZoneScopeManager
, so this warning is really for people who try to do something custom, and I believe for them it's quite useful.
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.
Part of what I'm hoping for from the browser side is code simplicity (as in fewer total bytes of code shipped to the client), so if we can find a way to avoid null checks and warning messages by designing the interface a bit differently I'd prefer that.
What if we had something like new NoopScopeManager({warn: true})
by default that would write warning logs any time someone attempts to call one of its methods? That way in the browser, where we use something like a GlobalWindowScopeManager
the code for the if statements and warning logs can get tree shaken out (at least in principle).
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.
@hekike
Since NoopScopeManager
will have the same behavior as the current one (just calling fn
, it almost cost nothing to use it and avoid null check everywhere. If you think warning user is important then a option like @draffensperger suggested would be the way to go for me.
this.logger.warn( | ||
'getCurrentSpan() returns an invalid default span without a scopeManager' | ||
); | ||
return BasicTracer.defaultSpan; |
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 think this should be null
. In the noop it makes sense to always return a span so that nothing breaks when you disable the tracer, but for the base implementation null
makes more sense to indicate the absence of a span on the scope.
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.
What is the condition where we won't have a scope manager set? If we require that in the constructor, can we just delegate the default span behavior to the scope manager?
I would prefer to avoid returning null
because then the TS type needs to be types.Span|null
, which is annoying for other TypeScript code to consume because it requires null checks. And the null
type could also be confusing to end users who would expect it to return non-null.
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.
See my previous comment (#99 (comment)) about the default scope manager.
Looks like there are two opinions here: return null, log warning. Both can work for getCurrentSpan()
and although I like the idea of null
to indicate you really have no ScopeManager, but the same strict check concept is not applicable for withSpan
so I'd vote for warning log. I guess throwing in withSpan
without ScopeManager would be quite abusive.
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.
Added to SIG agenda.
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.
Actually i think it's also applicable to withSpan
when using the NoopScopeManager
, it will just always return a null
active scope, which is fine (at least for me).
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 have users that explicitly don't want scope management, and they definitely don't want a warning because of it.
Do you know what would be a use-case that you don't want scope management but you still want to call withSpan
and getCurrentSpan
however you know they don't do anything?
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.
(the default null without warning for getCurrentSpan
would make sense for me as it shows well that you cannot use this feature without scope manager, although the withSpan
would just silently swallow this fact, I don't think you can enforce it on API level, except if you throw)
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.
Do you know what would be a use-case that you don't want scope management but you still want to call withSpan and getCurrentSpan however you know they don't do anything?
The auto instrumentation will call these methods.
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.
Isn't ScopeManager a hard requirement for auto instrumentation? Otherwise, you don't have a root span. How would it 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.
That's a good point. Hopefully it will be possible to have at least some level of auto instrumentation even without a scope manager, but it would definitely be challenging. How would the auto instrumentation detect the presence of a proper scope manager?
|
||
// make sampling decision | ||
if (!this.sampler.shouldSample(parentSpanContext)) { | ||
return BasicTracer.defaultSpan; |
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 will not propagate the trace parent and trace state properly.
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 think even if sampling decision is false
, we should assign correct SpanContext
for DefaultSpan/NoopSpan
? @rochdev this is what you meant, right?
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 mean that we shouldn't return a single global instance, otherwise it will not be possible to attach the IDs on it for propagation.
// parent is a SpanContext | ||
if ( | ||
options.parent && | ||
typeof (options.parent as types.SpanContext).traceId |
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.
How is this converted to JavaScript? It doesn't look like this check is JS compatible. Same for other occurrences of as
.
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.
My understanding is that it's attempting to check whether the options.parent
has a traceId
field. Is that correct?
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.
as
is just used to force the type of a variable, in this case it would be transpiled to if (options.parent && options.parent.traceId) {}
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.
Can we remove the typeof
then?
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.
could someone show me an example of how would it work with TS?
I get a Type error without typeof
, instanceof
also doesn't 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.
What error do you get if you remove the typeof
?
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.
Okay, I see now. The typeof
can be removed, the as
not.
*/ | ||
getCurrentSpan(): types.Span { | ||
// Return with defaultSpan if no scope manager provided. | ||
if (!this.scopeManager) { |
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.
In what case could this.scopeManager
be falsey? Isn't it set in the constructor? Or is this attempting to guard against someone changing it to undefined after the initial tracer construction?
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 think once we set to default no-op ScopeManager in constructor (this.scopeManager = config.scopeManager || new NoopScopeManager()
), this check can be removed.
// parent is a SpanContext | ||
if ( | ||
options.parent && | ||
typeof (options.parent as types.SpanContext).traceId |
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.
My understanding is that it's attempting to check whether the options.parent
has a traceId
field. Is that correct?
this.logger.warn( | ||
'getCurrentSpan() returns an invalid default span without a scopeManager' | ||
); | ||
return BasicTracer.defaultSpan; |
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.
What is the condition where we won't have a scope manager set? If we require that in the constructor, can we just delegate the default span behavior to the scope manager?
I would prefer to avoid returning null
because then the TS type needs to be types.Span|null
, which is annoying for other TypeScript code to consume because it requires null checks. And the null
type could also be confusing to end users who would expect it to return non-null.
Codecov Report
@@ Coverage Diff @@
## master #99 +/- ##
=======================================
Coverage 98.74% 98.74%
=======================================
Files 29 29
Lines 1915 1915
Branches 221 221
=======================================
Hits 1891 1891
Misses 24 24
|
b91c1c9
to
e01a1e7
Compare
6d80a3d
to
5900edf
Compare
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.
Overall looks good to me, added few last comments.
|
||
// make sampling decision | ||
if (!this.sampler.shouldSample(parentSpanContext)) { | ||
return BasicTracer.defaultSpan; |
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 think even if sampling decision is false
, we should assign correct SpanContext
for DefaultSpan/NoopSpan
? @rochdev this is what you meant, right?
@mayurkale22 so we would only use |
This is my take, feel free to suggest if anything is wrong :) Current Span types: Problem:
Solution 1: Solution 2: Solution 3: |
I would prefer Solution 1 where |
Should we wait for this decision or should we merge this PR and fix later with a todo comment? |
ea92a32
to
a8b7da6
Compare
@mayurkale22 I addressed all the comments (or added a todo) and rebased with the master. I think it's good to go. |
It is challenging to build a minimal tracer that makes sense without propagation and exporter interfaces. Here is my initial take on it.