-
Notifications
You must be signed in to change notification settings - Fork 803
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 initial span interface #31
Conversation
@rochdev @vmarchaud @hekike @bg451 Please review. Also, please file an issue here so that I can add you as a reviewer. |
*/ | ||
addEvent( | ||
name: string, | ||
attributes?: { [key: string]: string | number | boolean } |
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 Object not supported here intentionally?
spec: values may be string, booleans or numeric type.
maybe would be a useful addition like we did for OpenCensus's setAttribute
and automatically JSON.stringify()
objects.
I assume setAttribute
will do the same as spec is must be either a string, a boolean value, or a numeric 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.
Added the object
type.
*/ | ||
addLink( | ||
spanContext: SpanContext, | ||
attributes?: { [key: string]: string | number | boolean } |
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.
Object?
@mayurkale22 Which sponsor should we put on the issue ? I guess the first one is you but for the second one ? |
* @param value the value for this attribute. If the value is a typeof object | ||
* it has to be JSON.stringify-able otherwise it will raise an exception. | ||
*/ | ||
setAttribute(key: string, value: string | number | boolean | object): void; |
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 those setters would return the Span itself (this
), we would provide fluid interfaces, like span.setAttibute().addEvent()
.
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 value
should be of type any
similar to OpenTracing. The idea is that vendors can accept different values in different ways, so this should not be restricted.
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.
There should also be setAttributes
and setEvents
to make it easier to add in bulk.
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 those setters would return the Span itself (
this
), we would provide fluid interfaces, likespan.setAttibute().addEvent()
.
Sure, done.
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.
There should also be
setAttributes
andsetEvents
to make it easier to add in bulk.
Feel free to open an issue to discuss and if goes well, will include in the next iteration of the API.
* Sets an attribute to the span. | ||
* | ||
* @param key the key for this attribute. | ||
* @param value the value for this attribute. If the value is a typeof object |
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 would be problematic for Datadog since we currently accept values that are not JSON serializable and we have a custom serializer that knows how to handle them.
*/ | ||
addEvent( | ||
name: string, | ||
attributes?: { [key: string]: string | number | boolean | object } |
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.
Similar to the above, attributes
values should be of type any
.
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 am fine with changing it to any
type, but will lose type-safety and to mitigate this I have to add TSLint rule flag (tslint:disable-next-line:no-any
). WDYT?
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.
Since there is a limited number of types in JS it would be possible to simply specify all of them, but I think any
is clearer that the intent is to accept anything. I'm personally fine with a TSLint comment. If it happens to much we could simply remove the rule. Code reviews will ensure that there is no abuse.
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.
Ok, done.
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 we use unknown
instead of any
? That allows the same semantics (the passed in value can be any type), but it requires type guards or a cast to actually do something with the type and so is safer from a type checking perspective. As a plus it doesn't require the lint comment. (Most times where you say any
you can usually say unknown
and get the same effect)
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.
Would it make more sense then to include every possible JavaScript types? There are not that many and it would remove the need for casting for common types.
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 we use unknown instead of any?
For now, I have changed to unknown
. If needed, will change to either any
or every possible JavaScript types.
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 unknown
is basically equivalent (maybe exactly equivalent?) to the union type of every possible type. You can use typeof
and instanceof
that would do runtime checks but are also used by the TS compiler to infer a more narrow type at compile time for code in code blocks guarded by those checks. Casts would only be needed if you somehow know what type it is and force it to that, which perhaps something vendor-specific might do.
See the function f20(x: unknown)
example in the in the TS 3.0 release notes for an example of how typeof
and instanceof
can be used for type narrowing by the compiler (and of course checking of the value's actual JS runtime type so it can be correctly serialized).
* Adds a link to the Span. | ||
* | ||
* @param spanContext the context of the linked span. | ||
* @param [attributes] the attributes that will be added; these are |
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.
It's not clear what these attributes are used for.
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 attributes
are the collection of key:value pairs associated with the link to conserve meaningful information related to link.
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 I meant is that this should be better explained in the TypeDoc. In general I feel the documentation of the file doesn't explain much of what the different methods do. Of course, documentation can be improved in later iterations.
* associated with this link. | ||
*/ | ||
addLink( | ||
spanContext: SpanContext, |
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 accept a Span
as well.
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 the type of link determined?
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.
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 you please mention the use case with span ?
In general, I think everywhere a SpanContext
is accepted should also accept a Span
. This avoids the caller having to always call span.context()
if they are referencing a span. I don't have a specific use case but I've had a lot of cases where I was happy that OpenTracing did that for me. This is especially true if the context propagator returns the active span.
* | ||
* @param name the Span name. | ||
*/ | ||
updateName(name: string): void; |
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 would use setName
to be consistent with setAttribute
.
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.
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 decision is to go with updateName
for this feature, so terminology will be aligned across languages. I will add TODO with issue number to consider this later.
* Call to End of a Span MUST not have any effects on child spans. Those may | ||
* still be running and can be ended later. | ||
*/ | ||
end(): void; |
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 accept a timestamp since it's the only way to support some frameworks (i.e. Apollo Tracing).
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.
Makes sense, done.
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.
Shouldn't we change the spec for this?
https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/tracing-api.md#end
There MUST be no parameter.
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 would say yes as the same issue applies to every language. The same will be true also for the ability to set the start time.
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.
* @returns true if this Span is active and recording information like events | ||
* with the AddEvent operation and attributes using setAttributes. | ||
*/ | ||
isRecordingEvents(): boolean; |
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.
From the description it sounds like the fact that it's recording events is only part of the state. Should this be called isActive
instead or something generic?
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 flag is used to indicate whether events should be recorded or not. If it's set, Events
and Attributes
, etc. will be recorded. Otherwise, these events will be dropped. The only way to set this flag when we create the span. If not set, the implementation will provide a default.
/** The ID of the Span. */ | ||
spanId: string; | ||
/** Trace options to propagate. */ | ||
traceOptions?: number; |
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.
It's unclear how this is supposed to be used. What does this number represent?
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.
Addressed in 52e53ab, PTAL
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.
Would it make more sense to have different fields instead of a single bit field? While it requires a little bit more memory, it makes such an interface a lot easier to understand and use.
/** Trace options to propagate. */ | ||
traceOptions?: number; | ||
/** Tracing-system-specific info to propagate. */ | ||
traceState?: string; |
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.
It's unclear what should be the format of this string.
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.
Addressed in 52e53ab, PTAL
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 would be easier to interact with by using an actual object than can then be serialized to a string by some formatter/encoder. Of course this could be done with a getter and a corresponding _traceState
private property to hold the object, but then every vendor will have to replicate this logic.
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.
Makes sense, added TraceState
and TraceOptions
classes with TODO comment.
} | ||
|
||
/** An enumeration of canonical status codes. */ | ||
export enum CanonicalCode { |
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.
Does it make sense to have a fixed enum for this? What if the reason falls outside of the range of values?
Also, should there be a code for exceptions, with the error information attached?
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 will revise after open-telemetry/opentelemetry-specification#59. Currently it is copied from OpenCensus project.
* Do not return `this`. The Span generally should not be used after it | ||
* is ended so chaining is not desired in this context. | ||
* | ||
* @param [endTime] the timestamp to set as Span's end time. If not provided, |
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 you specify how the timestamp is formatted? Are we going to use epoch millis?
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 you don't mind, I will update once we have some conclusion here #19
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.
Sounds good. Could you add a TODO comment that points to that issue though?
* propagated along side of a distributed context. | ||
*/ | ||
export interface SpanContext { | ||
/** The ID of the trace that this span belongs to. */ |
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.
Should this specify the format? Hex-encoded 128-bit number? Same for spanId.
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.
Done 424c9fd
@hekike @rochdev @danielkhan @draffensperger I tried to address all the comments, please take a look again. I think this is just first iteration will update/add moving forward. |
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.
Gave a few more comments but LGTM overall.
* Do not return `this`. The Span generally should not be used after it | ||
* is ended so chaining is not desired in this context. | ||
* | ||
* @param [endTime] the timestamp to set as Span's end time. If not provided, |
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.
Sounds good. Could you add a TODO comment that points to that issue though?
* sampled or not. | ||
* SAMPLED_VALUE = 0x1 and NOT_SAMPLED_VALUE = 0x0; | ||
*/ | ||
traceOptions?: TraceOptions; |
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 this just be a number
instead of a TraceOptions
structure? Seems like the comment implies that.
* Multiple tracing systems (with different formatting): | ||
* tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE | ||
*/ | ||
traceState?: TraceState; |
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.
Seems that the comment about is describing this as a string, but the type seems to imply something more complex - is this meant to become the parsed trace state?
I'm OK with leaving this as-is though since the structure is marked as TODO anyway.
* limitations under the License. | ||
*/ | ||
|
||
export class TraceOptions { |
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.
Should the file name be something like trace_options.ts
to match this?
Also, should this be an interface instead?
* limitations under the License. | ||
*/ | ||
|
||
export class TraceState { |
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.
Similar comments here: should the file be trace_state.ts
, and should this be an interface? (I realize this is TODO, but wanted to comment anyway on this since this is the types
package)
I think I addressed all of the comments by updating the PR, adding TODOs, or opening issues. Merging this PR now. Thanks for the reviews @danielkhan @rochdev @hekike @draffensperger |
Completes #29
This is based on the specs: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/tracing-api.md#span
This PR contains following interfaces: