dd-trace 2.26.2 → 2.27.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.
- package/ci/init.js +2 -1
- package/index.d.ts +20 -0
- package/package.json +2 -2
- package/packages/datadog-instrumentations/src/aws-sdk.js +86 -0
- package/packages/datadog-instrumentations/src/cucumber.js +74 -15
- package/packages/datadog-instrumentations/src/cypress.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +24 -33
- package/packages/datadog-instrumentations/src/mocha.js +4 -7
- package/packages/datadog-instrumentations/src/playwright.js +2 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +12 -5
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +29 -24
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +31 -16
- package/packages/datadog-plugin-cucumber/src/index.js +42 -11
- package/packages/datadog-plugin-cypress/src/plugin.js +129 -4
- package/packages/datadog-plugin-cypress/src/support.js +5 -0
- package/packages/datadog-plugin-jest/src/index.js +18 -67
- package/packages/datadog-plugin-mocha/src/index.js +35 -84
- package/packages/datadog-plugin-playwright/src/index.js +2 -61
- package/packages/datadog-shimmer/src/shimmer.js +28 -11
- package/packages/dd-trace/src/appsec/reporter.js +14 -14
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +1 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -5
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +32 -10
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
- package/packages/dd-trace/src/config.js +55 -6
- package/packages/dd-trace/src/encode/0.4.js +1 -1
- package/packages/dd-trace/src/encode/0.5.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +3 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +186 -36
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +99 -0
- package/packages/dd-trace/src/opentracing/span.js +2 -1
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +69 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/telemetry/send-data.js +4 -1
|
@@ -4,6 +4,7 @@ const pick = require('lodash.pick')
|
|
|
4
4
|
const id = require('../../id')
|
|
5
5
|
const DatadogSpanContext = require('../span_context')
|
|
6
6
|
const log = require('../../log')
|
|
7
|
+
const TraceState = require('./tracestate')
|
|
7
8
|
|
|
8
9
|
const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
|
|
9
10
|
|
|
@@ -29,8 +30,15 @@ const tagValueExpr = /^[\x20-\x2b\x2d-\x7e]*$/ // ASCII minus commas
|
|
|
29
30
|
const ddKeys = [traceKey, spanKey, samplingKey, originKey]
|
|
30
31
|
const b3Keys = [b3TraceKey, b3SpanKey, b3ParentKey, b3SampledKey, b3FlagsKey, b3HeaderKey]
|
|
31
32
|
const logKeys = ddKeys.concat(b3Keys)
|
|
32
|
-
const traceparentExpr = /^(
|
|
33
|
+
const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
|
|
33
34
|
const traceparentKey = 'traceparent'
|
|
35
|
+
// Origin value in tracestate replaces '~', ',' and ';' with '_"
|
|
36
|
+
const tracestateOriginFilter = /[^\x20-\x2b\x2d-\x3a\x3c-\x7d]/g
|
|
37
|
+
// Tag keys in tracestate replace ' ', ',' and '=' with '_'
|
|
38
|
+
const tracestateTagKeyFilter = /[^\x21-\x2b\x2d-\x3c\x3e-\x7e]/g
|
|
39
|
+
// Tag values in tracestate replace ',', '~' and ';' with '_'
|
|
40
|
+
const tracestateTagValueFilter = /[^\x20-\x2b\x2d-\x3a\x3c-\x7d]/g
|
|
41
|
+
const invalidSegment = /^0+$/
|
|
34
42
|
|
|
35
43
|
class TextMapPropagator {
|
|
36
44
|
constructor (config) {
|
|
@@ -38,15 +46,11 @@ class TextMapPropagator {
|
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
inject (spanContext, carrier) {
|
|
41
|
-
carrier[traceKey] = spanContext.toTraceId()
|
|
42
|
-
carrier[spanKey] = spanContext.toSpanId()
|
|
43
|
-
|
|
44
|
-
this._injectOrigin(spanContext, carrier)
|
|
45
|
-
this._injectSamplingPriority(spanContext, carrier)
|
|
46
49
|
this._injectBaggageItems(spanContext, carrier)
|
|
47
|
-
this.
|
|
50
|
+
this._injectDatadog(spanContext, carrier)
|
|
51
|
+
this._injectB3MultipleHeaders(spanContext, carrier)
|
|
52
|
+
this._injectB3SingleHeader(spanContext, carrier)
|
|
48
53
|
this._injectTraceparent(spanContext, carrier)
|
|
49
|
-
this._injectTags(spanContext, carrier)
|
|
50
54
|
|
|
51
55
|
log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
52
56
|
}
|
|
@@ -61,6 +65,17 @@ class TextMapPropagator {
|
|
|
61
65
|
return spanContext
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
_injectDatadog (spanContext, carrier) {
|
|
69
|
+
if (!this._hasPropagationStyle('inject', 'datadog')) return
|
|
70
|
+
|
|
71
|
+
carrier[traceKey] = spanContext.toTraceId()
|
|
72
|
+
carrier[spanKey] = spanContext.toSpanId()
|
|
73
|
+
|
|
74
|
+
this._injectOrigin(spanContext, carrier)
|
|
75
|
+
this._injectSamplingPriority(spanContext, carrier)
|
|
76
|
+
this._injectTags(spanContext, carrier)
|
|
77
|
+
}
|
|
78
|
+
|
|
64
79
|
_injectOrigin (spanContext, carrier) {
|
|
65
80
|
const origin = spanContext._trace.origin
|
|
66
81
|
|
|
@@ -112,8 +127,10 @@ class TextMapPropagator {
|
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
_injectB3MultipleHeaders (spanContext, carrier) {
|
|
131
|
+
const hasB3 = this._hasPropagationStyle('inject', 'b3')
|
|
132
|
+
const hasB3multi = this._hasPropagationStyle('inject', 'b3multi')
|
|
133
|
+
if (!(hasB3 || hasB3multi)) return
|
|
117
134
|
|
|
118
135
|
carrier[b3TraceKey] = spanContext._traceId.toString(16)
|
|
119
136
|
carrier[b3SpanKey] = spanContext._spanId.toString(16)
|
|
@@ -128,16 +145,92 @@ class TextMapPropagator {
|
|
|
128
145
|
}
|
|
129
146
|
}
|
|
130
147
|
|
|
148
|
+
_injectB3SingleHeader (spanContext, carrier) {
|
|
149
|
+
const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header')
|
|
150
|
+
if (!hasB3SingleHeader) return null
|
|
151
|
+
|
|
152
|
+
const traceId = spanContext._traceId.toString(16)
|
|
153
|
+
const spanId = spanContext._spanId.toString(16)
|
|
154
|
+
const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
|
|
155
|
+
|
|
156
|
+
carrier[b3HeaderKey] = `${traceId}-${spanId}-${sampled}`
|
|
157
|
+
if (spanContext._parentId) {
|
|
158
|
+
carrier[b3HeaderKey] += '-' + spanContext._parentId.toString(16)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
131
162
|
_injectTraceparent (spanContext, carrier) {
|
|
132
|
-
if (!this.
|
|
163
|
+
if (!this._hasPropagationStyle('inject', 'tracecontext')) return
|
|
164
|
+
|
|
165
|
+
const {
|
|
166
|
+
_sampling: { priority, mechanism },
|
|
167
|
+
_tracestate: ts = new TraceState(),
|
|
168
|
+
_trace: { origin, tags }
|
|
169
|
+
} = spanContext
|
|
170
|
+
|
|
133
171
|
carrier[traceparentKey] = spanContext.toTraceparent()
|
|
172
|
+
|
|
173
|
+
ts.forVendor('dd', state => {
|
|
174
|
+
state.set('s', priority)
|
|
175
|
+
if (mechanism) {
|
|
176
|
+
state.set('t.dm', mechanism)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (typeof origin === 'string') {
|
|
180
|
+
const originValue = origin
|
|
181
|
+
.replace(tracestateOriginFilter, '_')
|
|
182
|
+
.replace(/[\x3d]/g, '~')
|
|
183
|
+
|
|
184
|
+
state.set('o', originValue)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const key in tags) {
|
|
188
|
+
if (!tags[key] || !key.startsWith('_dd.p.')) continue
|
|
189
|
+
|
|
190
|
+
const tagKey = 't.' + key.slice(6)
|
|
191
|
+
.replace(tracestateTagKeyFilter, '_')
|
|
192
|
+
|
|
193
|
+
const tagValue = tags[key]
|
|
194
|
+
.toString()
|
|
195
|
+
.replace(tracestateTagValueFilter, '_')
|
|
196
|
+
.replace(/[\x3d]/g, '~')
|
|
197
|
+
|
|
198
|
+
state.set(tagKey, tagValue)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
carrier.tracestate = ts.toString()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_hasPropagationStyle (mode, name) {
|
|
206
|
+
return this._config.tracePropagationStyle[mode].includes(name)
|
|
134
207
|
}
|
|
135
208
|
|
|
136
209
|
_extractSpanContext (carrier) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
210
|
+
for (const extractor of this._config.tracePropagationStyle.extract) {
|
|
211
|
+
let spanContext = null
|
|
212
|
+
switch (extractor) {
|
|
213
|
+
case 'datadog':
|
|
214
|
+
spanContext = this._extractDatadogContext(carrier)
|
|
215
|
+
break
|
|
216
|
+
case 'tracecontext':
|
|
217
|
+
spanContext = this._extractTraceparentContext(carrier)
|
|
218
|
+
break
|
|
219
|
+
case 'b3': // TODO: should match "b3 single header" in next major
|
|
220
|
+
case 'b3multi':
|
|
221
|
+
spanContext = this._extractB3MultiContext(carrier)
|
|
222
|
+
break
|
|
223
|
+
case 'b3 single header': // TODO: delete in major after singular "b3"
|
|
224
|
+
spanContext = this._extractB3SingleContext(carrier)
|
|
225
|
+
break
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (spanContext !== null) {
|
|
229
|
+
return spanContext
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return this._extractSqsdContext(carrier)
|
|
141
234
|
}
|
|
142
235
|
|
|
143
236
|
_extractDatadogContext (carrier) {
|
|
@@ -153,10 +246,20 @@ class TextMapPropagator {
|
|
|
153
246
|
return spanContext
|
|
154
247
|
}
|
|
155
248
|
|
|
156
|
-
|
|
157
|
-
|
|
249
|
+
_extractB3MultiContext (carrier) {
|
|
250
|
+
const b3 = this._extractB3MultipleHeaders(carrier)
|
|
251
|
+
if (!b3) return null
|
|
252
|
+
return this._extractB3Context(b3)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
_extractB3SingleContext (carrier) {
|
|
256
|
+
if (!b3HeaderExpr.test(carrier[b3HeaderKey])) return null
|
|
257
|
+
const b3 = this._extractB3SingleHeader(carrier)
|
|
258
|
+
if (!b3) return null
|
|
259
|
+
return this._extractB3Context(b3)
|
|
260
|
+
}
|
|
158
261
|
|
|
159
|
-
|
|
262
|
+
_extractB3Context (b3) {
|
|
160
263
|
const debug = b3[b3FlagsKey] === '1'
|
|
161
264
|
const priority = this._getPriority(b3[b3SampledKey], debug)
|
|
162
265
|
const spanContext = this._extractGenericContext(b3, b3TraceKey, b3SpanKey, 16)
|
|
@@ -192,25 +295,73 @@ class TextMapPropagator {
|
|
|
192
295
|
}
|
|
193
296
|
|
|
194
297
|
_extractTraceparentContext (carrier) {
|
|
195
|
-
if (!this._config.experimental.traceparent) return null
|
|
196
|
-
|
|
197
298
|
const headerValue = carrier[traceparentKey]
|
|
198
299
|
if (!headerValue) {
|
|
199
300
|
return null
|
|
200
301
|
}
|
|
201
|
-
const matches = headerValue.match(traceparentExpr)
|
|
302
|
+
const matches = headerValue.trim().match(traceparentExpr)
|
|
202
303
|
if (matches.length) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
304
|
+
const [ version, traceId, spanId, flags, tail ] = matches.slice(1)
|
|
305
|
+
const tracestate = TraceState.fromString(carrier.tracestate)
|
|
306
|
+
if (invalidSegment.test(traceId)) return null
|
|
307
|
+
if (invalidSegment.test(spanId)) return null
|
|
308
|
+
|
|
309
|
+
// Version ff is considered invalid
|
|
310
|
+
if (version === 'ff') return null
|
|
311
|
+
|
|
312
|
+
// Version 00 should have no tail, but future versions may
|
|
313
|
+
if (tail && version === '00') return null
|
|
314
|
+
|
|
315
|
+
const spanContext = new DatadogSpanContext({
|
|
316
|
+
traceId: id(traceId, 16),
|
|
317
|
+
spanId: id(spanId, 16),
|
|
318
|
+
sampling: { priority: parseInt(flags, 10) & 1 ? 1 : 0 },
|
|
319
|
+
tracestate
|
|
207
320
|
})
|
|
321
|
+
|
|
322
|
+
tracestate.forVendor('dd', state => {
|
|
323
|
+
for (const [key, value] of state.entries()) {
|
|
324
|
+
switch (key) {
|
|
325
|
+
case 's': {
|
|
326
|
+
const priority = parseInt(value, 10)
|
|
327
|
+
if (!Number.isInteger(priority)) continue
|
|
328
|
+
if (
|
|
329
|
+
(spanContext._sampling.priority === 1 && priority > 0) ||
|
|
330
|
+
(spanContext._sampling.priority === 0 && priority < 0)
|
|
331
|
+
) {
|
|
332
|
+
spanContext._sampling.priority = priority
|
|
333
|
+
}
|
|
334
|
+
break
|
|
335
|
+
}
|
|
336
|
+
case 'o':
|
|
337
|
+
spanContext._trace.origin = value
|
|
338
|
+
break
|
|
339
|
+
case 't.dm': {
|
|
340
|
+
const mechanism = parseInt(value, 10)
|
|
341
|
+
if (Number.isInteger(mechanism)) {
|
|
342
|
+
spanContext._sampling.mechanism = mechanism
|
|
343
|
+
spanContext._trace.tags['_dd.p.dm'] = mechanism
|
|
344
|
+
}
|
|
345
|
+
break
|
|
346
|
+
}
|
|
347
|
+
default:
|
|
348
|
+
if (!key.startsWith('t.')) continue
|
|
349
|
+
spanContext._trace.tags[`_dd.p.${key.slice(2)}`] = value
|
|
350
|
+
.replace(/[\x7e]/gm, '=')
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
this._extractBaggageItems(carrier, spanContext)
|
|
356
|
+
return spanContext
|
|
208
357
|
}
|
|
209
358
|
return null
|
|
210
359
|
}
|
|
211
360
|
|
|
212
361
|
_extractGenericContext (carrier, traceKey, spanKey, radix) {
|
|
213
362
|
if (carrier[traceKey] && carrier[spanKey]) {
|
|
363
|
+
if (invalidSegment.test(carrier[traceKey])) return null
|
|
364
|
+
|
|
214
365
|
return new DatadogSpanContext({
|
|
215
366
|
traceId: id(carrier[traceKey], radix),
|
|
216
367
|
spanId: id(carrier[spanKey], radix)
|
|
@@ -220,35 +371,34 @@ class TextMapPropagator {
|
|
|
220
371
|
return null
|
|
221
372
|
}
|
|
222
373
|
|
|
223
|
-
_extractB3Headers (carrier) {
|
|
224
|
-
if (b3HeaderExpr.test(carrier[b3HeaderKey])) {
|
|
225
|
-
return this._extractB3SingleHeader(carrier)
|
|
226
|
-
} else {
|
|
227
|
-
return this._extractB3MultipleHeaders(carrier)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
374
|
_extractB3MultipleHeaders (carrier) {
|
|
375
|
+
let empty = true
|
|
232
376
|
const b3 = {}
|
|
233
377
|
|
|
234
378
|
if (b3TraceExpr.test(carrier[b3TraceKey]) && b3SpanExpr.test(carrier[b3SpanKey])) {
|
|
235
379
|
b3[b3TraceKey] = carrier[b3TraceKey]
|
|
236
380
|
b3[b3SpanKey] = carrier[b3SpanKey]
|
|
381
|
+
empty = false
|
|
237
382
|
}
|
|
238
383
|
|
|
239
384
|
if (carrier[b3SampledKey]) {
|
|
240
385
|
b3[b3SampledKey] = carrier[b3SampledKey]
|
|
386
|
+
empty = false
|
|
241
387
|
}
|
|
242
388
|
|
|
243
389
|
if (carrier[b3FlagsKey]) {
|
|
244
390
|
b3[b3FlagsKey] = carrier[b3FlagsKey]
|
|
391
|
+
empty = false
|
|
245
392
|
}
|
|
246
393
|
|
|
247
|
-
return b3
|
|
394
|
+
return empty ? null : b3
|
|
248
395
|
}
|
|
249
396
|
|
|
250
397
|
_extractB3SingleHeader (carrier) {
|
|
251
|
-
const
|
|
398
|
+
const header = carrier[b3HeaderKey]
|
|
399
|
+
if (!header) return null
|
|
400
|
+
|
|
401
|
+
const parts = header.split('-')
|
|
252
402
|
|
|
253
403
|
if (parts[0] === 'd') {
|
|
254
404
|
return {
|
|
@@ -299,7 +449,7 @@ class TextMapPropagator {
|
|
|
299
449
|
const priority = parseInt(carrier[samplingKey], 10)
|
|
300
450
|
|
|
301
451
|
if (Number.isInteger(priority)) {
|
|
302
|
-
spanContext._sampling.priority =
|
|
452
|
+
spanContext._sampling.priority = priority
|
|
303
453
|
}
|
|
304
454
|
}
|
|
305
455
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const traceStateRegex = /[ \t]*([^=]+)=([ \t]*[^, \t]+)[ \t]*(,|$)/gim
|
|
4
|
+
const traceStateDataRegex = /([^:]+):([^;]+)(;|$)/gim
|
|
5
|
+
|
|
6
|
+
function fromString (Type, regex, value) {
|
|
7
|
+
if (typeof value !== 'string' || !value.length) {
|
|
8
|
+
return new Type()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const values = []
|
|
12
|
+
for (const row of value.matchAll(regex)) {
|
|
13
|
+
values.unshift(row.slice(1, 3))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return new Type(values)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toString (map, pairSeparator, fieldSeparator) {
|
|
20
|
+
return Array.from(map.entries())
|
|
21
|
+
.reverse()
|
|
22
|
+
.map((pair) => pair.join(pairSeparator))
|
|
23
|
+
.join(fieldSeparator)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class TraceStateData extends Map {
|
|
27
|
+
constructor (...args) {
|
|
28
|
+
super(...args)
|
|
29
|
+
this.changed = false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
set (...args) {
|
|
33
|
+
if (this.has(args[0]) && this.get(args[0]) === args[1]) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
this.changed = true
|
|
37
|
+
return super.set(...args)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
delete (...args) {
|
|
41
|
+
this.changed = true
|
|
42
|
+
return super.delete(...args)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
clear (...args) {
|
|
46
|
+
this.changed = true
|
|
47
|
+
return super.clear(...args)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static fromString (value) {
|
|
51
|
+
return fromString(TraceStateData, traceStateDataRegex, value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
toString () {
|
|
55
|
+
return toString(this, ':', ';')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Pairs are stored in reverse of the serialized format to rely on set ordering
|
|
61
|
+
* new entries at the end to express update movement.
|
|
62
|
+
*/
|
|
63
|
+
class TraceState extends Map {
|
|
64
|
+
// Delete entries on update to ensure they're moved to the end of the list
|
|
65
|
+
set (key, value) {
|
|
66
|
+
if (this.has(key)) {
|
|
67
|
+
this.delete(key)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return super.set(key, value)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
forVendor (vendor, handle) {
|
|
74
|
+
const data = super.get(vendor)
|
|
75
|
+
const state = TraceStateData.fromString(data)
|
|
76
|
+
const result = handle(state)
|
|
77
|
+
|
|
78
|
+
if (state.changed) {
|
|
79
|
+
const value = state.toString()
|
|
80
|
+
if (value) {
|
|
81
|
+
this.set(vendor, state.toString())
|
|
82
|
+
} else {
|
|
83
|
+
this.delete(vendor)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static fromString (value) {
|
|
91
|
+
return fromString(TraceState, traceStateRegex, value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
toString () {
|
|
95
|
+
return toString(this, '=', ',')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = TraceState
|
|
@@ -150,7 +150,8 @@ class DatadogSpan {
|
|
|
150
150
|
parentId: parent._spanId,
|
|
151
151
|
sampling: parent._sampling,
|
|
152
152
|
baggageItems: Object.assign({}, parent._baggageItems),
|
|
153
|
-
trace: parent._trace
|
|
153
|
+
trace: parent._trace,
|
|
154
|
+
tracestate: parent._tracestate
|
|
154
155
|
})
|
|
155
156
|
} else {
|
|
156
157
|
const spanId = id()
|
|
@@ -14,6 +14,7 @@ class DatadogSpanContext {
|
|
|
14
14
|
this._tags = props.tags || {}
|
|
15
15
|
this._sampling = props.sampling || {}
|
|
16
16
|
this._baggageItems = props.baggageItems || {}
|
|
17
|
+
this._tracestate = props.tracestate
|
|
17
18
|
this._noop = props.noop || null
|
|
18
19
|
this._trace = props.trace || {
|
|
19
20
|
started: [],
|
|
@@ -5,11 +5,18 @@ const {
|
|
|
5
5
|
getTestCommonTags,
|
|
6
6
|
getCodeOwnersForFilename,
|
|
7
7
|
TEST_CODE_OWNERS,
|
|
8
|
-
CI_APP_ORIGIN
|
|
8
|
+
CI_APP_ORIGIN,
|
|
9
|
+
getTestSessionCommonTags,
|
|
10
|
+
getTestModuleCommonTags,
|
|
11
|
+
TEST_SUITE_ID,
|
|
12
|
+
TEST_MODULE_ID,
|
|
13
|
+
TEST_SESSION_ID,
|
|
14
|
+
TEST_COMMAND,
|
|
15
|
+
TEST_BUNDLE
|
|
9
16
|
} = require('./util/test')
|
|
10
|
-
const { COMPONENT } = require('../constants')
|
|
11
|
-
|
|
12
17
|
const Plugin = require('./plugin')
|
|
18
|
+
const { COMPONENT } = require('../constants')
|
|
19
|
+
const log = require('../log')
|
|
13
20
|
|
|
14
21
|
module.exports = class CiPlugin extends Plugin {
|
|
15
22
|
constructor (...args) {
|
|
@@ -20,7 +27,9 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
20
27
|
return onDone({ err: new Error('CI Visibility was not initialized correctly') })
|
|
21
28
|
}
|
|
22
29
|
this.tracer._exporter.getItrConfiguration(this.testConfiguration, (err, itrConfig) => {
|
|
23
|
-
if (
|
|
30
|
+
if (err) {
|
|
31
|
+
log.error(`Error fetching intelligent test runner configuration: ${err.message}`)
|
|
32
|
+
} else {
|
|
24
33
|
this.itrConfig = itrConfig
|
|
25
34
|
}
|
|
26
35
|
onDone({ err, itrConfig })
|
|
@@ -32,9 +41,40 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
32
41
|
return onDone({ err: new Error('CI Visibility was not initialized correctly') })
|
|
33
42
|
}
|
|
34
43
|
this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites) => {
|
|
44
|
+
if (err) {
|
|
45
|
+
log.error(`Error fetching skippable suites: ${err.message}`)
|
|
46
|
+
}
|
|
35
47
|
onDone({ err, skippableSuites })
|
|
36
48
|
})
|
|
37
49
|
})
|
|
50
|
+
|
|
51
|
+
this.addSub(`ci:${this.constructor.name}:session:start`, ({ command, frameworkVersion, rootDir }) => {
|
|
52
|
+
const childOf = getTestParentSpan(this.tracer)
|
|
53
|
+
const testSessionSpanMetadata = getTestSessionCommonTags(command, frameworkVersion)
|
|
54
|
+
const testModuleSpanMetadata = getTestModuleCommonTags(command, frameworkVersion)
|
|
55
|
+
|
|
56
|
+
this.command = command
|
|
57
|
+
this.frameworkVersion = frameworkVersion
|
|
58
|
+
// only for playwright
|
|
59
|
+
this.rootDir = rootDir
|
|
60
|
+
|
|
61
|
+
this.testSessionSpan = this.tracer.startSpan(`${this.constructor.name}.test_session`, {
|
|
62
|
+
childOf,
|
|
63
|
+
tags: {
|
|
64
|
+
[COMPONENT]: this.constructor.name,
|
|
65
|
+
...this.testEnvironmentMetadata,
|
|
66
|
+
...testSessionSpanMetadata
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
this.testModuleSpan = this.tracer.startSpan(`${this.constructor.name}.test_module`, {
|
|
70
|
+
childOf: this.testSessionSpan,
|
|
71
|
+
tags: {
|
|
72
|
+
[COMPONENT]: this.constructor.name,
|
|
73
|
+
...this.testEnvironmentMetadata,
|
|
74
|
+
...testModuleSpanMetadata
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
})
|
|
38
78
|
}
|
|
39
79
|
|
|
40
80
|
configure (config) {
|
|
@@ -65,25 +105,42 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
65
105
|
}
|
|
66
106
|
}
|
|
67
107
|
|
|
68
|
-
startTestSpan (
|
|
69
|
-
const
|
|
70
|
-
const testCommonTags = getTestCommonTags(name, suite, this.tracer._version)
|
|
108
|
+
startTestSpan (testName, testSuite, testSuiteSpan, extraTags = {}) {
|
|
109
|
+
const childOf = getTestParentSpan(this.tracer)
|
|
71
110
|
|
|
72
|
-
|
|
73
|
-
...
|
|
111
|
+
let testTags = {
|
|
112
|
+
...getTestCommonTags(testName, testSuite, this.frameworkVersion),
|
|
74
113
|
[COMPONENT]: this.constructor.name,
|
|
75
114
|
...extraTags
|
|
76
115
|
}
|
|
77
116
|
|
|
78
|
-
const codeOwners = getCodeOwnersForFilename(
|
|
79
|
-
|
|
117
|
+
const codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
|
|
80
118
|
if (codeOwners) {
|
|
81
119
|
testTags[TEST_CODE_OWNERS] = codeOwners
|
|
82
120
|
}
|
|
83
121
|
|
|
122
|
+
if (testSuiteSpan) {
|
|
123
|
+
// This is a hack to get good time resolution on test events, while keeping
|
|
124
|
+
// the test event as the root span of its trace.
|
|
125
|
+
childOf._trace.startTime = testSuiteSpan.context()._trace.startTime
|
|
126
|
+
childOf._trace.ticks = testSuiteSpan.context()._trace.ticks
|
|
127
|
+
|
|
128
|
+
const suiteTags = {
|
|
129
|
+
[TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
|
|
130
|
+
[TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
|
|
131
|
+
[TEST_MODULE_ID]: testSuiteSpan.context()._parentId.toString(10),
|
|
132
|
+
[TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
|
|
133
|
+
[TEST_BUNDLE]: testSuiteSpan.context()._tags[TEST_COMMAND]
|
|
134
|
+
}
|
|
135
|
+
testTags = {
|
|
136
|
+
...testTags,
|
|
137
|
+
...suiteTags
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
84
141
|
const testSpan = this.tracer
|
|
85
142
|
.startSpan(`${this.constructor.name}.test`, {
|
|
86
|
-
childOf
|
|
143
|
+
childOf,
|
|
87
144
|
tags: {
|
|
88
145
|
...this.testEnvironmentMetadata,
|
|
89
146
|
...testTags
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
+
get '@aws-sdk/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
|
|
4
5
|
get '@cucumber/cucumber' () { return require('../../../datadog-plugin-cucumber/src') },
|
|
5
6
|
get '@playwright/test' () { return require('../../../datadog-plugin-playwright/src') },
|
|
6
7
|
get '@elastic/elasticsearch' () { return require('../../../datadog-plugin-elasticsearch/src') },
|
|
@@ -6,6 +6,9 @@ function sendData (config, application, host, reqType, payload = {}) {
|
|
|
6
6
|
port,
|
|
7
7
|
url
|
|
8
8
|
} = config
|
|
9
|
+
|
|
10
|
+
const { logger, tags, serviceMapping, ...trimmedPayload } = payload
|
|
11
|
+
|
|
9
12
|
const options = {
|
|
10
13
|
url,
|
|
11
14
|
hostname,
|
|
@@ -24,7 +27,7 @@ function sendData (config, application, host, reqType, payload = {}) {
|
|
|
24
27
|
tracer_time: Math.floor(Date.now() / 1000),
|
|
25
28
|
runtime_id: config.tags['runtime-id'],
|
|
26
29
|
seq_id: ++seqId,
|
|
27
|
-
payload,
|
|
30
|
+
payload: trimmedPayload,
|
|
28
31
|
application,
|
|
29
32
|
host
|
|
30
33
|
})
|