dd-trace 3.49.0 → 3.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,7 @@ class Kinesis extends BaseAwsSdkPlugin {
53
53
 
54
54
  // extract DSM context after as we might not have a parent-child but may have a DSM context
55
55
  this.responseExtractDSMContext(
56
- request.operation, response, span ?? null, streamName
56
+ request.operation, response, span || null, streamName
57
57
  )
58
58
  }
59
59
  })
@@ -143,62 +143,72 @@ class Kinesis extends BaseAwsSdkPlugin {
143
143
  }
144
144
 
145
145
  requestInject (span, request) {
146
- const operation = request.operation
147
- if (operation === 'putRecord' || operation === 'putRecords') {
148
- if (!request.params) {
149
- return
150
- }
151
- const traceData = {}
152
-
153
- // inject data with DD context
154
- this.tracer.inject(span, 'text_map', traceData)
155
- let injectPath
156
- if (request.params.Records && request.params.Records.length > 0) {
157
- injectPath = request.params.Records[0]
158
- } else if (request.params.Data) {
159
- injectPath = request.params
160
- } else {
161
- log.error('No valid payload passed, unable to pass trace context')
146
+ const { operation, params } = request
147
+ if (!params) return
148
+
149
+ let stream
150
+ switch (operation) {
151
+ case 'putRecord':
152
+ stream = params.StreamArn ? params.StreamArn : (params.StreamName ? params.StreamName : '')
153
+ this.injectToMessage(span, params, stream, true)
154
+ break
155
+ case 'putRecords':
156
+ stream = params.StreamArn ? params.StreamArn : (params.StreamName ? params.StreamName : '')
157
+ for (let i = 0; i < params.Records.length; i++) {
158
+ this.injectToMessage(span, params.Records[i], stream, i === 0)
159
+ }
160
+ }
161
+ }
162
+
163
+ injectToMessage (span, params, stream, injectTraceContext) {
164
+ if (!params) {
165
+ return
166
+ }
167
+
168
+ let parsedData
169
+ if (injectTraceContext || this.config.dsmEnabled) {
170
+ parsedData = this._tryParse(params.Data)
171
+ if (!parsedData) {
172
+ log.error('Unable to parse payload, unable to pass trace context or set DSM checkpoint (if enabled)')
162
173
  return
163
174
  }
175
+ }
164
176
 
165
- const parsedData = this._tryParse(injectPath.Data)
166
- if (parsedData) {
167
- parsedData._datadog = traceData
168
-
169
- // set DSM hash if enabled
170
- if (this.config.dsmEnabled) {
171
- // get payload size of request data
172
- const payloadSize = Buffer.from(JSON.stringify(parsedData)).byteLength
173
- let stream
174
- // users can optionally use either stream name or stream arn
175
- if (request.params && request.params.StreamArn) {
176
- stream = request.params.StreamArn
177
- } else if (request.params && request.params.StreamName) {
178
- stream = request.params.StreamName
179
- }
180
- const dataStreamsContext = this.tracer
181
- .setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
182
- if (dataStreamsContext) {
183
- const pathwayCtx = encodePathwayContext(dataStreamsContext)
184
- parsedData._datadog[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
185
- }
186
- }
177
+ const ddInfo = {}
178
+ // for now, we only want to inject to the first message, this may change for batches in the future
179
+ if (injectTraceContext) { this.tracer.inject(span, 'text_map', ddInfo) }
180
+
181
+ // set DSM hash if enabled
182
+ if (this.config.dsmEnabled) {
183
+ parsedData._datadog = ddInfo
184
+ const dataStreamsContext = this.setDSMCheckpoint(span, parsedData, stream)
185
+ if (dataStreamsContext) {
186
+ const pathwayCtx = encodePathwayContext(dataStreamsContext)
187
+ ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
188
+ }
189
+ }
187
190
 
188
- const finalData = Buffer.from(JSON.stringify(parsedData))
189
- const byteSize = finalData.length
190
- // Kinesis max payload size is 1MB
191
- // So we must ensure adding DD context won't go over that (512b is an estimate)
192
- if (byteSize >= 1048576) {
193
- log.info('Payload size too large to pass context')
194
- return
195
- }
196
- injectPath.Data = finalData
197
- } else {
198
- log.error('Unable to parse payload, unable to pass trace context')
191
+ if (Object.keys(ddInfo).length !== 0) {
192
+ parsedData._datadog = ddInfo
193
+ const finalData = Buffer.from(JSON.stringify(parsedData))
194
+ const byteSize = finalData.length
195
+ // Kinesis max payload size is 1MB
196
+ // So we must ensure adding DD context won't go over that (512b is an estimate)
197
+ if (byteSize >= 1048576) {
198
+ log.info('Payload size too large to pass context')
199
+ return
199
200
  }
201
+ params.Data = finalData
200
202
  }
201
203
  }
204
+
205
+ setDSMCheckpoint (span, parsedData, stream) {
206
+ // get payload size of request data
207
+ const payloadSize = Buffer.from(JSON.stringify(parsedData)).byteLength
208
+ const dataStreamsContext = this.tracer
209
+ .setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
210
+ return dataStreamsContext
211
+ }
202
212
  }
203
213
 
204
214
  module.exports = Kinesis
@@ -55,17 +55,17 @@ class Sns extends BaseAwsSdkPlugin {
55
55
 
56
56
  switch (operation) {
57
57
  case 'publish':
58
- this._injectMessageAttributes(span, params)
58
+ this.injectToMessage(span, params, params.TopicArn, true)
59
59
  break
60
60
  case 'publishBatch':
61
- if (params.PublishBatchRequestEntries && params.PublishBatchRequestEntries.length > 0) {
62
- this._injectMessageAttributes(span, params.PublishBatchRequestEntries[0])
61
+ for (let i = 0; i < params.PublishBatchRequestEntries.length; i++) {
62
+ this.injectToMessage(span, params.PublishBatchRequestEntries[i], params.TopicArn, i === 0)
63
63
  }
64
64
  break
65
65
  }
66
66
  }
67
67
 
68
- _injectMessageAttributes (span, params) {
68
+ injectToMessage (span, params, topicArn, injectTraceContext) {
69
69
  if (!params.MessageAttributes) {
70
70
  params.MessageAttributes = {}
71
71
  }
@@ -73,27 +73,50 @@ class Sns extends BaseAwsSdkPlugin {
73
73
  log.info('Message attributes full, skipping trace context injection')
74
74
  return
75
75
  }
76
+
76
77
  const ddInfo = {}
77
- this.tracer.inject(span, 'text_map', ddInfo)
78
- // add ddInfo before checking DSM so we can include DD attributes in payload size
79
- params.MessageAttributes._datadog = {
80
- DataType: 'Binary',
81
- BinaryValue: ddInfo
78
+ // for now, we only want to inject to the first message, this may change for batches in the future
79
+ if (injectTraceContext) {
80
+ this.tracer.inject(span, 'text_map', ddInfo)
81
+ // add ddInfo before checking DSM so we can include DD attributes in payload size
82
+ params.MessageAttributes._datadog = {
83
+ DataType: 'Binary',
84
+ BinaryValue: ddInfo
85
+ }
82
86
  }
87
+
83
88
  if (this.config.dsmEnabled) {
84
- const payloadSize = getHeadersSize({
85
- Message: params.Message,
86
- MessageAttributes: params.MessageAttributes
87
- })
88
- const dataStreamsContext = this.tracer
89
- .setCheckpoint(['direction:out', `topic:${params.TopicArn}`, 'type:sns'], span, payloadSize)
89
+ if (!params.MessageAttributes._datadog) {
90
+ params.MessageAttributes._datadog = {
91
+ DataType: 'Binary',
92
+ BinaryValue: ddInfo
93
+ }
94
+ }
95
+
96
+ const dataStreamsContext = this.setDSMCheckpoint(span, params, topicArn)
90
97
  if (dataStreamsContext) {
91
98
  const pathwayCtx = encodePathwayContext(dataStreamsContext)
92
99
  ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
93
100
  }
94
101
  }
95
- // BINARY types are automatically base64 encoded
96
- params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
102
+
103
+ if (Object.keys(ddInfo).length !== 0) {
104
+ // BINARY types are automatically base64 encoded
105
+ params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
106
+ } else if (params.MessageAttributes._datadog) {
107
+ // let's avoid adding any additional information to payload if we failed to inject
108
+ delete params.MessageAttributes._datadog
109
+ }
110
+ }
111
+
112
+ setDSMCheckpoint (span, params, topicArn) {
113
+ // only set a checkpoint if publishing to a topic
114
+ if (topicArn) {
115
+ const payloadSize = getHeadersSize(params)
116
+ const dataStreamsContext = this.tracer
117
+ .setCheckpoint(['direction:out', `topic:${topicArn}`, 'type:sns'], span, payloadSize)
118
+ return dataStreamsContext
119
+ }
97
120
  }
98
121
  }
99
122
 
@@ -40,7 +40,7 @@ class Sqs extends BaseAwsSdkPlugin {
40
40
  }
41
41
  // extract DSM context after as we might not have a parent-child but may have a DSM context
42
42
  this.responseExtractDSMContext(
43
- request.operation, request.params, response, span ?? null, parsedMessageAttributes ?? null
43
+ request.operation, request.params, response, span || null, parsedMessageAttributes || null
44
44
  )
45
45
  })
46
46
 
@@ -206,39 +206,75 @@ class Sqs extends BaseAwsSdkPlugin {
206
206
  }
207
207
 
208
208
  requestInject (span, request) {
209
- const operation = request.operation
210
- if (operation === 'sendMessage') {
211
- if (!request.params) {
212
- request.params = {}
213
- }
214
- if (!request.params.MessageAttributes) {
215
- request.params.MessageAttributes = {}
216
- } else if (Object.keys(request.params.MessageAttributes).length >= 10) { // SQS quota
217
- // TODO: add test when the test suite is fixed
218
- return
219
- }
220
- const ddInfo = {}
209
+ const { operation, params } = request
210
+
211
+ if (!params) return
212
+
213
+ switch (operation) {
214
+ case 'sendMessage':
215
+ this.injectToMessage(span, params, params.QueueUrl, true)
216
+ break
217
+ case 'sendMessageBatch':
218
+ for (let i = 0; i < params.Entries.length; i++) {
219
+ this.injectToMessage(span, params.Entries[i], params.QueueUrl, i === 0)
220
+ }
221
+ break
222
+ }
223
+ }
224
+
225
+ injectToMessage (span, params, queueUrl, injectTraceContext) {
226
+ if (!params) {
227
+ params = {}
228
+ }
229
+ if (!params.MessageAttributes) {
230
+ params.MessageAttributes = {}
231
+ } else if (Object.keys(params.MessageAttributes).length >= 10) { // SQS quota
232
+ // TODO: add test when the test suite is fixed
233
+ return
234
+ }
235
+ const ddInfo = {}
236
+ // for now, we only want to inject to the first message, this may change for batches in the future
237
+ if (injectTraceContext) {
221
238
  this.tracer.inject(span, 'text_map', ddInfo)
222
- request.params.MessageAttributes._datadog = {
239
+ params.MessageAttributes._datadog = {
223
240
  DataType: 'String',
224
241
  StringValue: JSON.stringify(ddInfo)
225
242
  }
226
- if (this.config.dsmEnabled) {
227
- const payloadSize = getHeadersSize({
228
- Body: request.params.MessageBody,
229
- MessageAttributes: request.params.MessageAttributes
230
- })
231
- const queue = request.params.QueueUrl.split('/').pop()
232
- const dataStreamsContext = this.tracer
233
- .setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
234
- if (dataStreamsContext) {
235
- const pathwayCtx = encodePathwayContext(dataStreamsContext)
236
- ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
243
+ }
244
+
245
+ if (this.config.dsmEnabled) {
246
+ if (!params.MessageAttributes._datadog) {
247
+ params.MessageAttributes._datadog = {
248
+ DataType: 'String',
249
+ StringValue: JSON.stringify(ddInfo)
237
250
  }
238
251
  }
239
- request.params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
252
+
253
+ const dataStreamsContext = this.setDSMCheckpoint(span, params, queueUrl)
254
+ if (dataStreamsContext) {
255
+ const pathwayCtx = encodePathwayContext(dataStreamsContext)
256
+ ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
257
+
258
+ params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
259
+ }
260
+ }
261
+
262
+ if (params.MessageAttributes._datadog && Object.keys(ddInfo).length === 0) {
263
+ // let's avoid adding any additional information to payload if we failed to inject
264
+ delete params.MessageAttributes._datadog
240
265
  }
241
266
  }
267
+
268
+ setDSMCheckpoint (span, params, queueUrl) {
269
+ const payloadSize = getHeadersSize({
270
+ Body: params.MessageBody,
271
+ MessageAttributes: params.MessageAttributes
272
+ })
273
+ const queue = queueUrl.split('/').pop()
274
+ const dataStreamsContext = this.tracer
275
+ .setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
276
+ return dataStreamsContext
277
+ }
242
278
  }
243
279
 
244
280
  module.exports = Sqs
@@ -15,7 +15,10 @@ const {
15
15
  TEST_ITR_FORCED_RUN,
16
16
  TEST_CODE_OWNERS,
17
17
  ITR_CORRELATION_ID,
18
- TEST_SOURCE_FILE
18
+ TEST_SOURCE_FILE,
19
+ TEST_EARLY_FLAKE_IS_ENABLED,
20
+ TEST_IS_NEW,
21
+ TEST_EARLY_FLAKE_IS_RETRY
19
22
  } = require('../../dd-trace/src/plugins/util/test')
20
23
  const { RESOURCE_NAME } = require('../../../ext/tags')
21
24
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
@@ -46,7 +49,8 @@ class CucumberPlugin extends CiPlugin {
46
49
  numSkippedSuites,
47
50
  testCodeCoverageLinesTotal,
48
51
  hasUnskippableSuites,
49
- hasForcedToRunSuites
52
+ hasForcedToRunSuites,
53
+ isEarlyFlakeDetectionEnabled
50
54
  }) => {
51
55
  const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
52
56
  addIntelligentTestRunnerSpanTags(
@@ -63,6 +67,9 @@ class CucumberPlugin extends CiPlugin {
63
67
  hasForcedToRunSuites
64
68
  }
65
69
  )
70
+ if (isEarlyFlakeDetectionEnabled) {
71
+ this.testSessionSpan.setTag(TEST_EARLY_FLAKE_IS_ENABLED, 'true')
72
+ }
66
73
 
67
74
  this.testSessionSpan.setTag(TEST_STATUS, status)
68
75
  this.testModuleSpan.setTag(TEST_STATUS, status)
@@ -141,7 +148,12 @@ class CucumberPlugin extends CiPlugin {
141
148
  const store = storage.getStore()
142
149
  const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
143
150
  const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
144
- const testSpan = this.startTestSpan(testName, testSuite, testSourceFile, testSourceLine)
151
+
152
+ const extraTags = {
153
+ [TEST_SOURCE_START]: testSourceLine,
154
+ [TEST_SOURCE_FILE]: testSourceFile
155
+ }
156
+ const testSpan = this.startTestSpan(testName, testSuite, extraTags)
145
157
 
146
158
  this.enter(testSpan, store)
147
159
  })
@@ -160,12 +172,19 @@ class CucumberPlugin extends CiPlugin {
160
172
  this.enter(span, store)
161
173
  })
162
174
 
163
- this.addSub('ci:cucumber:test:finish', ({ isStep, status, skipReason, errorMessage }) => {
175
+ this.addSub('ci:cucumber:test:finish', ({ isStep, status, skipReason, errorMessage, isNew, isEfdRetry }) => {
164
176
  const span = storage.getStore().span
165
177
  const statusTag = isStep ? 'step.status' : TEST_STATUS
166
178
 
167
179
  span.setTag(statusTag, status)
168
180
 
181
+ if (isNew) {
182
+ span.setTag(TEST_IS_NEW, 'true')
183
+ if (isEfdRetry) {
184
+ span.setTag(TEST_EARLY_FLAKE_IS_RETRY, 'true')
185
+ }
186
+ }
187
+
169
188
  if (skipReason) {
170
189
  span.setTag(TEST_SKIP_REASON, skipReason)
171
190
  }
@@ -193,15 +212,12 @@ class CucumberPlugin extends CiPlugin {
193
212
  })
194
213
  }
195
214
 
196
- startTestSpan (testName, testSuite, testSourceFile, testSourceLine) {
215
+ startTestSpan (testName, testSuite, extraTags) {
197
216
  return super.startTestSpan(
198
217
  testName,
199
218
  testSuite,
200
219
  this.testSuiteSpan,
201
- {
202
- [TEST_SOURCE_START]: testSourceLine,
203
- [TEST_SOURCE_FILE]: testSourceFile
204
- }
220
+ extraTags
205
221
  )
206
222
  }
207
223
  }
@@ -0,0 +1,3 @@
1
+ const cypressPlugin = require('./cypress-plugin')
2
+
3
+ module.exports = cypressPlugin.afterRun.bind(cypressPlugin)