dd-trace 5.102.0 → 5.103.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/ext/exporters.js +1 -0
- package/package.json +12 -11
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +1 -1
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
- package/packages/datadog-instrumentations/src/couchbase.js +69 -220
- package/packages/datadog-instrumentations/src/cucumber.js +1 -1
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/graphql.js +13 -12
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
- package/packages/datadog-instrumentations/src/ioredis.js +16 -12
- package/packages/datadog-instrumentations/src/jest.js +351 -50
- package/packages/datadog-instrumentations/src/kafkajs.js +164 -173
- package/packages/datadog-instrumentations/src/mocha/main.js +73 -1
- package/packages/datadog-instrumentations/src/mongodb-core.js +33 -8
- package/packages/datadog-instrumentations/src/pg.js +24 -10
- package/packages/datadog-instrumentations/src/playwright.js +427 -55
- package/packages/datadog-instrumentations/src/redis.js +19 -10
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +1 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +214 -22
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- package/packages/datadog-plugin-electron/src/index.js +17 -0
- package/packages/datadog-plugin-electron/src/ipc.js +143 -0
- package/packages/datadog-plugin-electron/src/net.js +82 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-graphql/src/execute.js +6 -28
- package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
- package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
- package/packages/datadog-plugin-graphql/src/utils.js +29 -0
- package/packages/datadog-plugin-grpc/src/client.js +6 -7
- package/packages/datadog-plugin-grpc/src/util.js +57 -22
- package/packages/datadog-plugin-http/src/client.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-mocha/src/index.js +1 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +36 -70
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -0
- package/packages/datadog-plugin-redis/src/index.js +18 -23
- package/packages/dd-trace/src/aiguard/index.js +3 -1
- package/packages/dd-trace/src/aiguard/sdk.js +36 -30
- package/packages/dd-trace/src/aiguard/tags.js +20 -11
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/azure_metadata.js +17 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
- package/packages/dd-trace/src/config/helper.js +4 -0
- package/packages/dd-trace/src/config/index.js +2 -2
- package/packages/dd-trace/src/config/major-overrides.js +98 -0
- package/packages/dd-trace/src/config/parsers.js +7 -1
- package/packages/dd-trace/src/config/supported-configurations.json +51 -38
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
- package/packages/dd-trace/src/debugger/index.js +7 -7
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +45 -54
- package/packages/dd-trace/src/encode/0.5.js +34 -3
- package/packages/dd-trace/src/encode/agentless-json.js +1 -1
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/electron/index.js +49 -0
- package/packages/dd-trace/src/external-logger/src/index.js +2 -1
- package/packages/dd-trace/src/git_metadata.js +10 -8
- package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
- package/packages/dd-trace/src/lambda/index.js +62 -14
- package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
- package/packages/dd-trace/src/llmobs/index.js +13 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
- package/packages/dd-trace/src/plugins/database.js +54 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/plugin.js +2 -4
- package/packages/dd-trace/src/plugins/util/ci.js +8 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +37 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
- package/packages/dd-trace/src/ritm.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/serverless.js +5 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
|
@@ -3,46 +3,81 @@
|
|
|
3
3
|
const pick = require('../../datadog-core/src/utils/src/pick')
|
|
4
4
|
const log = require('../../dd-trace/src/log')
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} ParsedMethodPath
|
|
8
|
+
* @property {string} name
|
|
9
|
+
* @property {string} service
|
|
10
|
+
* @property {string} package
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Sentinel returned by `getFilter` when the user has not configured a metadata
|
|
14
|
+
// filter. `addMetadataTags` short-circuits on this identity to skip the
|
|
15
|
+
// `metadata.getMap()` clone in the default no-filter case.
|
|
6
16
|
function getEmptyObject () {
|
|
7
17
|
return {}
|
|
8
18
|
}
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
/**
|
|
21
|
+
* gRPC method paths are stable per service definition (e.g.
|
|
22
|
+
* `/pkg.Service/Method`); a service typically only has a small finite set.
|
|
23
|
+
* Cache the parsed `{name, service, package}` triple by path so we skip the
|
|
24
|
+
* `path.split('/')` + `serviceParts.split('.')` + `serviceParts.pop()` work
|
|
25
|
+
* on every call.
|
|
26
|
+
*
|
|
27
|
+
* @type {Map<string, ParsedMethodPath>}
|
|
28
|
+
*/
|
|
29
|
+
const methodPathCache = new Map()
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} path
|
|
33
|
+
* @returns {ParsedMethodPath}
|
|
34
|
+
*/
|
|
35
|
+
function parseMethodPath (path) {
|
|
36
|
+
const methodParts = path.split('/')
|
|
37
|
+
|
|
38
|
+
if (methodParts.length > 2) {
|
|
39
|
+
const serviceParts = methodParts[1].split('.')
|
|
40
|
+
return {
|
|
41
|
+
name: methodParts[2],
|
|
42
|
+
service: serviceParts.pop(),
|
|
43
|
+
package: serviceParts.join('.'),
|
|
18
44
|
}
|
|
45
|
+
}
|
|
19
46
|
|
|
20
|
-
|
|
47
|
+
return { name: methodParts.at(-1), service: '', package: '' }
|
|
48
|
+
}
|
|
21
49
|
|
|
22
|
-
|
|
50
|
+
module.exports = {
|
|
51
|
+
getEmptyObject,
|
|
23
52
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const pkg = serviceParts.join('.')
|
|
53
|
+
getMethodMetadata (path, kind) {
|
|
54
|
+
if (typeof path !== 'string') {
|
|
55
|
+
return { path, kind, name: '', service: '', package: '' }
|
|
56
|
+
}
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
tags.name = methodParts.at(-1)
|
|
58
|
+
let parsed = methodPathCache.get(path)
|
|
59
|
+
if (parsed === undefined) {
|
|
60
|
+
parsed = parseMethodPath(path)
|
|
61
|
+
methodPathCache.set(path, parsed)
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
return
|
|
64
|
+
return {
|
|
65
|
+
path,
|
|
66
|
+
kind,
|
|
67
|
+
name: parsed.name,
|
|
68
|
+
service: parsed.service,
|
|
69
|
+
package: parsed.package,
|
|
70
|
+
}
|
|
38
71
|
},
|
|
39
72
|
|
|
40
73
|
addMetadataTags (span, metadata, filter, type) {
|
|
41
74
|
if (!metadata || typeof metadata.getMap !== 'function') return
|
|
75
|
+
// Default no-op filter: skip the full metadata clone via `getMap()`.
|
|
76
|
+
if (filter === getEmptyObject) return
|
|
42
77
|
|
|
43
78
|
const values = filter(metadata.getMap())
|
|
44
79
|
|
|
45
|
-
for (const key
|
|
80
|
+
for (const key of Object.keys(values)) {
|
|
46
81
|
span.setTag(`grpc.${type}.metadata.${key}`, values[key])
|
|
47
82
|
}
|
|
48
83
|
},
|
|
@@ -39,10 +39,10 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
39
39
|
// TODO delegate to super.startspan
|
|
40
40
|
const span = this.startSpan(this.operationName(), {
|
|
41
41
|
childOf,
|
|
42
|
-
integrationName: this.
|
|
42
|
+
integrationName: this.component,
|
|
43
43
|
service: this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
|
|
44
44
|
meta: {
|
|
45
|
-
[COMPONENT]: this.
|
|
45
|
+
[COMPONENT]: this.component,
|
|
46
46
|
'span.kind': 'client',
|
|
47
47
|
'resource.name': method,
|
|
48
48
|
'span.type': 'http',
|
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
TEST_SOURCE_START,
|
|
22
22
|
TEST_ITR_UNSKIPPABLE,
|
|
23
23
|
TEST_ITR_FORCED_RUN,
|
|
24
|
+
TEST_ITR_SKIPPING_ENABLED,
|
|
24
25
|
TEST_CODE_OWNERS,
|
|
25
26
|
ITR_CORRELATION_ID,
|
|
26
27
|
TEST_SOURCE_FILE,
|
|
@@ -107,6 +108,7 @@ class JestPlugin extends CiPlugin {
|
|
|
107
108
|
process.on('message', handler)
|
|
108
109
|
}
|
|
109
110
|
this.testSuiteSpanPerTestSuiteAbsolutePath = new Map()
|
|
111
|
+
this.pendingTestSuiteFinishes = new Set()
|
|
110
112
|
|
|
111
113
|
this.addSub('ci:jest:session:finish', ({
|
|
112
114
|
status,
|
|
@@ -123,58 +125,66 @@ class JestPlugin extends CiPlugin {
|
|
|
123
125
|
isTestManagementTestsEnabled,
|
|
124
126
|
onDone,
|
|
125
127
|
}) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
const finishSession = () => {
|
|
129
|
+
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
130
|
+
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
128
131
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
addIntelligentTestRunnerSpanTags(
|
|
135
|
-
this.testSessionSpan,
|
|
136
|
-
this.testModuleSpan,
|
|
137
|
-
{
|
|
138
|
-
isSuitesSkipped,
|
|
139
|
-
isSuitesSkippingEnabled,
|
|
140
|
-
isCodeCoverageEnabled,
|
|
141
|
-
testCodeCoverageLinesTotal,
|
|
142
|
-
skippingType: 'suite',
|
|
143
|
-
skippingCount: numSkippedSuites,
|
|
144
|
-
hasUnskippableSuites,
|
|
145
|
-
hasForcedToRunSuites,
|
|
132
|
+
if (error) {
|
|
133
|
+
this.testSessionSpan.setTag('error', error)
|
|
134
|
+
this.testModuleSpan.setTag('error', error)
|
|
146
135
|
}
|
|
147
|
-
)
|
|
148
136
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
137
|
+
addIntelligentTestRunnerSpanTags(
|
|
138
|
+
this.testSessionSpan,
|
|
139
|
+
this.testModuleSpan,
|
|
140
|
+
{
|
|
141
|
+
isSuitesSkipped,
|
|
142
|
+
isSuitesSkippingEnabled,
|
|
143
|
+
isCodeCoverageEnabled,
|
|
144
|
+
testCodeCoverageLinesTotal,
|
|
145
|
+
skippingType: 'suite',
|
|
146
|
+
skippingCount: numSkippedSuites,
|
|
147
|
+
hasUnskippableSuites,
|
|
148
|
+
hasForcedToRunSuites,
|
|
149
|
+
}
|
|
150
|
+
)
|
|
158
151
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
152
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
153
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
|
|
154
|
+
}
|
|
155
|
+
if (isEarlyFlakeDetectionFaulty) {
|
|
156
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
|
|
157
|
+
}
|
|
158
|
+
if (isTestManagementTestsEnabled) {
|
|
159
|
+
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
160
|
+
}
|
|
166
161
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
this.testModuleSpan.finish()
|
|
163
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
164
|
+
this.testSessionSpan.finish()
|
|
165
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
|
|
166
|
+
hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
|
|
167
|
+
})
|
|
168
|
+
finishAllTraceSpans(this.testSessionSpan)
|
|
169
|
+
|
|
170
|
+
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
171
|
+
provider: this.ciProviderName,
|
|
172
|
+
autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
appClosingTelemetry()
|
|
176
|
+
this.tracer._exporter.flush(() => {
|
|
177
|
+
if (onDone) {
|
|
178
|
+
onDone()
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
}
|
|
171
182
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})
|
|
183
|
+
if (this.pendingTestSuiteFinishes.size > 0) {
|
|
184
|
+
Promise.all(this.pendingTestSuiteFinishes).then(finishSession)
|
|
185
|
+
} else {
|
|
186
|
+
finishSession()
|
|
187
|
+
}
|
|
178
188
|
})
|
|
179
189
|
|
|
180
190
|
// Test suites can be run in a different process from jest's main one.
|
|
@@ -197,6 +207,7 @@ class JestPlugin extends CiPlugin {
|
|
|
197
207
|
config._ddIsDiEnabled = this.libraryConfig?.isDiEnabled ?? false
|
|
198
208
|
config._ddIsKnownTestsEnabled = this.libraryConfig?.isKnownTestsEnabled ?? false
|
|
199
209
|
config._ddIsImpactedTestsEnabled = this.libraryConfig?.isImpactedTestsEnabled ?? false
|
|
210
|
+
config._ddItrSkippingEnabledTags = this.getSessionItrSkippingEnabledTags()
|
|
200
211
|
}
|
|
201
212
|
})
|
|
202
213
|
|
|
@@ -217,6 +228,7 @@ class JestPlugin extends CiPlugin {
|
|
|
217
228
|
_ddForcedToRun,
|
|
218
229
|
_ddUnskippable,
|
|
219
230
|
_ddTestCodeCoverageEnabled,
|
|
231
|
+
_ddItrSkippingEnabledTags: itrSkippingEnabledTags,
|
|
220
232
|
} = testEnvironmentOptions
|
|
221
233
|
|
|
222
234
|
const testSessionSpanContext = this.tracer.extract('text_map', {
|
|
@@ -228,6 +240,7 @@ class JestPlugin extends CiPlugin {
|
|
|
228
240
|
...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, 'jest'),
|
|
229
241
|
// requestErrorTags from test env options may be undefined
|
|
230
242
|
...(requestErrorTags !== undefined && requestErrorTags !== null ? requestErrorTags : {}),
|
|
243
|
+
...(itrSkippingEnabledTags !== undefined && itrSkippingEnabledTags !== null ? itrSkippingEnabledTags : {}),
|
|
231
244
|
}
|
|
232
245
|
|
|
233
246
|
if (_ddUnskippable) {
|
|
@@ -305,10 +318,18 @@ class JestPlugin extends CiPlugin {
|
|
|
305
318
|
}
|
|
306
319
|
})
|
|
307
320
|
|
|
308
|
-
this.addSub('ci:jest:test-suite:finish', ({
|
|
321
|
+
this.addSub('ci:jest:test-suite:finish', ({
|
|
322
|
+
status,
|
|
323
|
+
errorMessage,
|
|
324
|
+
error,
|
|
325
|
+
testSuiteAbsolutePath,
|
|
326
|
+
waitForFinish,
|
|
327
|
+
onDone,
|
|
328
|
+
}) => {
|
|
309
329
|
const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath)
|
|
310
330
|
if (!testSuiteSpan) {
|
|
311
331
|
log.warn('"ci:jest:test-suite:finish": no span found for test suite absolute path %s', testSuiteAbsolutePath)
|
|
332
|
+
onDone?.()
|
|
312
333
|
return
|
|
313
334
|
}
|
|
314
335
|
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
@@ -319,8 +340,13 @@ class JestPlugin extends CiPlugin {
|
|
|
319
340
|
testSuiteSpan.setTag('error', new Error(errorMessage))
|
|
320
341
|
testSuiteSpan.setTag(TEST_STATUS, 'fail')
|
|
321
342
|
}
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
let resolvePendingFinish
|
|
344
|
+
const pendingFinish = new Promise(resolve => {
|
|
345
|
+
resolvePendingFinish = resolve
|
|
346
|
+
})
|
|
347
|
+
this.pendingTestSuiteFinishes.add(pendingFinish)
|
|
348
|
+
|
|
349
|
+
const finish = () => {
|
|
324
350
|
testSuiteSpan.finish()
|
|
325
351
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
326
352
|
// Suites potentially run in a different process than the session,
|
|
@@ -334,7 +360,19 @@ class JestPlugin extends CiPlugin {
|
|
|
334
360
|
}
|
|
335
361
|
this.removeAllDiProbes()
|
|
336
362
|
this.testSuiteSpanPerTestSuiteAbsolutePath.delete(testSuiteAbsolutePath)
|
|
337
|
-
|
|
363
|
+
this.pendingTestSuiteFinishes.delete(pendingFinish)
|
|
364
|
+
resolvePendingFinish()
|
|
365
|
+
if (onDone) {
|
|
366
|
+
onDone()
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (waitForFinish) {
|
|
371
|
+
// Give late async work time to run before Jest restarts a worker because of workerIdleMemoryLimit.
|
|
372
|
+
realSetTimeout(finish)
|
|
373
|
+
} else {
|
|
374
|
+
process.nextTick(finish)
|
|
375
|
+
}
|
|
338
376
|
})
|
|
339
377
|
|
|
340
378
|
this.addSub('ci:jest:test-suite:error', ({ error, errorMessage, testSuiteAbsolutePath }) => {
|
|
@@ -558,6 +596,10 @@ class JestPlugin extends CiPlugin {
|
|
|
558
596
|
extraTags[TEST_HAS_DYNAMIC_NAME] = 'true'
|
|
559
597
|
}
|
|
560
598
|
const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath) || this.testSuiteSpan
|
|
599
|
+
const skippingEnabled = testSuiteSpan?.context()._tags?.[TEST_ITR_SKIPPING_ENABLED]
|
|
600
|
+
if (skippingEnabled !== undefined) {
|
|
601
|
+
extraTags[TEST_ITR_SKIPPING_ENABLED] = skippingEnabled
|
|
602
|
+
}
|
|
561
603
|
|
|
562
604
|
return super.startTestSpan(name, suite, testSuiteSpan, extraTags)
|
|
563
605
|
}
|
|
@@ -90,9 +90,8 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
93
|
+
const MAX_DEPTH = 10
|
|
94
|
+
const MAX_QUERY_LENGTH = 10_000
|
|
96
95
|
|
|
97
96
|
function extractQuery (statements) {
|
|
98
97
|
if (statements.length === 1 && statements[0].q) return statements[0].q
|
|
@@ -100,7 +99,7 @@ function extractQuery (statements) {
|
|
|
100
99
|
const extractedQueries = []
|
|
101
100
|
for (let i = 0; i < statements.length; i++) {
|
|
102
101
|
if (statements[i].q) {
|
|
103
|
-
extractedQueries.push(
|
|
102
|
+
extractedQueries.push(statements[i].q)
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
|
|
@@ -110,12 +109,12 @@ function extractQuery (statements) {
|
|
|
110
109
|
function getQuery (cmd) {
|
|
111
110
|
if (!cmd || (typeof cmd !== 'object' && !Array.isArray(cmd))) return
|
|
112
111
|
|
|
113
|
-
if (Array.isArray(cmd)) return
|
|
114
|
-
if (cmd.query) return
|
|
115
|
-
if (cmd.filter) return
|
|
116
|
-
if (cmd.pipeline) return
|
|
117
|
-
if (cmd.deletes) return
|
|
118
|
-
if (cmd.updates) return
|
|
112
|
+
if (Array.isArray(cmd)) return sanitiseAndStringify(extractQuery(cmd))
|
|
113
|
+
if (cmd.query) return sanitiseAndStringify(cmd.query)
|
|
114
|
+
if (cmd.filter) return sanitiseAndStringify(cmd.filter)
|
|
115
|
+
if (cmd.pipeline) return sanitiseAndStringify(cmd.pipeline)
|
|
116
|
+
if (cmd.deletes) return sanitiseAndStringify(extractQuery(cmd.deletes))
|
|
117
|
+
if (cmd.updates) return sanitiseAndStringify(extractQuery(cmd.updates))
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
function getResource (plugin, ns, query, operationName) {
|
|
@@ -129,73 +128,40 @@ function getResource (plugin, ns, query, operationName) {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
function truncate (input) {
|
|
132
|
-
return input.slice(0,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function shouldSimplify (input) {
|
|
136
|
-
return !isObject(input) || typeof input.toJSON === 'function'
|
|
131
|
+
return input.length > MAX_QUERY_LENGTH ? input.slice(0, MAX_QUERY_LENGTH) : input
|
|
137
132
|
}
|
|
138
133
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const {
|
|
160
|
-
input, output, depth,
|
|
161
|
-
} = queue.pop()
|
|
162
|
-
const nextDepth = depth + 1
|
|
163
|
-
for (const key of Object.keys(input)) {
|
|
164
|
-
let child = input[key]
|
|
165
|
-
if (typeof child === 'function') continue
|
|
166
|
-
|
|
167
|
-
if (isBSON(child)) {
|
|
168
|
-
child = typeof child.toJSON === 'function' ? child.toJSON() : '?'
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (depth >= 10 || shouldHide(child)) {
|
|
172
|
-
output[key] = '?'
|
|
173
|
-
} else if (shouldSimplify(child)) {
|
|
174
|
-
output[key] = child
|
|
175
|
-
} else {
|
|
176
|
-
output[key] = {}
|
|
177
|
-
queue.push({
|
|
178
|
-
input: child,
|
|
179
|
-
output: output[key],
|
|
180
|
-
depth: nextDepth,
|
|
181
|
-
})
|
|
134
|
+
// Single-pass sanitisation. The replacer:
|
|
135
|
+
// - skips functions and coerces bigint to its decimal string,
|
|
136
|
+
// - returns '?' for Buffer / BSON Binary on the *original* value (JSON.stringify already invoked
|
|
137
|
+
// toJSON before calling us; Buffer / Binary do have toJSON outputs we want to suppress),
|
|
138
|
+
// - lets JSON.stringify call toJSON on other BSON types (ObjectId, Long, Decimal128, Date, Timestamp, ...)
|
|
139
|
+
// so the result lands here as a primitive or plain object,
|
|
140
|
+
// - returns '?' for BSON types without toJSON (MinKey, MaxKey) where `value === original`,
|
|
141
|
+
// - tracks depth via an ancestor stack so cycles and depth >= MAX_DEPTH collapse to '?'.
|
|
142
|
+
function sanitiseAndStringify (input) {
|
|
143
|
+
const ancestors = []
|
|
144
|
+
return JSON.stringify(input, function (key, value) {
|
|
145
|
+
if (typeof value === 'function') return
|
|
146
|
+
if (typeof value === 'bigint') return value.toString()
|
|
147
|
+
|
|
148
|
+
const original = key === '' ? value : this[key]
|
|
149
|
+
if (typeof original === 'object' && original !== null) {
|
|
150
|
+
if (Buffer.isBuffer(original)) return '?'
|
|
151
|
+
const bsontype = original._bsontype
|
|
152
|
+
if (bsontype !== undefined && (bsontype === 'Binary' || value === original)) {
|
|
153
|
+
return '?'
|
|
182
154
|
}
|
|
183
155
|
}
|
|
184
|
-
}
|
|
185
156
|
|
|
186
|
-
|
|
187
|
-
}
|
|
157
|
+
if (value === null || typeof value !== 'object') return value
|
|
188
158
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
function isBSON (val) {
|
|
194
|
-
return val && val._bsontype && !isBinary(val)
|
|
195
|
-
}
|
|
159
|
+
while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop()
|
|
160
|
+
if (ancestors.length >= MAX_DEPTH || ancestors.includes(value)) return '?'
|
|
161
|
+
ancestors.push(value)
|
|
196
162
|
|
|
197
|
-
|
|
198
|
-
|
|
163
|
+
return value
|
|
164
|
+
})
|
|
199
165
|
}
|
|
200
166
|
|
|
201
167
|
function isHeartbeat (ops, config) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../datadog-core')
|
|
4
|
-
const CLIENT_PORT_KEY = require('../../dd-trace/src/constants')
|
|
4
|
+
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
5
5
|
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
6
6
|
|
|
7
7
|
class MySQLPlugin extends DatabasePlugin {
|
|
@@ -9,9 +9,9 @@ class PGPlugin extends DatabasePlugin {
|
|
|
9
9
|
static system = 'postgres'
|
|
10
10
|
|
|
11
11
|
bindStart (ctx) {
|
|
12
|
-
const { params = {}, query, processId, stream } = ctx
|
|
12
|
+
const { params = {}, query, originalText, processId, stream } = ctx
|
|
13
13
|
const service = this.serviceName({ pluginConfig: this.config, params })
|
|
14
|
-
const originalStatement = this.maybeTruncate(
|
|
14
|
+
const originalStatement = this.maybeTruncate(originalText)
|
|
15
15
|
|
|
16
16
|
const span = this.startSpan(this.operationName(), {
|
|
17
17
|
service,
|
|
@@ -32,7 +32,7 @@ class PGPlugin extends DatabasePlugin {
|
|
|
32
32
|
span.setTag('db.stream', 1)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
ctx.injected = this.injectDbmQuery(span, originalText, service.name, !!query.name)
|
|
36
36
|
|
|
37
37
|
return ctx.currentStore
|
|
38
38
|
}
|
|
@@ -321,6 +321,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
321
321
|
isAtrRetry,
|
|
322
322
|
isModified,
|
|
323
323
|
finalStatus,
|
|
324
|
+
earlyFlakeAbortReason,
|
|
324
325
|
onDone,
|
|
325
326
|
}) => {
|
|
326
327
|
if (!span) return
|
|
@@ -384,6 +385,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
384
385
|
if (finalStatus) {
|
|
385
386
|
span.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
386
387
|
}
|
|
388
|
+
if (earlyFlakeAbortReason) {
|
|
389
|
+
span.setTag(TEST_EARLY_FLAKE_ABORT_REASON, earlyFlakeAbortReason)
|
|
390
|
+
}
|
|
387
391
|
for (const step of steps) {
|
|
388
392
|
const stepStartTime = step.startTime.getTime()
|
|
389
393
|
const stepSpan = this.tracer.startSpan('playwright.step', {
|
|
@@ -4,6 +4,9 @@ const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
|
4
4
|
const CachePlugin = require('../../dd-trace/src/plugins/cache')
|
|
5
5
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
6
6
|
|
|
7
|
+
const MAX_ARG_LENGTH = 100
|
|
8
|
+
const MAX_COMMAND_LENGTH = 1000
|
|
9
|
+
|
|
7
10
|
class RedisPlugin extends CachePlugin {
|
|
8
11
|
static id = 'redis'
|
|
9
12
|
static system = 'redis'
|
|
@@ -14,7 +17,7 @@ class RedisPlugin extends CachePlugin {
|
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
bindStart (ctx) {
|
|
17
|
-
const { db, command, args, connectionOptions, connectionName } = ctx
|
|
20
|
+
const { db, command, args, argsStartIndex, connectionOptions, connectionName } = ctx
|
|
18
21
|
|
|
19
22
|
const resource = command
|
|
20
23
|
const normalizedCommand = command.toUpperCase()
|
|
@@ -29,7 +32,7 @@ class RedisPlugin extends CachePlugin {
|
|
|
29
32
|
meta: {
|
|
30
33
|
'db.type': this._spanType,
|
|
31
34
|
'db.name': db || '0',
|
|
32
|
-
[`${this._spanType}.raw_command`]: formatCommand(normalizedCommand, args),
|
|
35
|
+
[`${this._spanType}.raw_command`]: formatCommand(normalizedCommand, args, argsStartIndex),
|
|
33
36
|
'out.host': connectionOptions.host,
|
|
34
37
|
[CLIENT_PORT_KEY]: connectionOptions.port,
|
|
35
38
|
},
|
|
@@ -43,36 +46,28 @@ class RedisPlugin extends CachePlugin {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
function formatCommand (command, args) {
|
|
49
|
+
function formatCommand (command, args, argsStartIndex = 0) {
|
|
47
50
|
if (!args || command === 'AUTH') return command
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
let result = command
|
|
53
|
+
for (let i = argsStartIndex, l = args.length; i < l; i++) {
|
|
54
|
+
const arg = args[i]
|
|
55
|
+
if (typeof arg === 'function') continue
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
result = `${result} ${formatArg(arg)}`
|
|
58
|
+
if (result.length > MAX_COMMAND_LENGTH) return result.slice(0, MAX_COMMAND_LENGTH - 3) + '...'
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
return
|
|
61
|
+
return result
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
function formatArg (arg) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
case 'number':
|
|
64
|
-
return trim(String(arg), 100)
|
|
65
|
-
default:
|
|
66
|
-
return '?'
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function trim (str, maxlen) {
|
|
71
|
-
if (str.length > maxlen) {
|
|
72
|
-
str = str.slice(0, maxlen - 3) + '...'
|
|
65
|
+
if (typeof arg === 'string') {
|
|
66
|
+
return arg.length > MAX_ARG_LENGTH ? arg.slice(0, MAX_ARG_LENGTH - 3) + '...' : arg
|
|
73
67
|
}
|
|
74
|
-
|
|
75
|
-
return
|
|
68
|
+
// Number stringification is bounded (~23 chars max), so it never hits MAX_ARG_LENGTH.
|
|
69
|
+
if (typeof arg === 'number') return String(arg)
|
|
70
|
+
return '?'
|
|
76
71
|
}
|
|
77
72
|
|
|
78
73
|
function normalizeConfig (config) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const log = require('../log')
|
|
4
4
|
const { incomingHttpRequestStart, aiguardChannel } = require('./channels')
|
|
5
5
|
const AIGuard = require('./sdk')
|
|
6
|
+
const { SOURCE_AUTO, INTEGRATION_NONE } = require('./tags')
|
|
6
7
|
|
|
7
8
|
let isEnabled = false
|
|
8
9
|
let aiguard
|
|
@@ -51,7 +52,8 @@ function onEvaluate (ctx) {
|
|
|
51
52
|
return
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
const opts = { block, source: SOURCE_AUTO, integration: ctx.integration || INTEGRATION_NONE }
|
|
56
|
+
aiguard.evaluate(ctx.messages, opts)
|
|
55
57
|
.then(() => {
|
|
56
58
|
ctx.resolve()
|
|
57
59
|
})
|