dd-trace 5.110.0 → 5.112.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/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +34 -1
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +37 -34
- 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/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- 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/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -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 +13 -2
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- 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/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- 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 +15 -24
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +639 -12
- package/packages/datadog-plugin-graphql/src/index.js +37 -9
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +17 -6
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +31 -15
- package/packages/datadog-plugin-mocha/src/index.js +40 -15
- 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-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +4 -3
- package/packages/datadog-plugin-vitest/src/index.js +120 -72
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- 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/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +14 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- 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/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +127 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- 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/base.js +4 -4
- 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/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +23 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +15 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// Capture real timers at module load time, before any test can install fake timers.
|
|
4
|
+
const realSetTimeout = setTimeout
|
|
5
|
+
const realClearTimeout = clearTimeout
|
|
6
|
+
|
|
7
|
+
const { threadId } = require('node:worker_threads')
|
|
8
|
+
|
|
3
9
|
const { storage } = require('../../../datadog-core')
|
|
4
10
|
const { COMPONENT } = require('../constants')
|
|
5
11
|
const log = require('../log')
|
|
@@ -12,6 +18,7 @@ const {
|
|
|
12
18
|
} = require('../ci-visibility/telemetry')
|
|
13
19
|
const getDiClient = require('../ci-visibility/dynamic-instrumentation')
|
|
14
20
|
const { DD_MAJOR } = require('../../../../version')
|
|
21
|
+
const { version: tracerVersion } = require('../../../../package.json')
|
|
15
22
|
const id = require('../id')
|
|
16
23
|
const { OS_VERSION, OS_PLATFORM, OS_ARCHITECTURE, RUNTIME_NAME, RUNTIME_VERSION } = require('./util/env')
|
|
17
24
|
const {
|
|
@@ -61,7 +68,7 @@ const {
|
|
|
61
68
|
DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX,
|
|
62
69
|
DI_DEBUG_ERROR_FILE_SUFFIX,
|
|
63
70
|
DI_DEBUG_ERROR_LINE_SUFFIX,
|
|
64
|
-
getLibraryCapabilitiesTags,
|
|
71
|
+
getLibraryCapabilitiesTags: getDefaultLibraryCapabilitiesTags,
|
|
65
72
|
getPullRequestDiff,
|
|
66
73
|
getModifiedFilesFromDiff,
|
|
67
74
|
getPullRequestBaseBranch,
|
|
@@ -84,6 +91,9 @@ const {
|
|
|
84
91
|
} = require('./util/test')
|
|
85
92
|
|
|
86
93
|
const legacyStorage = storage('legacy')
|
|
94
|
+
const DI_OPERATION_TIMEOUT_MS = 2000
|
|
95
|
+
const DI_LOGGER_THREAD_ID = threadId === 0 ? `pid:${process.pid}` : `pid:${process.pid};tid:${threadId}`
|
|
96
|
+
const DI_LOGGER_THREAD_NAME = threadId === 0 ? 'MainThread' : `WorkerThread:${threadId}`
|
|
87
97
|
|
|
88
98
|
const FRAMEWORK_TO_TRIMMED_COMMAND = {
|
|
89
99
|
vitest: 'vitest run',
|
|
@@ -108,8 +118,20 @@ const TEST_FRAMEWORKS_TO_SKIP_GIT_METADATA_EXTRACTION = new Set([
|
|
|
108
118
|
'cucumber',
|
|
109
119
|
])
|
|
110
120
|
|
|
121
|
+
function withTimeout (promise, timeoutMs) {
|
|
122
|
+
return new Promise(resolve => {
|
|
123
|
+
const timeoutId = realSetTimeout(resolve, timeoutMs)
|
|
124
|
+
|
|
125
|
+
const done = () => {
|
|
126
|
+
realClearTimeout(timeoutId)
|
|
127
|
+
resolve()
|
|
128
|
+
}
|
|
129
|
+
promise.then(done, done)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
111
133
|
function setItrSkippingEnabledTagFromLibraryConfig (plugin, frameworkVersion) {
|
|
112
|
-
const libraryCapabilitiesTags =
|
|
134
|
+
const libraryCapabilitiesTags = getDefaultLibraryCapabilitiesTags(plugin.constructor.id, frameworkVersion)
|
|
113
135
|
|
|
114
136
|
if (!libraryCapabilitiesTags[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS] ||
|
|
115
137
|
!plugin.libraryConfig ||
|
|
@@ -143,6 +165,8 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
143
165
|
super(...args)
|
|
144
166
|
|
|
145
167
|
this.fileLineToProbeId = new Map()
|
|
168
|
+
this.diBreakpointHitPromise = undefined
|
|
169
|
+
this.diBreakpointHitResolvers = []
|
|
146
170
|
this.rootDir = process.cwd() // fallback in case :session:start events are not emitted
|
|
147
171
|
this._testSuiteSpansByTestSuite = new Map()
|
|
148
172
|
this._pendingWorkerTracesByTestSuite = new Map()
|
|
@@ -164,18 +188,20 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
164
188
|
setItrSkippingEnabledTagFromLibraryConfig(this, frameworkVersion)
|
|
165
189
|
}
|
|
166
190
|
|
|
167
|
-
const
|
|
168
|
-
? getSessionRequestErrorTags(this.testSessionSpan)
|
|
169
|
-
: Object.fromEntries(this._pendingRequestErrorTags.map(({ tag, value }) => [tag, value]))
|
|
170
|
-
|
|
171
|
-
const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id, frameworkVersion)
|
|
191
|
+
const libraryCapabilitiesTags = this.getLibraryCapabilitiesTags(frameworkVersion, ctx)
|
|
172
192
|
const metadataTags = {
|
|
173
193
|
test: {
|
|
174
194
|
...libraryCapabilitiesTags,
|
|
175
195
|
},
|
|
176
196
|
}
|
|
177
197
|
this.tracer._exporter.addMetadataTags(metadataTags)
|
|
178
|
-
onDone({
|
|
198
|
+
onDone({
|
|
199
|
+
err,
|
|
200
|
+
isTestDynamicInstrumentationEnabled: this.config.isTestDynamicInstrumentationEnabled,
|
|
201
|
+
libraryConfig,
|
|
202
|
+
repositoryRoot: this.repositoryRoot,
|
|
203
|
+
requestErrorTags: this._getCurrentRequestErrorTags(),
|
|
204
|
+
})
|
|
179
205
|
})
|
|
180
206
|
})
|
|
181
207
|
|
|
@@ -307,7 +333,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
307
333
|
this.libraryConfig.isKnownTestsEnabled = false
|
|
308
334
|
}
|
|
309
335
|
}
|
|
310
|
-
onDone({ err, knownTests })
|
|
336
|
+
onDone({ err, knownTests, requestErrorTags: this._getCurrentRequestErrorTags() })
|
|
311
337
|
})
|
|
312
338
|
})
|
|
313
339
|
|
|
@@ -327,7 +353,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
327
353
|
this.libraryConfig.isTestManagementEnabled = false
|
|
328
354
|
}
|
|
329
355
|
}
|
|
330
|
-
onDone({ err, testManagementTests })
|
|
356
|
+
onDone({ err, testManagementTests, requestErrorTags: this._getCurrentRequestErrorTags() })
|
|
331
357
|
})
|
|
332
358
|
})
|
|
333
359
|
|
|
@@ -431,6 +457,15 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
431
457
|
}
|
|
432
458
|
}
|
|
433
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Returns library capability metadata tags for this test framework.
|
|
462
|
+
* @param {string} frameworkVersion - The test framework version.
|
|
463
|
+
* @returns {Record<string, string|undefined>}
|
|
464
|
+
*/
|
|
465
|
+
getLibraryCapabilitiesTags (frameworkVersion) {
|
|
466
|
+
return getDefaultLibraryCapabilitiesTags(this.constructor.id, frameworkVersion)
|
|
467
|
+
}
|
|
468
|
+
|
|
434
469
|
/**
|
|
435
470
|
* Adds a hidden _dd tag to the test session span when a test-optimization request fails.
|
|
436
471
|
* If the session span does not exist yet (e.g. library-configuration failed before session:start),
|
|
@@ -450,6 +485,18 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
450
485
|
}
|
|
451
486
|
}
|
|
452
487
|
|
|
488
|
+
/**
|
|
489
|
+
* Returns the current request error tags, including tags queued before session creation.
|
|
490
|
+
*
|
|
491
|
+
* @returns {Record<string, string>}
|
|
492
|
+
*/
|
|
493
|
+
_getCurrentRequestErrorTags () {
|
|
494
|
+
if (this.testSessionSpan) {
|
|
495
|
+
return getSessionRequestErrorTags(this.testSessionSpan)
|
|
496
|
+
}
|
|
497
|
+
return Object.fromEntries(this._pendingRequestErrorTags.map(({ tag, value }) => [tag, value]))
|
|
498
|
+
}
|
|
499
|
+
|
|
453
500
|
/**
|
|
454
501
|
* Updates repository-root-dependent state when a worker receives the root from
|
|
455
502
|
* the coordinator process after plugin configuration.
|
|
@@ -767,6 +814,11 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
767
814
|
}
|
|
768
815
|
|
|
769
816
|
onDiBreakpointHit ({ snapshot }) {
|
|
817
|
+
for (const resolve of this.diBreakpointHitResolvers) {
|
|
818
|
+
resolve()
|
|
819
|
+
}
|
|
820
|
+
this.diBreakpointHitResolvers.length = 0
|
|
821
|
+
|
|
770
822
|
if (!this.activeTestSpan || this.activeTestSpan.context()._isFinished) {
|
|
771
823
|
// This is unexpected and is caused by a race condition.
|
|
772
824
|
log.warn('Breakpoint snapshot could not be attached to the active test span')
|
|
@@ -790,8 +842,17 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
790
842
|
)
|
|
791
843
|
|
|
792
844
|
const activeTestSpanContext = this.activeTestSpan.context()
|
|
845
|
+
const topStackFrame = snapshot.stack?.[0]
|
|
793
846
|
|
|
794
847
|
this.tracer._exporter.exportDiLogs(this.testEnvironmentMetadata, {
|
|
848
|
+
message: '',
|
|
849
|
+
logger: {
|
|
850
|
+
name: snapshot.probe.location.file,
|
|
851
|
+
method: topStackFrame?.function || '',
|
|
852
|
+
version: tracerVersion,
|
|
853
|
+
thread_id: DI_LOGGER_THREAD_ID,
|
|
854
|
+
thread_name: DI_LOGGER_THREAD_NAME,
|
|
855
|
+
},
|
|
795
856
|
debugger: { snapshot },
|
|
796
857
|
dd: {
|
|
797
858
|
trace_id: activeTestSpanContext.toTraceId(),
|
|
@@ -800,6 +861,94 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
800
861
|
})
|
|
801
862
|
}
|
|
802
863
|
|
|
864
|
+
/**
|
|
865
|
+
* Wait for a Dynamic Instrumentation operation without blocking test framework progress forever.
|
|
866
|
+
*
|
|
867
|
+
* @param {Promise<void>} promise - Dynamic Instrumentation operation promise.
|
|
868
|
+
* @returns {Promise<void>}
|
|
869
|
+
*/
|
|
870
|
+
waitForDiOperation (promise) {
|
|
871
|
+
return withTimeout(promise, DI_OPERATION_TIMEOUT_MS)
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Resolve any prepared breakpoint-hit wait when no caller still needs it.
|
|
876
|
+
*/
|
|
877
|
+
cancelDiBreakpointHitWait () {
|
|
878
|
+
for (const resolve of this.diBreakpointHitResolvers) {
|
|
879
|
+
resolve()
|
|
880
|
+
}
|
|
881
|
+
this.diBreakpointHitResolvers.length = 0
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Wait for a prepared breakpoint hit before resolving any unused waiters.
|
|
886
|
+
*
|
|
887
|
+
* @returns {Promise<void>}
|
|
888
|
+
*/
|
|
889
|
+
waitForPreparedDiBreakpointHit () {
|
|
890
|
+
if (!this.diBreakpointHitPromise) {
|
|
891
|
+
this.cancelDiBreakpointHitWait()
|
|
892
|
+
return Promise.resolve()
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return this.waitForDiOperation(this.diBreakpointHitPromise).then(
|
|
896
|
+
() => this.cancelDiBreakpointHitWait(),
|
|
897
|
+
() => this.cancelDiBreakpointHitWait()
|
|
898
|
+
)
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Prepare a wait for the next breakpoint hit before the retried test starts.
|
|
903
|
+
*
|
|
904
|
+
* @returns {Promise<void>}
|
|
905
|
+
*/
|
|
906
|
+
prepareDiBreakpointHitWait () {
|
|
907
|
+
if (!this.di) return Promise.resolve()
|
|
908
|
+
|
|
909
|
+
let resolveHit
|
|
910
|
+
const hitPromise = new Promise(resolve => {
|
|
911
|
+
resolveHit = resolve
|
|
912
|
+
this.diBreakpointHitResolvers.push(resolve)
|
|
913
|
+
})
|
|
914
|
+
|
|
915
|
+
const preparedPromise = hitPromise.finally(() => {
|
|
916
|
+
const resolverIndex = this.diBreakpointHitResolvers.indexOf(resolveHit)
|
|
917
|
+
if (resolverIndex !== -1) {
|
|
918
|
+
this.diBreakpointHitResolvers.splice(resolverIndex, 1)
|
|
919
|
+
}
|
|
920
|
+
if (this.diBreakpointHitPromise === preparedPromise) {
|
|
921
|
+
this.diBreakpointHitPromise = undefined
|
|
922
|
+
}
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
this.diBreakpointHitPromise = preparedPromise
|
|
926
|
+
return this.diBreakpointHitPromise
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Wait until the DI worker has posted any breakpoint hits it was already processing.
|
|
931
|
+
*
|
|
932
|
+
* @returns {Promise<void>}
|
|
933
|
+
*/
|
|
934
|
+
waitForDiBreakpointHits () {
|
|
935
|
+
if (!this.di) return Promise.resolve()
|
|
936
|
+
if (this.diBreakpointHitPromise) return this.waitForDiOperation(this.diBreakpointHitPromise)
|
|
937
|
+
|
|
938
|
+
return this.waitForInFlightDiBreakpointHits()
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Wait until the DI worker has posted breakpoint hits it was already processing.
|
|
943
|
+
*
|
|
944
|
+
* @returns {Promise<void>}
|
|
945
|
+
*/
|
|
946
|
+
waitForInFlightDiBreakpointHits () {
|
|
947
|
+
if (!this.di) return Promise.resolve()
|
|
948
|
+
|
|
949
|
+
return this.waitForDiOperation(this.di.waitForInFlightBreakpointHits())
|
|
950
|
+
}
|
|
951
|
+
|
|
803
952
|
removeAllDiProbes () {
|
|
804
953
|
if (this.fileLineToProbeId.size === 0) {
|
|
805
954
|
return Promise.resolve()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const plugins = {
|
|
4
|
+
get '@anthropic-ai/claude-agent-sdk' () { return require('../../../datadog-plugin-claude-agent-sdk/src') },
|
|
4
5
|
get '@anthropic-ai/sdk' () { return require('../../../datadog-plugin-anthropic/src') },
|
|
5
6
|
get '@apollo/gateway' () { return require('../../../datadog-plugin-apollo/src') },
|
|
6
7
|
get '@aws-sdk/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
|
|
@@ -46,6 +47,7 @@ const plugins = {
|
|
|
46
47
|
get bunyan () { return require('../../../datadog-plugin-bunyan/src') },
|
|
47
48
|
get 'cassandra-driver' () { return require('../../../datadog-plugin-cassandra-driver/src') },
|
|
48
49
|
get child_process () { return require('../../../datadog-plugin-child_process/src') },
|
|
50
|
+
get 'claude-agent-sdk' () { return require('../../../datadog-plugin-claude-agent-sdk/src') },
|
|
49
51
|
get connect () { return require('../../../datadog-plugin-connect/src') },
|
|
50
52
|
get couchbase () { return require('../../../datadog-plugin-couchbase/src') },
|
|
51
53
|
get cypress () { return require('../../../datadog-plugin-cypress/src') },
|
|
@@ -37,7 +37,12 @@ function isPromptCompletionSampled (sampler, span) {
|
|
|
37
37
|
|
|
38
38
|
module.exports = function makeUtilities (integrationName, tracerConfig) {
|
|
39
39
|
const integrationConfig = tracerConfig[integrationName] || {}
|
|
40
|
-
|
|
40
|
+
// The per-integration config is keyed by the canonical environment variable
|
|
41
|
+
// names (e.g. `DD_LANGCHAIN_SPAN_CHAR_LIMIT`), nested under the integration's
|
|
42
|
+
// namespace. Derive those leaf names from the integration name.
|
|
43
|
+
const prefix = `DD_${integrationName.toUpperCase()}_`
|
|
44
|
+
const spanCharLimit = integrationConfig[`${prefix}SPAN_CHAR_LIMIT`]
|
|
45
|
+
const spanPromptCompletionSampleRate = integrationConfig[`${prefix}SPAN_PROMPT_COMPLETION_SAMPLE_RATE`]
|
|
41
46
|
|
|
42
47
|
const sampler = new Sampler(spanPromptCompletionSampleRate ?? 1)
|
|
43
48
|
|
|
@@ -95,6 +95,8 @@ const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
|
|
|
95
95
|
const TEST_CODE_OWNERS = 'test.codeowners'
|
|
96
96
|
const TEST_SOURCE_FILE = 'test.source.file'
|
|
97
97
|
const TEST_SOURCE_START = 'test.source.start'
|
|
98
|
+
const TEST_FAILURE_SCREENSHOT_UPLOADED = 'test.failure_screenshot.uploaded'
|
|
99
|
+
const TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR = 'test.failure_screenshot.upload_error'
|
|
98
100
|
const LIBRARY_VERSION = 'library_version'
|
|
99
101
|
const TEST_COMMAND = 'test.command'
|
|
100
102
|
const TEST_MODULE = 'test.module'
|
|
@@ -112,6 +114,12 @@ const TEST_RETRY_REASON = 'test.retry_reason'
|
|
|
112
114
|
const TEST_HAS_FAILED_ALL_RETRIES = 'test.has_failed_all_retries'
|
|
113
115
|
const TEST_IS_MODIFIED = 'test.is_modified'
|
|
114
116
|
const TEST_HAS_DYNAMIC_NAME = '_dd.has_dynamic_name'
|
|
117
|
+
const EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS = [
|
|
118
|
+
{ limitMs: 5 * 1000, key: '5s' },
|
|
119
|
+
{ limitMs: 10 * 1000, key: '10s' },
|
|
120
|
+
{ limitMs: 30 * 1000, key: '30s' },
|
|
121
|
+
{ limitMs: 5 * 60 * 1000, key: '5m' },
|
|
122
|
+
]
|
|
115
123
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
116
124
|
|
|
117
125
|
// Matches patterns that are almost certainly runtime-generated values in test names:
|
|
@@ -427,6 +435,8 @@ module.exports = {
|
|
|
427
435
|
TEST_SKIP_REASON,
|
|
428
436
|
TEST_IS_RUM_ACTIVE,
|
|
429
437
|
TEST_SOURCE_FILE,
|
|
438
|
+
TEST_FAILURE_SCREENSHOT_UPLOADED,
|
|
439
|
+
TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR,
|
|
430
440
|
CI_APP_ORIGIN,
|
|
431
441
|
LIBRARY_VERSION,
|
|
432
442
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
@@ -493,6 +503,7 @@ module.exports = {
|
|
|
493
503
|
removeInvalidMetadata,
|
|
494
504
|
parseAnnotations,
|
|
495
505
|
getIsFaultyEarlyFlakeDetection,
|
|
506
|
+
EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
496
507
|
getEfdRetryCount,
|
|
497
508
|
getMaxEfdRetryCount,
|
|
498
509
|
TEST_BROWSER_DRIVER,
|
|
@@ -1447,13 +1458,7 @@ function parseAnnotations (annotations) {
|
|
|
1447
1458
|
* @returns {number}
|
|
1448
1459
|
*/
|
|
1449
1460
|
function getEfdRetryCount (durationMs, slowTestRetries) {
|
|
1450
|
-
const
|
|
1451
|
-
{ limitMs: 5 * 1000, key: '5s' },
|
|
1452
|
-
{ limitMs: 10 * 1000, key: '10s' },
|
|
1453
|
-
{ limitMs: 30 * 1000, key: '30s' },
|
|
1454
|
-
{ limitMs: 5 * 60 * 1000, key: '5m' },
|
|
1455
|
-
]
|
|
1456
|
-
for (const { limitMs, key } of thresholds) {
|
|
1461
|
+
for (const { limitMs, key } of EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS) {
|
|
1457
1462
|
if (durationMs < limitMs) {
|
|
1458
1463
|
return slowTestRetries[key] ?? 0
|
|
1459
1464
|
}
|
|
@@ -1616,7 +1621,7 @@ function isFailedTestReplaySupported (testFramework, frameworkVersion) {
|
|
|
1616
1621
|
: true
|
|
1617
1622
|
}
|
|
1618
1623
|
|
|
1619
|
-
function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
|
|
1624
|
+
function getLibraryCapabilitiesTags (testFramework, frameworkVersion, options = {}) {
|
|
1620
1625
|
return {
|
|
1621
1626
|
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework)
|
|
1622
1627
|
? '1'
|
|
@@ -1638,7 +1643,8 @@ function getLibraryCapabilitiesTags (testFramework, frameworkVersion) {
|
|
|
1638
1643
|
isAttemptToFixSupported(testFramework, frameworkVersion)
|
|
1639
1644
|
? '5'
|
|
1640
1645
|
: undefined,
|
|
1641
|
-
[DD_CAPABILITIES_FAILED_TEST_REPLAY]:
|
|
1646
|
+
[DD_CAPABILITIES_FAILED_TEST_REPLAY]: !options.omitFailedTestReplay &&
|
|
1647
|
+
isFailedTestReplaySupported(testFramework, frameworkVersion)
|
|
1642
1648
|
? '1'
|
|
1643
1649
|
: undefined,
|
|
1644
1650
|
}
|
|
@@ -18,7 +18,7 @@ function exporterFromURL (url) {
|
|
|
18
18
|
return new FileExporter({}, fileURLToPath(url))
|
|
19
19
|
}
|
|
20
20
|
// The subprocess has no tracer config, so mirror the canonical config property
|
|
21
|
-
// names the exporters read. Normalize the raw env to the parsed profiling.
|
|
21
|
+
// names the exporters read. Normalize the raw env to the parsed profiling.DD_PROFILING_ENABLED
|
|
22
22
|
// values ('true' | 'false' | 'auto') the main process would have produced.
|
|
23
23
|
const profilingEnabled = (getValueFromEnvSources('DD_PROFILING_ENABLED') ?? '').toLowerCase()
|
|
24
24
|
const enabled = profilingEnabled === 'auto'
|
|
@@ -28,7 +28,7 @@ function exporterFromURL (url) {
|
|
|
28
28
|
url,
|
|
29
29
|
DD_PROFILING_UPLOAD_TIMEOUT: timeoutMs,
|
|
30
30
|
DD_INJECTION_ENABLED: getValueFromEnvSources('DD_INJECTION_ENABLED'),
|
|
31
|
-
profiling: { enabled },
|
|
31
|
+
profiling: { DD_PROFILING_ENABLED: enabled },
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -42,6 +42,19 @@ function sendRequest (options, form, callback) {
|
|
|
42
42
|
storage('legacy').run({ noop: true }, () => {
|
|
43
43
|
requestCounter.inc()
|
|
44
44
|
const start = perf.now()
|
|
45
|
+
|
|
46
|
+
// Ensure the callback runs at most once per request. The 'timeout' handler
|
|
47
|
+
// below can fire after a response was already received (e.g. on a lingering
|
|
48
|
+
// keep-alive socket, once it goes idle); without this guard that would
|
|
49
|
+
// invoke the callback a second time and corrupt the retry accounting in
|
|
50
|
+
// AgentExporter.export.
|
|
51
|
+
let settled = false
|
|
52
|
+
const done = (err, res) => {
|
|
53
|
+
if (settled) return
|
|
54
|
+
settled = true
|
|
55
|
+
callback(err, res)
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
const req = request(options, res => {
|
|
46
59
|
durationDistribution.track(perf.now() - start)
|
|
47
60
|
countStatusCode(res.statusCode)
|
|
@@ -49,16 +62,28 @@ function sendRequest (options, form, callback) {
|
|
|
49
62
|
statusCodeErrorCounter.inc()
|
|
50
63
|
const error = new Error(`HTTP Error ${res.statusCode}`)
|
|
51
64
|
error.status = res.statusCode
|
|
52
|
-
|
|
65
|
+
done(error)
|
|
53
66
|
} else {
|
|
54
|
-
|
|
67
|
+
done(null, res)
|
|
55
68
|
}
|
|
56
69
|
})
|
|
57
70
|
|
|
58
71
|
req.on('error', (err) => {
|
|
59
72
|
networkErrorCounter.inc()
|
|
60
|
-
|
|
73
|
+
done(err)
|
|
61
74
|
})
|
|
75
|
+
|
|
76
|
+
// `options.timeout` sets the socket's idle timeout, which only emits a
|
|
77
|
+
// 'timeout' event — per the Node docs it does NOT abort the request. Without
|
|
78
|
+
// destroying the socket here, a stalled upload hangs indefinitely: no
|
|
79
|
+
// 'error' fires, so the retry/backoff in AgentExporter.export never runs and
|
|
80
|
+
// the export promise never settles. Destroying the request makes the
|
|
81
|
+
// 'error' handler above run, which drives the retry (see the trace exporter
|
|
82
|
+
// in ../common/request.js, which likewise aborts the request on timeout).
|
|
83
|
+
req.on('timeout', () => {
|
|
84
|
+
req.destroy(new Error(`Profiling agent export timed out after ${options.timeout}ms`))
|
|
85
|
+
})
|
|
86
|
+
|
|
62
87
|
if (form) {
|
|
63
88
|
sizeDistribution.track(form.size())
|
|
64
89
|
form.pipe(req)
|
|
@@ -11,10 +11,10 @@ const processTags = require('../../process-tags')
|
|
|
11
11
|
/** @typedef {import('../../config/config-base')} TracerConfig */
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Maps the canonical profiling.
|
|
14
|
+
* Maps the canonical profiling.DD_PROFILING_ENABLED value to the activation reported in the
|
|
15
15
|
* profile event.
|
|
16
16
|
*
|
|
17
|
-
* @param {string} [enabled] - config.profiling.
|
|
17
|
+
* @param {string} [enabled] - config.profiling.DD_PROFILING_ENABLED ('true' | 'false' | 'auto')
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
20
|
function getActivation (enabled) {
|
|
@@ -38,7 +38,7 @@ class EventSerializer {
|
|
|
38
38
|
this.#service = config.service
|
|
39
39
|
this.#appVersion = config.version
|
|
40
40
|
this.#libraryInjected = !!config.DD_INJECTION_ENABLED
|
|
41
|
-
this.#activation = getActivation(config.profiling?.
|
|
41
|
+
this.#activation = getActivation(config.profiling?.DD_PROFILING_ENABLED)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -148,7 +148,7 @@ class NativeWallProfiler {
|
|
|
148
148
|
this.#endpointCollectionEnabled = config.DD_PROFILING_ENDPOINT_COLLECTION_ENABLED
|
|
149
149
|
this.#flushIntervalMillis = flushInterval
|
|
150
150
|
this.#samplingIntervalMicros = SAMPLING_INTERVAL * 1000
|
|
151
|
-
this.#telemetryHeartbeatIntervalMillis = config.telemetry.
|
|
151
|
+
this.#telemetryHeartbeatIntervalMillis = config.telemetry.DD_TELEMETRY_HEARTBEAT_INTERVAL
|
|
152
152
|
this.#timelineEnabled = config.DD_PROFILING_TIMELINE_ENABLED
|
|
153
153
|
this.#v8ProfilerBugWorkaroundEnabled = config.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
|
|
154
154
|
|
|
@@ -138,7 +138,7 @@ class Tracer extends NoopProxy {
|
|
|
138
138
|
spanleak.startScrubber()
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
if (config.remoteConfig.
|
|
141
|
+
if (config.remoteConfig.DD_REMOTE_CONFIGURATION_ENABLED && !config.isCiVisibility) {
|
|
142
142
|
const RemoteConfig = require('./remote_config')
|
|
143
143
|
const rc = new RemoteConfig(config)
|
|
144
144
|
|
|
@@ -180,11 +180,11 @@ class Tracer extends NoopProxy {
|
|
|
180
180
|
openfeatureRemoteConfig.enable(rc, config, () => this.openfeature)
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
if (config.profiling.
|
|
183
|
+
if (config.profiling.DD_PROFILING_ENABLED === 'true') {
|
|
184
184
|
this._profilerStarted = this._startProfiler(config)
|
|
185
185
|
} else {
|
|
186
186
|
this._profilerStarted = false
|
|
187
|
-
if (config.profiling.
|
|
187
|
+
if (config.profiling.DD_PROFILING_ENABLED === 'auto') {
|
|
188
188
|
const { SSIHeuristics } = require('./profiling/ssi-heuristics')
|
|
189
189
|
const ssiHeuristics = new SSIHeuristics(config)
|
|
190
190
|
ssiHeuristics.start()
|
|
@@ -216,7 +216,7 @@ class Tracer extends NoopProxy {
|
|
|
216
216
|
|
|
217
217
|
this._modules.rewriter.enable(config)
|
|
218
218
|
|
|
219
|
-
if (config.
|
|
219
|
+
if (config.DD_TRACE_ENABLED && config.testOptimization.DD_CIVISIBILITY_MANUAL_API_ENABLED) {
|
|
220
220
|
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
221
221
|
this._testApiManualPlugin = new TestApiManualPlugin(this)
|
|
222
222
|
// `shouldGetEnvironmentData` is passed as false so that we only lazily calculate it
|
|
@@ -225,7 +225,7 @@ class Tracer extends NoopProxy {
|
|
|
225
225
|
this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
|
|
226
226
|
}
|
|
227
227
|
if (config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED) {
|
|
228
|
-
if (config.
|
|
228
|
+
if (config.DD_API_KEY) {
|
|
229
229
|
const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
|
|
230
230
|
const automaticLogPlugin = new LogSubmissionPlugin(this)
|
|
231
231
|
automaticLogPlugin.configure({ ...config, enabled: true })
|
|
@@ -237,7 +237,7 @@ class Tracer extends NoopProxy {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
if (config.
|
|
240
|
+
if (config.testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
|
|
241
241
|
const getDynamicInstrumentationClient = require('./ci-visibility/dynamic-instrumentation')
|
|
242
242
|
// We instantiate the client but do not start the Worker here. The worker is started lazily
|
|
243
243
|
getDynamicInstrumentationClient(config)
|
|
@@ -270,11 +270,11 @@ class Tracer extends NoopProxy {
|
|
|
270
270
|
* @param {import('./config/config-base')} config - Tracer configuration
|
|
271
271
|
*/
|
|
272
272
|
#updateTracing (config) {
|
|
273
|
-
if (config.
|
|
273
|
+
if (config.DD_TRACE_ENABLED !== false) {
|
|
274
274
|
if (config.appsec.enabled) {
|
|
275
275
|
this._modules.appsec.enable(config)
|
|
276
276
|
}
|
|
277
|
-
if (config.llmobs.
|
|
277
|
+
if (config.llmobs.DD_LLMOBS_ENABLED) {
|
|
278
278
|
this._modules.llmobs.enable(config)
|
|
279
279
|
}
|
|
280
280
|
if (!this._tracingInitialized) {
|
|
@@ -11,16 +11,15 @@ const startedSpans = new WeakSet()
|
|
|
11
11
|
const finishedSpans = new WeakSet()
|
|
12
12
|
|
|
13
13
|
class SpanProcessor {
|
|
14
|
-
constructor (exporter, prioritySampler, config) {
|
|
14
|
+
constructor (exporter, prioritySampler, config, otlpStatsExporter) {
|
|
15
15
|
this._exporter = exporter
|
|
16
16
|
this._prioritySampler = prioritySampler
|
|
17
17
|
this._config = config
|
|
18
18
|
this._killAll = false
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
if (config.stats?.enabled && !config.appsec?.standalone?.enabled) {
|
|
20
|
+
if (config.stats?.DD_TRACE_STATS_COMPUTATION_ENABLED && !config.appsec?.standalone?.enabled) {
|
|
22
21
|
const { SpanStatsProcessor } = require('./span_stats')
|
|
23
|
-
this._stats = new SpanStatsProcessor(config)
|
|
22
|
+
this._stats = new SpanStatsProcessor(config, otlpStatsExporter)
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
this._spanSampler = new SpanSampler(config.sampler)
|
|
@@ -42,11 +41,11 @@ class SpanProcessor {
|
|
|
42
41
|
const active = []
|
|
43
42
|
const formatted = []
|
|
44
43
|
const trace = spanContext._trace
|
|
45
|
-
const { flushMinSpans,
|
|
44
|
+
const { flushMinSpans, DD_TRACE_ENABLED } = this._config
|
|
46
45
|
const { started, finished } = trace
|
|
47
46
|
|
|
48
47
|
if (trace.record === false) return
|
|
49
|
-
if (
|
|
48
|
+
if (DD_TRACE_ENABLED === false) {
|
|
50
49
|
this._erase(trace, active)
|
|
51
50
|
return
|
|
52
51
|
}
|