-
Notifications
You must be signed in to change notification settings - Fork 36
Inferred Spans #254
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
Inferred Spans #254
Conversation
…method. Refactor to satisfy law of demeter.
…PI-like inferred spans
… ending all inferred spans in invocationDone by memoizing lambda span and using its start time for the end time of async inferred spans
…nd return headers. Set inferred span attrs so service names are not overwritten. Update tests, remove dead code
…an for snssqs events. Specs fo snssqs events
Codecov Report
@@ Coverage Diff @@
## main #254 +/- ##
==========================================
- Coverage 83.56% 82.26% -1.31%
==========================================
Files 35 37 +2
Lines 1339 1618 +279
Branches 284 349 +65
==========================================
+ Hits 1119 1331 +212
- Misses 196 238 +42
- Partials 24 49 +25
Continue to review full report at Codecov.
|
@@ -238,6 +238,89 @@ export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined | |||
return; | |||
} | |||
|
|||
export function readTraceFromSNSSQSEvent(event: SQSEvent): TraceContext | undefined { |
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.
Out of curiosity, in dd-lambda-js do we have multiple readTraceFrom...Event
functions that are specifically tailored for each aws managed service?
If so then why do we have a readTraceFromSQSEvent function and readTraceFromSNSSQSEvent (line 241)?
Is an SQS event and SNSSQS event different? Therefore needing two separate functions?
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 they are a bit different, basically the SNSSQS event is not batched, but a typical SNS event has an array of Records
with each SNS message inside it. I think we could probably refactor this though, going to take a closer look
export function readTraceFromEventbridgeEvent(event: EventBridgeEvent<any, any>): TraceContext | undefined { | ||
if (event.detail && event.detail._datadog) { | ||
try { | ||
const traceData = event.detail._datadog; |
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 we anticipate having a _datadog
key in the event.detail
. The way it was implemented in dd-trace-py
, we just have x-datadog-trace-id
, etc. We could change it either way, but it's important we're consistent.
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 see, I'd been following the convention for sns/sqs where we stick a _datadog
string map in the message attributes, but I can change it to this. But yes, we need stay consistent here.
src/trace/context.ts
Outdated
if (isSNSEvent(event)) { | ||
return readTraceFromSNSEvent(event); | ||
} | ||
// TODO [astuyve] SNSSQS must be before SQS |
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.
You might want to move isSNSSQSEvent in this case
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 if tests hit this section of code? It might be important to ensure we get it right and keep getting it right in the future.
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'll add better tests for this to ensure it's hit.
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 should remove this TODO
since it looks like isSNSSQSEvent
is being hit before isSQSEvent
and just leave the comment.
…ontaining trace context
} from "./utils"; | ||
|
||
export { TraceHeaders } from "./trace"; | ||
|
||
export const apiKeyEnvVar = "DD_API_KEY"; | ||
export const apiKeyKMSEnvVar = "DD_KMS_API_KEY"; | ||
export const captureLambdaPayloadEnvVar = "DD_CAPTURE_LAMBDA_PAYLOAD"; | ||
export const traceManagedServicesEnvVar = "DD_TRACE_MANAGED_SERVICES"; |
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 update the README with some information about this env var.
parsedBody.MessageAttributes && | ||
parsedBody.MessageAttributes._datadog && | ||
parsedBody.MessageAttributes._datadog.Value |
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: conditions like these can be simplified to: parsedBody?.MessageAttributes?._datadog?.Value
src/trace/span-inferrer.spec.ts
Outdated
const sqsEvent = require("../../event_samples/sqs.json"); | ||
const ddbEvent = require("../../event_samples/dynamodb.json"); | ||
const kinesisEvent = require("../../event_samples/kinesis.json"); | ||
const eventBridgeEvent = require("../../event_samples/event-bridge.json"); |
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: can we rename this to eventbridge
, the -
prevents it from showing up in search.
src/trace/context.ts
Outdated
if (typeof traceID !== "string") { | ||
return; | ||
} | ||
const parentID = traceData[parentIDHeader]; | ||
if (typeof parentID !== "string") { | ||
return; | ||
} | ||
const sampledHeader = traceData[samplingPriorityHeader]; | ||
if (typeof sampledHeader !== "string") { | ||
return; | ||
} |
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: can we do a single type check for all three variables instead of three separate conditions?
src/trace/listener.ts
Outdated
// TODO [astuyve] discuss with team RE nulls or undefined, or a coalescing strategy | ||
parentSpanContext = rootTraceHeaders ? this.tracerWrapper.extract(rootTraceHeaders) || undefined : undefined; |
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.
// TODO [astuyve] discuss with team RE nulls or undefined, or a coalescing strategy | |
parentSpanContext = rootTraceHeaders ? this.tracerWrapper.extract(rootTraceHeaders) || undefined : undefined; | |
parentSpanContext = rootTraceHeaders ? this.tracerWrapper.extract(rootTraceHeaders) ?? undefined : undefined; |
src/trace/span-inferrer.ts
Outdated
"span.type": "http", | ||
"resource.name": resourceName, | ||
"service.name": domain, | ||
api_id: event.requestContext.apiId, |
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 rename this to apiid
? This is done since the metric dimension is apiid
and we want to have an attribute that's the same name as the metric tag. This ensure that the attribute is there when pivoting from a metric to a trace query.
api_id: event.requestContext.apiId, | |
apiid: event.requestContext.apiId, | |
apiname: event.requestContext.apiId, |
src/trace/span-inferrer.ts
Outdated
const resourceName = `${eventName} ${tableName}`; | ||
options.tags = { | ||
operation_name: "aws.dynamodb", | ||
"aws.dynamodb.table_name": tableName, |
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.
"aws.dynamodb.table_name": tableName, | |
"tablename": tableName, |
src/trace/span-inferrer.ts
Outdated
type: Type, | ||
subject: Subject, | ||
message_id: MessageId, | ||
topic_name: topicName, |
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.
topic_name: topicName, | |
topicname: topicName, |
src/trace/span-inferrer.ts
Outdated
type: Type, | ||
subject: Subject, | ||
message_id: MessageId, | ||
topic_name: topicName, |
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.
topic_name: topicName, | |
topicname: topicName, |
… span attribute names for topicname, tablename, etc.
src/trace/context.ts
Outdated
@@ -238,6 +237,136 @@ export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined | |||
return; | |||
} | |||
|
|||
export function readTraceFromSNSSQSEvent(event: SQSEvent): TraceContext | undefined { | |||
if (event?.Records[0]?.body) { |
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 very unlikely that Records
here will be null
, but the proper syntax is the weird looking event?.Records?.[0]?.body
.
const testEvent1 = {Records: [{body: "Hello"}]}
console.log("testEvent1?.Records?.[0]?.body:", testEvent1?.Records?.[0]?.body)
console.log("testEvent1?.Records[0]?.body:", testEvent1?.Records[0]?.body)
const testEvent2 = {Records: []}
console.log("testEvent2?.Records?.[0]?.body:", testEvent2?.Records?.[0]?.body)
console.log("testEvent2?.Records[0]?.body:", testEvent2?.Records[0]?.body)
const testEvent3 = {Records: null}
console.log("testEvent3?.Records?.[0]?.body:", testEvent3?.Records?.[0]?.body)
console.log("testEvent3?.Records[0]?.body:", testEvent3?.Records[0]?.body)
/usr/local/lib/node_modules/ts-node-fm/src/index.ts:226
testEvent1?.Records?.[0]?.body: Hello
testEvent1?.Records[0]?.body: Hello
testEvent2?.Records?.[0]?.body: undefined
testEvent2?.Records[0]?.body: undefined
testEvent3?.Records?.[0]?.body: undefined
TypeError: Cannot read property '0' of null
at index.ts:10:126
at Script.runInThisContext (vm.js:122:20)
at startRepl (/usr/local/lib/node_modules/ts-node-fm/src/bin.ts:157:12)
at Object.<anonymous> (/usr/local/lib/node_modules/ts-node-fm/src/bin.ts:66:1)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
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... But it's ugly. I'll use it :)
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 does this PR do?
Creates inferred spans for:
Motivation
Testing Guidelines
Additional Notes
Types of Changes
Check all that apply