dd-trace 6.0.0 → 6.1.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 +1 -2
- package/index.d.ts +21 -1
- package/package.json +32 -29
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +4 -5
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +28 -1
- package/packages/datadog-instrumentations/src/vitest-util.js +5 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +104 -34
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +14 -23
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +351 -11
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-graphql/src/execute.js +21 -10
- package/packages/datadog-plugin-graphql/src/index.js +17 -1
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +30 -14
- package/packages/datadog-plugin-mocha/src/index.js +39 -14
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-playwright/src/index.js +3 -2
- package/packages/datadog-plugin-vitest/src/index.js +51 -9
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/appsec/graphql.js +9 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -10
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +75 -7
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +37 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugin_manager.js +16 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +147 -7
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
|
@@ -241,7 +241,7 @@ class GraphQLExecutePlugin extends TracingPlugin {
|
|
|
241
241
|
if (res?.errors?.length) {
|
|
242
242
|
span.setTag('error', res.errors[0])
|
|
243
243
|
for (const err of res.errors) {
|
|
244
|
-
extractErrorIntoSpanEvent(this.
|
|
244
|
+
extractErrorIntoSpanEvent(this.config, span, err)
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -271,6 +271,9 @@ class GraphQLExecutePlugin extends TracingPlugin {
|
|
|
271
271
|
const loc = this.config.source && document && fieldNode && fieldNode.loc
|
|
272
272
|
const source = loc && document.slice(loc.start, loc.end)
|
|
273
273
|
|
|
274
|
+
// ctx form: startSpan sets field.currentStore = { ...activeStore, span }
|
|
275
|
+
// without entering it. Only the field's first resolver call runs in that
|
|
276
|
+
// store (isFirst check in wrapResolve); siblings use field.parentStore.
|
|
274
277
|
const span = this.startSpan('graphql.resolve', {
|
|
275
278
|
service: this.config.service,
|
|
276
279
|
resource: `${fieldName}:${returnType}`,
|
|
@@ -283,7 +286,9 @@ class GraphQLExecutePlugin extends TracingPlugin {
|
|
|
283
286
|
'graphql.field.type': baseTypeName,
|
|
284
287
|
'graphql.source': source,
|
|
285
288
|
},
|
|
286
|
-
},
|
|
289
|
+
}, field)
|
|
290
|
+
|
|
291
|
+
field.span = span
|
|
287
292
|
|
|
288
293
|
if (fieldNode && this.config.variables && fieldNode.arguments) {
|
|
289
294
|
const variables = this.config.variables(variableValues)
|
|
@@ -388,15 +393,22 @@ function wrapResolve (resolve) {
|
|
|
388
393
|
pathString,
|
|
389
394
|
collapsedKey: collapsedKey ?? pathString,
|
|
390
395
|
span: null,
|
|
396
|
+
// Set by startResolveSpan; currentStore is used by the first resolver
|
|
397
|
+
// call only, siblings use parentStore (see the isFirst check below).
|
|
398
|
+
parentStore: null,
|
|
399
|
+
currentStore: null,
|
|
391
400
|
}
|
|
392
401
|
rootCtx.fields.set(fieldKey, field)
|
|
393
402
|
}
|
|
394
403
|
|
|
395
404
|
// Collapsed siblings still publish updateField (master's contract: one
|
|
396
405
|
// publish per resolver call, even when the span is collapsed) and route
|
|
397
|
-
// through callInAsyncScope so the abort signal stops them mid-flight.
|
|
406
|
+
// through callInAsyncScope so the abort signal stops them mid-flight. They
|
|
407
|
+
// run in the parent store, not field.currentStore: the first sibling's
|
|
408
|
+
// synchronous resolver already finished the shared graphql.resolve span, so
|
|
409
|
+
// re-entering its store would parent user spans to a closed span.
|
|
398
410
|
if (!isFirst) {
|
|
399
|
-
return callInAsyncScope(resolve, this, arguments, rootCtx.abortController, (err) => {
|
|
411
|
+
return callInAsyncScope(resolve, this, arguments, rootCtx.abortController, field.parentStore, (err) => {
|
|
400
412
|
if (updateFieldCh.hasSubscribers) {
|
|
401
413
|
updateFieldCh.publish({ rootCtx, field, error: err, pathString: field.pathString })
|
|
402
414
|
}
|
|
@@ -406,9 +418,8 @@ function wrapResolve (resolve) {
|
|
|
406
418
|
const executeSpan = rootCtx.executeSpan
|
|
407
419
|
const startTime = executeSpan._getTime()
|
|
408
420
|
const span = rootCtx.plugin.startResolveSpan(field, rootCtx, executeSpan, startTime)
|
|
409
|
-
field.span = span
|
|
410
421
|
|
|
411
|
-
return callInAsyncScope(resolve, this, arguments, rootCtx.abortController, (err, res) => {
|
|
422
|
+
return callInAsyncScope(resolve, this, arguments, rootCtx.abortController, field.currentStore, (err, res) => {
|
|
412
423
|
const endTime = executeSpan._getTime()
|
|
413
424
|
rootCtx.plugin.finishResolveSpan(span, field, err, res, endTime || startTime)
|
|
414
425
|
if (updateFieldCh.hasSubscribers) {
|
|
@@ -446,16 +457,16 @@ function wrapFieldType (field) {
|
|
|
446
457
|
wrapFields(unwrapped)
|
|
447
458
|
}
|
|
448
459
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
460
|
+
// Runs the resolver inside `store`, including any code after an internal
|
|
461
|
+
// `await`. A `.then()` the caller attaches afterward runs outside `store`.
|
|
462
|
+
function callInAsyncScope (fn, thisArg, args, abortController, store, cb) {
|
|
452
463
|
if (abortController?.signal.aborted) {
|
|
453
464
|
cb(null, null)
|
|
454
465
|
throw new AbortError('Aborted')
|
|
455
466
|
}
|
|
456
467
|
|
|
457
468
|
try {
|
|
458
|
-
const result = fn.apply(thisArg, args)
|
|
469
|
+
const result = legacyStorage.run(store, () => fn.apply(thisArg, args))
|
|
459
470
|
if (typeof result?.then === 'function') {
|
|
460
471
|
return result.then(
|
|
461
472
|
res => { cb(null, res); return res },
|
|
@@ -30,6 +30,13 @@ class GraphQLPlugin extends CompositePlugin {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// config validator helpers
|
|
33
|
+
//
|
|
34
|
+
// `collapse`, `depth`, `variables`, and `errorExtensions` arrive pre-merged on
|
|
35
|
+
// `config`: plugin_manager seeds the `DD_TRACE_GRAPHQL_*` env values (already
|
|
36
|
+
// parsed to their declared type) as the base and a programmatic
|
|
37
|
+
// `tracer.use('graphql', …)` value overrides them. So these helpers only shape
|
|
38
|
+
// the merged value — coerce, validate, and turn `variables` into a filter — and
|
|
39
|
+
// never read the environment themselves.
|
|
33
40
|
|
|
34
41
|
function validateConfig (config) {
|
|
35
42
|
const collapse = config.collapse === undefined || !!config.collapse
|
|
@@ -41,6 +48,7 @@ function validateConfig (config) {
|
|
|
41
48
|
// v5 counted collapsed list indices toward `depth`, so the same query reached a
|
|
42
49
|
// different depth depending on `collapse`. v6 counts selection-set depth only.
|
|
43
50
|
countListIndices: DD_MAJOR < 6 && collapse,
|
|
51
|
+
errorExtensions: getErrorExtensions(config),
|
|
44
52
|
hooks: getHooks(config),
|
|
45
53
|
}
|
|
46
54
|
}
|
|
@@ -58,13 +66,21 @@ function getVariablesFilter (config) {
|
|
|
58
66
|
if (typeof config.variables === 'function') {
|
|
59
67
|
return config.variables
|
|
60
68
|
} else if (Array.isArray(config.variables)) {
|
|
61
|
-
return variables => pick(variables, config.variables)
|
|
69
|
+
return config.variables.length > 0 ? variables => pick(variables, config.variables) : null
|
|
62
70
|
} else if (config.hasOwnProperty('variables')) {
|
|
63
71
|
log.error('Expected `variables` to be an array or function.')
|
|
64
72
|
}
|
|
65
73
|
return null
|
|
66
74
|
}
|
|
67
75
|
|
|
76
|
+
function getErrorExtensions (config) {
|
|
77
|
+
if (Array.isArray(config.errorExtensions)) {
|
|
78
|
+
return config.errorExtensions.length > 0 ? config.errorExtensions : undefined
|
|
79
|
+
} else if (config.hasOwnProperty('errorExtensions')) {
|
|
80
|
+
log.error('Expected `errorExtensions` to be an array.')
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
68
84
|
const noop = () => {}
|
|
69
85
|
const noopHooks = { execute: noop, parse: noop, validate: noop, resolve: undefined }
|
|
70
86
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @param {{ errorExtensions?: string[] }} config Resolved plugin config; `errorExtensions` lists the
|
|
5
|
+
* GraphQL error `extensions` keys to copy onto the span event.
|
|
6
|
+
* @param {import('../../dd-trace/src/opentracing/span')} span
|
|
7
|
+
* @param {{ name?: string, message?: string, stack?: string, locations?: Array<{ line: number, column: number }>,
|
|
8
|
+
* path?: Array<string|number>, extensions?: Record<string, unknown> }} exc
|
|
9
|
+
*/
|
|
3
10
|
function extractErrorIntoSpanEvent (config, span, exc) {
|
|
4
11
|
const attributes = {}
|
|
5
12
|
|
|
@@ -29,8 +36,8 @@ function extractErrorIntoSpanEvent (config, span, exc) {
|
|
|
29
36
|
attributes.message = exc.message
|
|
30
37
|
}
|
|
31
38
|
|
|
32
|
-
if (config.
|
|
33
|
-
for (const ext of config.
|
|
39
|
+
if (config.errorExtensions) {
|
|
40
|
+
for (const ext of config.errorExtensions) {
|
|
34
41
|
if (exc.extensions?.[ext]) {
|
|
35
42
|
const value = exc.extensions[ext]
|
|
36
43
|
|
|
@@ -38,7 +38,7 @@ class GraphQLValidatePlugin extends TracingPlugin {
|
|
|
38
38
|
if (errors?.length) {
|
|
39
39
|
span.setTag('error', errors[0])
|
|
40
40
|
for (const err of errors) {
|
|
41
|
-
extractErrorIntoSpanEvent(this.
|
|
41
|
+
extractErrorIntoSpanEvent(this.config, span, err)
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -61,15 +61,6 @@ const isJestWorker = !!getEnvironmentVariable('JEST_WORKER_ID')
|
|
|
61
61
|
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
62
62
|
const CHILD_MESSAGE_END = 2
|
|
63
63
|
|
|
64
|
-
function withTimeout (promise, timeoutMs) {
|
|
65
|
-
return new Promise(resolve => {
|
|
66
|
-
realSetTimeout(resolve, timeoutMs)
|
|
67
|
-
|
|
68
|
-
// Also resolve if the original promise resolves
|
|
69
|
-
promise.then(resolve)
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
class JestPlugin extends CiPlugin {
|
|
74
65
|
static id = 'jest'
|
|
75
66
|
|
|
@@ -451,6 +442,7 @@ class JestPlugin extends CiPlugin {
|
|
|
451
442
|
isAtrRetry,
|
|
452
443
|
finalStatus,
|
|
453
444
|
earlyFlakeAbortReason,
|
|
445
|
+
promises,
|
|
454
446
|
}) => {
|
|
455
447
|
span.setTag(TEST_STATUS, status)
|
|
456
448
|
if (finalStatus) {
|
|
@@ -481,12 +473,33 @@ class JestPlugin extends CiPlugin {
|
|
|
481
473
|
this.getTestTelemetryTags(span)
|
|
482
474
|
)
|
|
483
475
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
476
|
+
const finish = () => {
|
|
477
|
+
span.finish()
|
|
478
|
+
finishAllTraceSpans(span)
|
|
479
|
+
this.activeTestSpan = null
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (finalStatus) {
|
|
483
|
+
if (promises?.hitBreakpointPromise) {
|
|
484
|
+
finish()
|
|
485
|
+
this.cancelDiBreakpointHitWait()
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
if (promises && this.diBreakpointHitPromise) {
|
|
489
|
+
promises.hitBreakpointPromise = this.waitForPreparedDiBreakpointHit().then(finish)
|
|
490
|
+
return
|
|
491
|
+
}
|
|
492
|
+
finish()
|
|
493
|
+
this.cancelDiBreakpointHitWait()
|
|
494
|
+
return
|
|
495
|
+
}
|
|
496
|
+
finish()
|
|
497
|
+
if (status === 'fail' && promises?.hitBreakpointPromise) {
|
|
498
|
+
this.prepareDiBreakpointHitWait()
|
|
499
|
+
}
|
|
487
500
|
})
|
|
488
501
|
|
|
489
|
-
this.addSub('ci:jest:test:err', ({ span, error, shouldSetProbe, promises }) => {
|
|
502
|
+
this.addSub('ci:jest:test:err', ({ span, error, shouldSetProbe, shouldWaitForHitProbe, promises }) => {
|
|
490
503
|
if (error && span) {
|
|
491
504
|
span.setTag(TEST_STATUS, 'fail')
|
|
492
505
|
span.setTag('error', getFormattedError(error, this.repositoryRoot))
|
|
@@ -494,8 +507,11 @@ class JestPlugin extends CiPlugin {
|
|
|
494
507
|
const probeInformation = this.addDiProbe(error)
|
|
495
508
|
if (probeInformation) {
|
|
496
509
|
const { setProbePromise } = probeInformation
|
|
497
|
-
|
|
510
|
+
this.prepareDiBreakpointHitWait()
|
|
511
|
+
promises.isProbeReady = this.waitForDiOperation(setProbePromise)
|
|
498
512
|
}
|
|
513
|
+
} else if (shouldWaitForHitProbe && this.di) {
|
|
514
|
+
promises.hitBreakpointPromise = this.waitForDiBreakpointHits()
|
|
499
515
|
}
|
|
500
516
|
}
|
|
501
517
|
})
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
// Capture real Date.now at module load time, before any test can install fake timers.
|
|
4
|
-
const realDateNow = Date.now.bind(Date)
|
|
5
|
-
|
|
6
3
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
7
4
|
const { storage } = require('../../datadog-core')
|
|
8
5
|
|
|
@@ -52,8 +49,6 @@ const {
|
|
|
52
49
|
TELEMETRY_TEST_SESSION,
|
|
53
50
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
54
51
|
|
|
55
|
-
const BREAKPOINT_SET_GRACE_PERIOD_MS = 200
|
|
56
|
-
|
|
57
52
|
class MochaPlugin extends CiPlugin {
|
|
58
53
|
static id = 'mocha'
|
|
59
54
|
|
|
@@ -263,6 +258,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
263
258
|
span.finish()
|
|
264
259
|
finishAllTraceSpans(span)
|
|
265
260
|
this.activeTestSpan = null
|
|
261
|
+
this.cancelDiBreakpointHitWait()
|
|
266
262
|
if (this.di && this.libraryConfig?.isDiEnabled && this.runningTestProbe && isLastRetry) {
|
|
267
263
|
this.removeDiProbe(this.runningTestProbe)
|
|
268
264
|
this.runningTestProbe = null
|
|
@@ -307,8 +303,24 @@ class MochaPlugin extends CiPlugin {
|
|
|
307
303
|
return ctx.currentStore
|
|
308
304
|
})
|
|
309
305
|
|
|
310
|
-
this.addSub('ci:mocha:test:retry', ({
|
|
306
|
+
this.addSub('ci:mocha:test:retry', ({
|
|
307
|
+
span,
|
|
308
|
+
isFirstAttempt,
|
|
309
|
+
willBeRetried,
|
|
310
|
+
err,
|
|
311
|
+
test,
|
|
312
|
+
isAtrRetry,
|
|
313
|
+
promises,
|
|
314
|
+
}) => {
|
|
311
315
|
if (span) {
|
|
316
|
+
const finishSpan = () => {
|
|
317
|
+
span.finish()
|
|
318
|
+
finishAllTraceSpans(span)
|
|
319
|
+
if (this.activeTestSpan === span) {
|
|
320
|
+
this.activeTestSpan = null
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
312
324
|
span.setTag(TEST_STATUS, 'fail')
|
|
313
325
|
if (!isFirstAttempt) {
|
|
314
326
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
@@ -330,21 +342,34 @@ class MochaPlugin extends CiPlugin {
|
|
|
330
342
|
if (isFirstAttempt && willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
331
343
|
const probeInformation = this.addDiProbe(err)
|
|
332
344
|
if (probeInformation) {
|
|
333
|
-
const { file, line, stackIndex } = probeInformation
|
|
345
|
+
const { file, line, stackIndex, setProbePromise } = probeInformation
|
|
334
346
|
this.runningTestProbe = { file, line }
|
|
335
347
|
this.testErrorStackIndex = stackIndex
|
|
336
348
|
test._ddShouldWaitForHitProbe = true
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
// However, Mocha doesn't have a mechanism for waiting asyncrounously here, so for now, we'll have to
|
|
341
|
-
// fall back to a fixed syncronous delay.
|
|
349
|
+
this.prepareDiBreakpointHitWait()
|
|
350
|
+
if (promises) {
|
|
351
|
+
promises.setProbePromise = this.waitForDiOperation(setProbePromise)
|
|
342
352
|
}
|
|
343
353
|
}
|
|
344
354
|
}
|
|
345
355
|
|
|
346
|
-
|
|
347
|
-
|
|
356
|
+
if (!isFirstAttempt &&
|
|
357
|
+
willBeRetried &&
|
|
358
|
+
this.di &&
|
|
359
|
+
this.libraryConfig?.isDiEnabled &&
|
|
360
|
+
this.runningTestProbe &&
|
|
361
|
+
promises) {
|
|
362
|
+
promises.finishTestPromise = this.waitForInFlightDiBreakpointHits().then(finishSpan, finishSpan)
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
finishSpan()
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
this.addSub('ci:mocha:test:di:wait', ({ promises }) => {
|
|
371
|
+
if (this.di) {
|
|
372
|
+
promises.hitBreakpointPromise = this.waitForDiBreakpointHits()
|
|
348
373
|
}
|
|
349
374
|
})
|
|
350
375
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const MongodbCoreQueryPlugin = require('./query')
|
|
4
|
+
|
|
5
|
+
const MAX_RESOURCE_LENGTH = 10_000
|
|
6
|
+
|
|
7
|
+
class MongodbCoreBulkWritePlugin extends MongodbCoreQueryPlugin {
|
|
8
|
+
// bulkWrite is higher-level than the wire commands `query` traces, so it has its own operation
|
|
9
|
+
// and thus its own `apm:mongodb:bulkwrite:*` channels. It reuses the query plugin's `id`,
|
|
10
|
+
// `component`, and collection-stripping `getPeerService`, and inherits `finish`, `error`, and the
|
|
11
|
+
// finish-time parent-store restore from the tracing/outbound base classes.
|
|
12
|
+
static operation = 'bulkwrite'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Open the parent span for a `Collection#bulkWrite`. The per-type wire commands nest as
|
|
16
|
+
* children and carry the statements, host, and DBM comment, so this span only records
|
|
17
|
+
* the namespace and resource.
|
|
18
|
+
*
|
|
19
|
+
* @param {{ ns: string }} ctx
|
|
20
|
+
*/
|
|
21
|
+
bindStart (ctx) {
|
|
22
|
+
const { ns } = ctx
|
|
23
|
+
const serviceResult = this.serviceName({ pluginConfig: this.config })
|
|
24
|
+
|
|
25
|
+
this.startSpan(this.operationName(), {
|
|
26
|
+
service: serviceResult,
|
|
27
|
+
resource: truncate(`bulkWrite ${ns}`),
|
|
28
|
+
type: 'mongodb',
|
|
29
|
+
kind: 'client',
|
|
30
|
+
meta: {
|
|
31
|
+
'db.name': ns,
|
|
32
|
+
},
|
|
33
|
+
}, ctx)
|
|
34
|
+
|
|
35
|
+
return ctx.currentStore
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function truncate (input) {
|
|
40
|
+
return input.length > MAX_RESOURCE_LENGTH ? input.slice(0, MAX_RESOURCE_LENGTH) : input
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = MongodbCoreBulkWritePlugin
|