-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[Event Hubs] add tracing support when sending events #5207
Conversation
e0ee841
to
d39c136
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.
I really like this PR, just a few different thoughts
* @param traceParent Serialized span context data as a `traceparent` header value. | ||
* @returns The `SpanContext` generated from the `traceparent` value. | ||
*/ | ||
export function extractSpanContextFromTraceParent(traceParent: string): 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.
nit: I think spanContextFromTraceParentHeader
might be a slightly better name
@@ -0,0 +1,26 @@ | |||
import { SpanContext } from '../interfaces/span_context'; |
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.
not to dump more work on you, but I feel like this function would be a nice/easy thing to add a couple unit tests 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.
Oh, I plan to! This PR is still in draft so not what I would consider ship-able yet!
* @param spanContext Contains context for a specific span. | ||
* @returns The `spanContext` represented as a `traceparent` value. | ||
*/ | ||
export function getTraceParent(spanContext: SpanContext): 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.
getTraceParentHeader
?
*/ | ||
export function getTraceParent(spanContext: SpanContext): string { | ||
if (!spanContext.traceId || !spanContext.spanId) { | ||
throw new Error(`Missing required fields "traceId" or "spanId" from 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.
I know it's tedious, but can you break this into two separate warnings? That way if this fails in production someone can look at the logs and know which field was missing
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.
Related to another comment, what do you think about returning null if these checks fail, and logging the errors instead of throwing? That fits better with the overall philosophy that turning on tracing shouldn't break user's applications.
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.
Love it
@@ -0,0 +1,21 @@ | |||
import { SpanContext } from "../interfaces/span_context"; |
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 we combine these two utility functions into one ts file? I'm not sure how the other libs organize themselves
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.
otherwise it feels like these functions should be the default export...
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 I'd prefer to combine them. I became wary of default exports as I went back and forth between commonjs and es2015 imports, but I think our packages handle those differences today without friction...
It's also a personal habit of mine to give exports their own file (rollup flattens them in the build artifact so no performance penalty) but I can adjust if others find that annoying!
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'm not totally opposed to the practice, it just felt like overkill, but maybe I go too much the other way (put everything in one module and break that out later)
|
||
export const TRACEPARENT_PROPERTY = "Diagnostic_Id"; | ||
|
||
export function instrumentEventData(eventData: EventData, span: Span): 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.
I'm curious, what is the success boolean used for?
} | ||
|
||
try { | ||
const traceParent = getTraceParent(span.context()); |
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 wonder if we should consider a way of parsing that doesn't throw, like returning null or some other sentinel value, since exceptions-as-control-flow is a little more expensive.
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 agree, and think null or undefined would be good choices. I think instead of throwing an error, the getTraceParent
method could log instead (I don't think logging has been added to core-tracing yet but it should eventually).
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.
Anders would say to use undefined
const messageSpan = createMessageSpan(options.parentSpan); | ||
// Create a shallow copy of eventData and eventData.properties in case we add the diagnostic id to the properties. | ||
eventData = {...eventData, properties: {...eventData.properties}}; | ||
isInstrumented = instrumentEventData(eventData, messageSpan); |
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 we didn't want instrumentEventData to mutate the object, we could just treat its input as readonly and do the object copy trick inside it and return the new version, right?
It just feels odd to have a method that mutates a passed-in argument if the only user of that method doesn't want that behavior
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.
Yeah, I originally had that but then came across a situation where I also needed to know if the event was instrumented by the method. I didn't want to make the return type return the boolean and the event data, but maybe I can tackle the isInstrumented
check another way.
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, I remember now. I wanted to know if an event being added to a batch via tryAdd
was instrumented by instrumentEventData
, or if it was already instrumented before being added. I can be smarter about handling this in tryAdd
and have instrumentEventData
go back to returning a new copy of EventData.
0a0a871
to
e6fd203
Compare
I think I've addressed all the feedback. Waiting on #5283 so I can use the TestTracer to write tests for the event-hubs changes. |
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.
Looking real good! Happy to get this landed soon, sprinkling a few more comments for now.
} | ||
|
||
|
||
const VERSION = "00"; |
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 this be at the top of the file?
} | ||
|
||
const flags = spanContext.traceFlags || TraceFlags.UNSAMPLED; | ||
const traceFlags = (flags < 10) ? `0${flags.toString(16)}` : flags.toString(16); |
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.
maybe encode the flags at hex in the previous step to save some .toString(16)
in this line?
sdk/core/core-tracing/lib/wrappers/opencensus/openCensusSpanWrapper.ts
Outdated
Show resolved
Hide resolved
|
||
const traceParent = getTraceParentHeader(span.context()); | ||
if (traceParent) { | ||
eventData.properties![TRACEPARENT_PROPERTY] = traceParent; |
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.
feels like good minor feedback to the TS team that we've implicitly initialized this above... 😉
f3cf6a8
to
789dab6
Compare
/azp run js - eventhubs-client - tests |
Azure Pipelines successfully started running 1 pipeline(s). |
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.
Looks good! 👍
I really like that you used chai for your test assertions, I think I'd like to see us standardize around that in other packages as well. ☕️
Left a few last comments for the road. ☄
* The set of options to configure the behavior of `tryAdd`. | ||
* - `parentSpan` : The `Span` or `SpanContext` to use as the `parent` of the span created while calling this operation. | ||
*/ | ||
export interface TryAddOptions { |
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 same interface is defined in messageSpan.ts
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 does not appear to be so.
* The set of options to manually propagate `Span` context for distributed tracing. | ||
* - `parentSpan` : The `Span` or `SpanContext` for the operation to use as a `parent` when creating its own span. | ||
*/ | ||
export interface ParentSpanOptions { |
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 also looks familiar... maybe there are some common interfaces that need to be pulled out?
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.
Yeah, maybe this can be pulled into core-tracing. I think core-http has something similar on RequestOptions, but this library doesn't depend on that so I duplicated it here.
|
||
tracer.getSpanGraph(rootSpan.context().traceId).should.eql(expectedGraph); | ||
tracer.getActiveSpans().length.should.equal(0, "All spans should have had end called."); | ||
}); | ||
}); | ||
}).timeout(60000); |
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.
kinda curious why the timeout is so big. I feel like we have a few tests that need to be sped up through mocking (though don't worry about this for this PR)
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.
Yes, this is because currently all the tests are live tests. I'll be taking a look at mocking in the near future :)
Relates to #5180
This PR is a WIP that adds support for tracing send requests. I need some clarification on setting the
send
span statuses, specifically when an error is encountered.Also waiting on
#5182#5233#5283 to be merged as there are some API changes made there that will impact this PR.