dd-trace 4.38.1 → 4.40.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/LICENSE-3rdparty.csv +0 -3
- package/README.md +8 -18
- package/ci/init.js +7 -0
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +2 -1
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +18 -3
- package/initialize.mjs +52 -0
- package/package.json +9 -12
- package/packages/datadog-instrumentations/src/amqplib.js +5 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
- package/packages/datadog-instrumentations/src/body-parser.js +0 -1
- package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
- package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
- package/packages/datadog-instrumentations/src/express.js +0 -1
- package/packages/datadog-instrumentations/src/graphql.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
- package/packages/datadog-instrumentations/src/http/server.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
- package/packages/datadog-instrumentations/src/mocha.js +4 -673
- package/packages/datadog-instrumentations/src/openai.js +188 -17
- package/packages/datadog-instrumentations/src/playwright.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +13 -6
- package/packages/datadog-plugin-graphql/src/resolve.js +4 -0
- package/packages/datadog-plugin-mocha/src/index.js +82 -8
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/index.js +219 -73
- package/packages/dd-trace/src/appsec/addresses.js +4 -2
- package/packages/dd-trace/src/appsec/blocking.js +19 -25
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/graphql.js +10 -3
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/rasp.js +35 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
- package/packages/dd-trace/src/config.js +97 -22
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/encode/0.4.js +47 -8
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/flare/file.js +44 -0
- package/packages/dd-trace/src/flare/index.js +98 -0
- package/packages/dd-trace/src/log/channels.js +54 -29
- package/packages/dd-trace/src/log/writer.js +7 -49
- package/packages/dd-trace/src/opentelemetry/span.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +6 -0
- package/packages/dd-trace/src/priority_sampler.js +8 -4
- package/packages/dd-trace/src/profiler.js +2 -1
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
- package/packages/dd-trace/src/proxy.js +49 -15
- package/packages/dd-trace/src/ritm.js +13 -1
- package/packages/dd-trace/src/sampling_rule.js +2 -1
- package/packages/dd-trace/src/startup-log.js +19 -15
- package/packages/dd-trace/src/telemetry/index.js +6 -2
- package/packages/dd-trace/src/tracer.js +3 -0
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
|
@@ -15,13 +15,15 @@ const V4_PACKAGE_SHIMS = [
|
|
|
15
15
|
file: 'resources/chat/completions.js',
|
|
16
16
|
targetClass: 'Completions',
|
|
17
17
|
baseResource: 'chat.completions',
|
|
18
|
-
methods: ['create']
|
|
18
|
+
methods: ['create'],
|
|
19
|
+
streamedResponse: true
|
|
19
20
|
},
|
|
20
21
|
{
|
|
21
22
|
file: 'resources/completions.js',
|
|
22
23
|
targetClass: 'Completions',
|
|
23
24
|
baseResource: 'completions',
|
|
24
|
-
methods: ['create']
|
|
25
|
+
methods: ['create'],
|
|
26
|
+
streamedResponse: true
|
|
25
27
|
},
|
|
26
28
|
{
|
|
27
29
|
file: 'resources/embeddings.js',
|
|
@@ -121,7 +123,7 @@ addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, expor
|
|
|
121
123
|
|
|
122
124
|
return fn.apply(this, arguments)
|
|
123
125
|
.then((response) => {
|
|
124
|
-
|
|
126
|
+
finish({
|
|
125
127
|
headers: response.headers,
|
|
126
128
|
body: response.data,
|
|
127
129
|
path: response.request.path,
|
|
@@ -130,10 +132,10 @@ addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, expor
|
|
|
130
132
|
|
|
131
133
|
return response
|
|
132
134
|
})
|
|
133
|
-
.catch(
|
|
134
|
-
|
|
135
|
+
.catch(error => {
|
|
136
|
+
finish(undefined, error)
|
|
135
137
|
|
|
136
|
-
throw
|
|
138
|
+
throw error
|
|
137
139
|
})
|
|
138
140
|
})
|
|
139
141
|
}
|
|
@@ -141,9 +143,140 @@ addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, expor
|
|
|
141
143
|
return exports
|
|
142
144
|
})
|
|
143
145
|
|
|
146
|
+
function addStreamedChunk (content, chunk) {
|
|
147
|
+
content.usage = chunk.usage // add usage if it was specified to be returned
|
|
148
|
+
for (const choice of chunk.choices) {
|
|
149
|
+
const choiceIdx = choice.index
|
|
150
|
+
const oldChoice = content.choices.find(choice => choice?.index === choiceIdx)
|
|
151
|
+
if (!oldChoice) {
|
|
152
|
+
// we don't know which choices arrive in which order
|
|
153
|
+
content.choices[choiceIdx] = choice
|
|
154
|
+
} else {
|
|
155
|
+
if (!oldChoice.finish_reason) {
|
|
156
|
+
oldChoice.finish_reason = choice.finish_reason
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// delta exists on chat completions
|
|
160
|
+
const delta = choice.delta
|
|
161
|
+
|
|
162
|
+
if (delta) {
|
|
163
|
+
const content = delta.content
|
|
164
|
+
if (content) {
|
|
165
|
+
if (oldChoice.delta.content) { // we don't want to append to undefined
|
|
166
|
+
oldChoice.delta.content += content
|
|
167
|
+
} else {
|
|
168
|
+
oldChoice.delta.content = content
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
const text = choice.text
|
|
173
|
+
if (text) {
|
|
174
|
+
if (oldChoice.text) {
|
|
175
|
+
oldChoice.text += text
|
|
176
|
+
} else {
|
|
177
|
+
oldChoice.text = text
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// tools only exist on chat completions
|
|
183
|
+
const tools = delta && choice.delta.tool_calls
|
|
184
|
+
|
|
185
|
+
if (tools) {
|
|
186
|
+
oldChoice.delta.tool_calls = tools.map((newTool, toolIdx) => {
|
|
187
|
+
const oldTool = oldChoice.delta.tool_calls[toolIdx]
|
|
188
|
+
|
|
189
|
+
if (oldTool) {
|
|
190
|
+
oldTool.function.arguments += newTool.function.arguments
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return oldTool
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function convertBufferstoObjects (chunks = []) {
|
|
201
|
+
return Buffer
|
|
202
|
+
.concat(chunks) // combine the buffers
|
|
203
|
+
.toString() // stringify
|
|
204
|
+
.split(/(?=data:)/) // split on "data:"
|
|
205
|
+
.map(chunk => chunk.split('\n').join('')) // remove newlines
|
|
206
|
+
.map(chunk => chunk.substring(6)) // remove 'data: ' from the front
|
|
207
|
+
.slice(0, -1) // remove the last [DONE] message
|
|
208
|
+
.map(JSON.parse) // parse all of the returned objects
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* For streamed responses, we need to accumulate all of the content in
|
|
213
|
+
* the chunks, and let the combined content be the final response.
|
|
214
|
+
* This way, spans look the same as when not streamed.
|
|
215
|
+
*/
|
|
216
|
+
function wrapStreamIterator (response, options, n) {
|
|
217
|
+
let processChunksAsBuffers = false
|
|
218
|
+
let chunks = []
|
|
219
|
+
return function (itr) {
|
|
220
|
+
return function () {
|
|
221
|
+
const iterator = itr.apply(this, arguments)
|
|
222
|
+
shimmer.wrap(iterator, 'next', next => function () {
|
|
223
|
+
return next.apply(this, arguments)
|
|
224
|
+
.then(res => {
|
|
225
|
+
const { done, value: chunk } = res
|
|
226
|
+
|
|
227
|
+
if (chunk) {
|
|
228
|
+
chunks.push(chunk)
|
|
229
|
+
if (chunk instanceof Buffer) {
|
|
230
|
+
// this operation should be safe
|
|
231
|
+
// if one chunk is a buffer (versus a plain object), the rest should be as well
|
|
232
|
+
processChunksAsBuffers = true
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (done) {
|
|
237
|
+
let body = {}
|
|
238
|
+
chunks = chunks.filter(chunk => chunk != null) // filter null or undefined values
|
|
239
|
+
|
|
240
|
+
if (chunks) {
|
|
241
|
+
if (processChunksAsBuffers) {
|
|
242
|
+
chunks = convertBufferstoObjects(chunks)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (chunks.length) {
|
|
246
|
+
// define the initial body having all the content outside of choices from the first chunk
|
|
247
|
+
// this will include import data like created, id, model, etc.
|
|
248
|
+
body = { ...chunks[0], choices: Array.from({ length: n }) }
|
|
249
|
+
// start from the first chunk, and add its choices into the body
|
|
250
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
251
|
+
addStreamedChunk(body, chunks[i])
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
finish({
|
|
257
|
+
headers: response.headers,
|
|
258
|
+
body,
|
|
259
|
+
path: response.url,
|
|
260
|
+
method: options.method
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return res
|
|
265
|
+
})
|
|
266
|
+
.catch(err => {
|
|
267
|
+
finish(undefined, err)
|
|
268
|
+
|
|
269
|
+
throw err
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
return iterator
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
144
277
|
for (const shim of V4_PACKAGE_SHIMS) {
|
|
145
|
-
const { file, targetClass, baseResource, methods } = shim
|
|
146
|
-
addHook({ name: 'openai', file, versions:
|
|
278
|
+
const { file, targetClass, baseResource, methods, versions, streamedResponse } = shim
|
|
279
|
+
addHook({ name: 'openai', file, versions: versions || ['>=4'] }, exports => {
|
|
147
280
|
const targetPrototype = exports[targetClass].prototype
|
|
148
281
|
|
|
149
282
|
for (const methodName of methods) {
|
|
@@ -152,6 +285,22 @@ for (const shim of V4_PACKAGE_SHIMS) {
|
|
|
152
285
|
return methodFn.apply(this, arguments)
|
|
153
286
|
}
|
|
154
287
|
|
|
288
|
+
// The OpenAI library lets you set `stream: true` on the options arg to any method
|
|
289
|
+
// However, we only want to handle streamed responses in specific cases
|
|
290
|
+
// chat.completions and completions
|
|
291
|
+
const stream = streamedResponse && getOption(arguments, 'stream', false)
|
|
292
|
+
|
|
293
|
+
// we need to compute how many prompts we are sending in streamed cases for completions
|
|
294
|
+
// not applicable for chat completiond
|
|
295
|
+
let n
|
|
296
|
+
if (stream) {
|
|
297
|
+
n = getOption(arguments, 'n', 1)
|
|
298
|
+
const prompt = getOption(arguments, 'prompt')
|
|
299
|
+
if (Array.isArray(prompt) && typeof prompt[0] !== 'number') {
|
|
300
|
+
n *= prompt.length
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
155
304
|
const client = this._client || this.client
|
|
156
305
|
|
|
157
306
|
startCh.publish({
|
|
@@ -170,19 +319,29 @@ for (const shim of V4_PACKAGE_SHIMS) {
|
|
|
170
319
|
// the original response is wrapped in a promise, so we need to unwrap it
|
|
171
320
|
.then(body => Promise.all([this.responsePromise, body]))
|
|
172
321
|
.then(([{ response, options }, body]) => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
322
|
+
if (stream) {
|
|
323
|
+
if (body.iterator) {
|
|
324
|
+
shimmer.wrap(body, 'iterator', wrapStreamIterator(response, options, n))
|
|
325
|
+
} else {
|
|
326
|
+
shimmer.wrap(
|
|
327
|
+
body.response.body, Symbol.asyncIterator, wrapStreamIterator(response, options, n)
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
finish({
|
|
332
|
+
headers: response.headers,
|
|
333
|
+
body,
|
|
334
|
+
path: response.url,
|
|
335
|
+
method: options.method
|
|
336
|
+
})
|
|
337
|
+
}
|
|
179
338
|
|
|
180
339
|
return body
|
|
181
340
|
})
|
|
182
|
-
.catch(
|
|
183
|
-
|
|
341
|
+
.catch(error => {
|
|
342
|
+
finish(undefined, error)
|
|
184
343
|
|
|
185
|
-
throw
|
|
344
|
+
throw error
|
|
186
345
|
})
|
|
187
346
|
.finally(() => {
|
|
188
347
|
// maybe we don't want to unwrap here in case the promise is re-used?
|
|
@@ -197,3 +356,15 @@ for (const shim of V4_PACKAGE_SHIMS) {
|
|
|
197
356
|
return exports
|
|
198
357
|
})
|
|
199
358
|
}
|
|
359
|
+
|
|
360
|
+
function finish (response, error) {
|
|
361
|
+
if (error) {
|
|
362
|
+
errorCh.publish({ error })
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
finishCh.publish(response)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function getOption (args, option, defaultValue) {
|
|
369
|
+
return args[args.length - 1]?.[option] || defaultValue
|
|
370
|
+
}
|
|
@@ -301,7 +301,7 @@ function dispatcherRunWrapperNew (run) {
|
|
|
301
301
|
if (!this._allTests) {
|
|
302
302
|
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
303
303
|
// Not available from >=1.44.0
|
|
304
|
-
this.
|
|
304
|
+
this._ddAllTests = testGroups.map(g => g.tests).flat()
|
|
305
305
|
}
|
|
306
306
|
remainingTestsByFile = getTestsBySuiteFromTestGroups(arguments[0])
|
|
307
307
|
return run.apply(this, arguments)
|
|
@@ -339,8 +339,9 @@ function getTestByTestId (dispatcher, testId) {
|
|
|
339
339
|
if (dispatcher._testById) {
|
|
340
340
|
return dispatcher._testById.get(testId)?.test
|
|
341
341
|
}
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
const allTests = dispatcher._allTests || dispatcher._ddAllTests
|
|
343
|
+
if (allTests) {
|
|
344
|
+
return allTests.find(({ id }) => id === testId)
|
|
344
345
|
}
|
|
345
346
|
}
|
|
346
347
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const METHODS = require('
|
|
3
|
+
const METHODS = require('http').METHODS.map(v => v.toLowerCase()).concat('all')
|
|
4
4
|
const pathToRegExp = require('path-to-regexp')
|
|
5
5
|
const shimmer = require('../../datadog-shimmer')
|
|
6
6
|
const { addHook, channel } = require('./helpers/instrument')
|
|
@@ -23,6 +23,9 @@ addHook({
|
|
|
23
23
|
}, (seleniumPackage, seleniumVersion) => {
|
|
24
24
|
// TODO: do not turn this into async. Use promises
|
|
25
25
|
shimmer.wrap(seleniumPackage.WebDriver.prototype, 'get', get => async function () {
|
|
26
|
+
if (!ciSeleniumDriverGetStartCh.hasSubscribers) {
|
|
27
|
+
return get.apply(this, arguments)
|
|
28
|
+
}
|
|
26
29
|
let traceId
|
|
27
30
|
const setTraceId = (inputTraceId) => {
|
|
28
31
|
traceId = inputTraceId
|
|
@@ -40,15 +43,20 @@ addHook({
|
|
|
40
43
|
isRumActive
|
|
41
44
|
})
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
if (traceId && isRumActive) {
|
|
47
|
+
await this.manage().addCookie({
|
|
48
|
+
name: DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
49
|
+
value: traceId
|
|
50
|
+
})
|
|
51
|
+
}
|
|
47
52
|
|
|
48
53
|
return getResult
|
|
49
54
|
})
|
|
50
55
|
|
|
51
56
|
shimmer.wrap(seleniumPackage.WebDriver.prototype, 'quit', quit => async function () {
|
|
57
|
+
if (!ciSeleniumDriverGetStartCh.hasSubscribers) {
|
|
58
|
+
return quit.apply(this, arguments)
|
|
59
|
+
}
|
|
52
60
|
const isRumActive = await this.executeScript(RUM_STOP_SESSION_SCRIPT)
|
|
53
61
|
|
|
54
62
|
if (isRumActive) {
|
|
@@ -58,10 +66,9 @@ addHook({
|
|
|
58
66
|
resolve()
|
|
59
67
|
}, DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS)
|
|
60
68
|
})
|
|
69
|
+
await this.manage().deleteCookie(DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME)
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
await this.manage().deleteCookie(DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME)
|
|
64
|
-
|
|
65
72
|
return quit.apply(this, arguments)
|
|
66
73
|
})
|
|
67
74
|
|
|
@@ -80,6 +80,10 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
80
80
|
// this will disable resolve subscribers if `config.depth` is set to 0
|
|
81
81
|
super.configure(config.depth === 0 ? false : config)
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
finish (finishTime) {
|
|
85
|
+
this.activeSpan.finish(finishTime)
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// helpers
|
|
@@ -20,7 +20,14 @@ const {
|
|
|
20
20
|
removeEfdStringFromTestName,
|
|
21
21
|
TEST_IS_NEW,
|
|
22
22
|
TEST_IS_RETRY,
|
|
23
|
-
TEST_EARLY_FLAKE_ENABLED
|
|
23
|
+
TEST_EARLY_FLAKE_ENABLED,
|
|
24
|
+
TEST_SESSION_ID,
|
|
25
|
+
TEST_MODULE_ID,
|
|
26
|
+
TEST_MODULE,
|
|
27
|
+
TEST_SUITE_ID,
|
|
28
|
+
TEST_COMMAND,
|
|
29
|
+
TEST_SUITE,
|
|
30
|
+
MOCHA_IS_PARALLEL
|
|
24
31
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
25
32
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
26
33
|
const {
|
|
@@ -33,6 +40,22 @@ const {
|
|
|
33
40
|
TELEMETRY_ITR_UNSKIPPABLE,
|
|
34
41
|
TELEMETRY_CODE_COVERAGE_NUM_FILES
|
|
35
42
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
43
|
+
const id = require('../../dd-trace/src/id')
|
|
44
|
+
const log = require('../../dd-trace/src/log')
|
|
45
|
+
|
|
46
|
+
function getTestSuiteLevelVisibilityTags (testSuiteSpan) {
|
|
47
|
+
const testSuiteSpanContext = testSuiteSpan.context()
|
|
48
|
+
const suiteTags = {
|
|
49
|
+
[TEST_SUITE_ID]: testSuiteSpanContext.toSpanId(),
|
|
50
|
+
[TEST_SESSION_ID]: testSuiteSpanContext.toTraceId(),
|
|
51
|
+
[TEST_COMMAND]: testSuiteSpanContext._tags[TEST_COMMAND],
|
|
52
|
+
[TEST_MODULE]: 'mocha'
|
|
53
|
+
}
|
|
54
|
+
if (testSuiteSpanContext._parentId) {
|
|
55
|
+
suiteTags[TEST_MODULE_ID] = testSuiteSpanContext._parentId.toString(10)
|
|
56
|
+
}
|
|
57
|
+
return suiteTags
|
|
58
|
+
}
|
|
36
59
|
|
|
37
60
|
class MochaPlugin extends CiPlugin {
|
|
38
61
|
static get id () {
|
|
@@ -50,7 +73,8 @@ class MochaPlugin extends CiPlugin {
|
|
|
50
73
|
if (!this.libraryConfig?.isCodeCoverageEnabled) {
|
|
51
74
|
return
|
|
52
75
|
}
|
|
53
|
-
const
|
|
76
|
+
const testSuite = getTestSuitePath(suiteFile, this.sourceRoot)
|
|
77
|
+
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
54
78
|
|
|
55
79
|
if (!coverageFiles.length) {
|
|
56
80
|
this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
|
|
@@ -73,16 +97,20 @@ class MochaPlugin extends CiPlugin {
|
|
|
73
97
|
})
|
|
74
98
|
|
|
75
99
|
this.addSub('ci:mocha:test-suite:start', ({
|
|
76
|
-
|
|
100
|
+
testSuiteAbsolutePath,
|
|
77
101
|
isUnskippable,
|
|
78
102
|
isForcedToRun,
|
|
79
103
|
itrCorrelationId
|
|
80
104
|
}) => {
|
|
81
|
-
|
|
105
|
+
// If the test module span is undefined, the plugin has not been initialized correctly and we bail out
|
|
106
|
+
if (!this.testModuleSpan) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
82
110
|
const testSuiteMetadata = getTestSuiteCommonTags(
|
|
83
111
|
this.command,
|
|
84
112
|
this.frameworkVersion,
|
|
85
|
-
|
|
113
|
+
testSuite,
|
|
86
114
|
'mocha'
|
|
87
115
|
)
|
|
88
116
|
if (isUnskippable) {
|
|
@@ -109,6 +137,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
109
137
|
if (itrCorrelationId) {
|
|
110
138
|
testSuiteSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
|
|
111
139
|
}
|
|
140
|
+
const store = storage.getStore()
|
|
112
141
|
this.enter(testSuiteSpan, store)
|
|
113
142
|
this._testSuites.set(testSuite, testSuiteSpan)
|
|
114
143
|
})
|
|
@@ -142,6 +171,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
142
171
|
this.enter(span, store)
|
|
143
172
|
})
|
|
144
173
|
|
|
174
|
+
this.addSub('ci:mocha:worker:finish', () => {
|
|
175
|
+
this.tracer._exporter.flush()
|
|
176
|
+
})
|
|
177
|
+
|
|
145
178
|
this.addSub('ci:mocha:test:finish', (status) => {
|
|
146
179
|
const store = storage.getStore()
|
|
147
180
|
const span = store?.span
|
|
@@ -194,7 +227,8 @@ class MochaPlugin extends CiPlugin {
|
|
|
194
227
|
hasForcedToRunSuites,
|
|
195
228
|
hasUnskippableSuites,
|
|
196
229
|
error,
|
|
197
|
-
isEarlyFlakeDetectionEnabled
|
|
230
|
+
isEarlyFlakeDetectionEnabled,
|
|
231
|
+
isParallel
|
|
198
232
|
}) => {
|
|
199
233
|
if (this.testSessionSpan) {
|
|
200
234
|
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
|
|
@@ -206,6 +240,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
206
240
|
this.testModuleSpan.setTag('error', error)
|
|
207
241
|
}
|
|
208
242
|
|
|
243
|
+
if (isParallel) {
|
|
244
|
+
this.testSessionSpan.setTag(MOCHA_IS_PARALLEL, 'true')
|
|
245
|
+
}
|
|
246
|
+
|
|
209
247
|
addIntelligentTestRunnerSpanTags(
|
|
210
248
|
this.testSessionSpan,
|
|
211
249
|
this.testModuleSpan,
|
|
@@ -234,6 +272,37 @@ class MochaPlugin extends CiPlugin {
|
|
|
234
272
|
this.libraryConfig = null
|
|
235
273
|
this.tracer._exporter.flush()
|
|
236
274
|
})
|
|
275
|
+
|
|
276
|
+
this.addSub('ci:mocha:worker-report:trace', (traces) => {
|
|
277
|
+
const formattedTraces = JSON.parse(traces).map(trace =>
|
|
278
|
+
trace.map(span => {
|
|
279
|
+
const formattedSpan = {
|
|
280
|
+
...span,
|
|
281
|
+
span_id: id(span.span_id),
|
|
282
|
+
trace_id: id(span.trace_id),
|
|
283
|
+
parent_id: id(span.parent_id)
|
|
284
|
+
}
|
|
285
|
+
if (formattedSpan.name === 'mocha.test') {
|
|
286
|
+
const testSuite = span.meta[TEST_SUITE]
|
|
287
|
+
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
288
|
+
if (!testSuiteSpan) {
|
|
289
|
+
log.warn(`Test suite span not found for test span with test suite ${testSuite}`)
|
|
290
|
+
return formattedSpan
|
|
291
|
+
}
|
|
292
|
+
const suiteTags = getTestSuiteLevelVisibilityTags(testSuiteSpan)
|
|
293
|
+
formattedSpan.meta = {
|
|
294
|
+
...formattedSpan.meta,
|
|
295
|
+
...suiteTags
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return formattedSpan
|
|
299
|
+
})
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
formattedTraces.forEach(trace => {
|
|
303
|
+
this.tracer._exporter.export(trace)
|
|
304
|
+
})
|
|
305
|
+
})
|
|
237
306
|
}
|
|
238
307
|
|
|
239
308
|
startTestSpan (testInfo) {
|
|
@@ -242,7 +311,8 @@ class MochaPlugin extends CiPlugin {
|
|
|
242
311
|
title,
|
|
243
312
|
isNew,
|
|
244
313
|
isEfdRetry,
|
|
245
|
-
testStartLine
|
|
314
|
+
testStartLine,
|
|
315
|
+
isParallel
|
|
246
316
|
} = testInfo
|
|
247
317
|
|
|
248
318
|
const testName = removeEfdStringFromTestName(testInfo.testName)
|
|
@@ -257,8 +327,12 @@ class MochaPlugin extends CiPlugin {
|
|
|
257
327
|
extraTags[TEST_SOURCE_START] = testStartLine
|
|
258
328
|
}
|
|
259
329
|
|
|
330
|
+
if (isParallel) {
|
|
331
|
+
extraTags[MOCHA_IS_PARALLEL] = 'true'
|
|
332
|
+
}
|
|
333
|
+
|
|
260
334
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
261
|
-
const testSuiteSpan = this._testSuites.get(
|
|
335
|
+
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
262
336
|
|
|
263
337
|
if (this.repositoryRoot !== this.sourceRoot && !!this.repositoryRoot) {
|
|
264
338
|
extraTags[TEST_SOURCE_FILE] = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
@@ -6,7 +6,7 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
|
6
6
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
7
7
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
8
8
|
|
|
9
|
-
const errorPages = ['/404', '/500', '/_error', '/_not-found']
|
|
9
|
+
const errorPages = ['/404', '/500', '/_error', '/_not-found', '/_not-found/page']
|
|
10
10
|
|
|
11
11
|
class NextPlugin extends ServerPlugin {
|
|
12
12
|
static get id () {
|
|
@@ -120,7 +120,6 @@ class NextPlugin extends ServerPlugin {
|
|
|
120
120
|
'resource.name': `${req.method} ${page}`.trim(),
|
|
121
121
|
'next.page': page
|
|
122
122
|
})
|
|
123
|
-
|
|
124
123
|
web.setRoute(req, page)
|
|
125
124
|
}
|
|
126
125
|
|