dd-trace 5.43.0 → 5.45.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/package.json +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +61 -23
- package/packages/datadog-instrumentations/src/dd-trace-api.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +134 -48
- package/packages/datadog-instrumentations/src/mocha/main.js +20 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +89 -30
- package/packages/datadog-instrumentations/src/mocha/worker.js +3 -1
- package/packages/datadog-instrumentations/src/playwright.js +97 -17
- package/packages/datadog-instrumentations/src/router.js +1 -0
- package/packages/datadog-instrumentations/src/tedious.js +13 -10
- package/packages/datadog-instrumentations/src/vitest.js +77 -17
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +69 -20
- package/packages/datadog-plugin-cypress/src/support.js +39 -10
- package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +8 -186
- package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +186 -0
- package/packages/datadog-plugin-google-cloud-vertexai/src/utils.js +19 -0
- package/packages/datadog-plugin-jest/src/index.js +38 -5
- package/packages/datadog-plugin-mocha/src/index.js +28 -5
- package/packages/datadog-plugin-playwright/src/index.js +22 -2
- package/packages/datadog-plugin-tedious/src/index.js +14 -9
- package/packages/datadog-plugin-vitest/src/index.js +46 -14
- package/packages/dd-trace/src/appsec/blocking.js +2 -0
- package/packages/dd-trace/src/appsec/graphql.js +3 -1
- package/packages/dd-trace/src/appsec/reporter.js +13 -8
- package/packages/dd-trace/src/appsec/sdk/track_event.js +7 -0
- package/packages/dd-trace/src/appsec/telemetry/common.js +6 -3
- package/packages/dd-trace/src/appsec/telemetry/index.js +28 -5
- package/packages/dd-trace/src/appsec/telemetry/user.js +9 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +29 -9
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +16 -7
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
- package/packages/dd-trace/src/dogstatsd.js +94 -77
- package/packages/dd-trace/src/histogram.js +12 -23
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/index.js +3 -0
- package/packages/dd-trace/src/llmobs/noop.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +196 -0
- package/packages/dd-trace/src/llmobs/sdk.js +2 -0
- package/packages/dd-trace/src/llmobs/span_processor.js +6 -0
- package/packages/dd-trace/src/llmobs/tagger.js +8 -2
- package/packages/dd-trace/src/llmobs/telemetry.js +108 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -0
- package/packages/dd-trace/src/llmobs/writers/spans/base.js +10 -1
- package/packages/dd-trace/src/plugin_manager.js +0 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +16 -26
- package/packages/dd-trace/src/plugins/database.js +4 -4
- package/packages/dd-trace/src/plugins/plugin.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +62 -1
- package/packages/dd-trace/src/remote_config/manager.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -6
- package/packages/dd-trace/src/telemetry/send-data.js +5 -1
|
@@ -1,12 +1,119 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const {
|
|
4
|
+
SPAN_KIND,
|
|
5
|
+
MODEL_PROVIDER,
|
|
6
|
+
PARENT_ID_KEY,
|
|
7
|
+
SESSION_ID,
|
|
8
|
+
ROOT_PARENT_ID,
|
|
9
|
+
INTEGRATION,
|
|
10
|
+
DECORATOR
|
|
11
|
+
} = require('./constants/tags')
|
|
12
|
+
|
|
13
|
+
const ERROR_TYPE = require('../constants')
|
|
14
|
+
|
|
3
15
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
16
|
+
|
|
17
|
+
const LLMObsTagger = require('./tagger')
|
|
18
|
+
|
|
4
19
|
const llmobsMetrics = telemetryMetrics.manager.namespace('mlobs')
|
|
5
20
|
|
|
21
|
+
function extractIntegrationFromTags (tags) {
|
|
22
|
+
if (!Array.isArray(tags)) return null
|
|
23
|
+
const integrationTag = tags.find(tag => tag.startsWith('integration:'))
|
|
24
|
+
if (!integrationTag) return null
|
|
25
|
+
return integrationTag.split(':')[1] || null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractTagsFromSpanEvent (event) {
|
|
29
|
+
const spanKind = event.meta?.['span.kind'] || ''
|
|
30
|
+
const integration = extractIntegrationFromTags(event.tags)
|
|
31
|
+
const error = event.status === 'error'
|
|
32
|
+
const autoinstrumented = integration != null
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
span_kind: spanKind,
|
|
36
|
+
autoinstrumented: Number(autoinstrumented),
|
|
37
|
+
error: error ? 1 : 0,
|
|
38
|
+
integration: integration || 'N/A'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
6
42
|
function incrementLLMObsSpanStartCount (tags, value = 1) {
|
|
7
43
|
llmobsMetrics.count('span.start', tags).inc(value)
|
|
8
44
|
}
|
|
9
45
|
|
|
46
|
+
function incrementLLMObsSpanFinishedCount (span, value = 1) {
|
|
47
|
+
const mlObsTags = LLMObsTagger.tagMap.get(span)
|
|
48
|
+
const spanTags = span.context()._tags
|
|
49
|
+
|
|
50
|
+
const isRootSpan = mlObsTags[PARENT_ID_KEY] === ROOT_PARENT_ID
|
|
51
|
+
const hasSessionId = mlObsTags[SESSION_ID] != null
|
|
52
|
+
const integration = mlObsTags[INTEGRATION]
|
|
53
|
+
const autoInstrumented = integration != null
|
|
54
|
+
const decorator = !!mlObsTags[DECORATOR]
|
|
55
|
+
const spanKind = mlObsTags[SPAN_KIND]
|
|
56
|
+
const modelProvider = mlObsTags[MODEL_PROVIDER]
|
|
57
|
+
const error = spanTags.error || spanTags[ERROR_TYPE]
|
|
58
|
+
|
|
59
|
+
const tags = {
|
|
60
|
+
autoinstrumented: Number(autoInstrumented),
|
|
61
|
+
has_session_id: Number(hasSessionId),
|
|
62
|
+
is_root_span: Number(isRootSpan),
|
|
63
|
+
span_kind: spanKind,
|
|
64
|
+
integration: integration || 'N/A',
|
|
65
|
+
error: error ? 1 : 0
|
|
66
|
+
}
|
|
67
|
+
if (!autoInstrumented) {
|
|
68
|
+
tags.decorator = Number(decorator)
|
|
69
|
+
}
|
|
70
|
+
if (modelProvider) {
|
|
71
|
+
tags.model_provider = modelProvider
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
llmobsMetrics.count('span.finished', tags).inc(value)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function recordLLMObsEnabled (startTime, config, value = 1) {
|
|
78
|
+
const initTimeMs = performance.now() - startTime
|
|
79
|
+
// There isn't an easy way to determine if a user automatically enabled LLMObs via
|
|
80
|
+
// in-code or command line setup. We'll use the presence of DD_LLMOBS_ENABLED env var
|
|
81
|
+
// as a rough heuristic, but note that this isn't perfect since
|
|
82
|
+
// a user may have env vars but enable manually in code.
|
|
83
|
+
const autoEnabled = !!config._env?.['llmobs.enabled']
|
|
84
|
+
const tags = {
|
|
85
|
+
error: 0,
|
|
86
|
+
agentless: Number(config.llmobs.agentlessEnabled),
|
|
87
|
+
site: config.site,
|
|
88
|
+
auto: Number(autoEnabled)
|
|
89
|
+
}
|
|
90
|
+
llmobsMetrics.count('product_enabled', tags).inc(value)
|
|
91
|
+
llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function recordLLMObsRawSpanSize (event, rawEventSize) {
|
|
95
|
+
const tags = extractTagsFromSpanEvent(event)
|
|
96
|
+
llmobsMetrics.distribution('span.raw_size', tags).track(rawEventSize)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function recordLLMObsSpanSize (event, eventSize, shouldTruncate) {
|
|
100
|
+
const tags = extractTagsFromSpanEvent(event)
|
|
101
|
+
tags.truncated = Number(shouldTruncate)
|
|
102
|
+
llmobsMetrics.distribution('span.size', tags).track(eventSize)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function recordDroppedPayload (numEvents, eventType, error) {
|
|
106
|
+
if (eventType !== 'span' && eventType !== 'evaluation_metric') return
|
|
107
|
+
const metricName = eventType === 'span' ? 'dropped_span_event' : 'dropped_eval_event'
|
|
108
|
+
const tags = { error }
|
|
109
|
+
llmobsMetrics.count(metricName, tags).inc(numEvents)
|
|
110
|
+
}
|
|
111
|
+
|
|
10
112
|
module.exports = {
|
|
11
|
-
|
|
113
|
+
recordLLMObsEnabled,
|
|
114
|
+
incrementLLMObsSpanStartCount,
|
|
115
|
+
incrementLLMObsSpanFinishedCount,
|
|
116
|
+
recordLLMObsRawSpanSize,
|
|
117
|
+
recordLLMObsSpanSize,
|
|
118
|
+
recordDroppedPayload
|
|
12
119
|
}
|
|
@@ -6,6 +6,7 @@ const { URL, format } = require('url')
|
|
|
6
6
|
const logger = require('../../log')
|
|
7
7
|
|
|
8
8
|
const { encodeUnicode } = require('../util')
|
|
9
|
+
const telemetry = require('../telemetry')
|
|
9
10
|
const log = require('../../log')
|
|
10
11
|
|
|
11
12
|
class BaseLLMObsWriter {
|
|
@@ -45,6 +46,7 @@ class BaseLLMObsWriter {
|
|
|
45
46
|
append (event, byteLength) {
|
|
46
47
|
if (this._buffer.length >= this._bufferLimit) {
|
|
47
48
|
logger.warn(`${this.constructor.name} event buffer full (limit is ${this._bufferLimit}), dropping event`)
|
|
49
|
+
telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
|
|
48
50
|
return
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -76,10 +78,12 @@ class BaseLLMObsWriter {
|
|
|
76
78
|
logger.error(
|
|
77
79
|
'Error sending %d LLMObs %s events to %s: %s', events.length, this._eventType, this._url, err.message, err
|
|
78
80
|
)
|
|
81
|
+
telemetry.recordDroppedPayload(events.length, this._eventType, 'request_error')
|
|
79
82
|
} else if (code >= 300) {
|
|
80
83
|
logger.error(
|
|
81
84
|
'Error sending %d LLMObs %s events to %s: %s', events.length, this._eventType, this._url, code
|
|
82
85
|
)
|
|
86
|
+
telemetry.recordDroppedPayload(events.length, this._eventType, 'http_error')
|
|
83
87
|
} else {
|
|
84
88
|
logger.debug(`Sent ${events.length} LLMObs ${this._eventType} events to ${this._url}`)
|
|
85
89
|
}
|
|
@@ -4,6 +4,7 @@ const { EVP_EVENT_SIZE_LIMIT, EVP_PAYLOAD_SIZE_LIMIT } = require('../../constant
|
|
|
4
4
|
const { DROPPED_VALUE_TEXT } = require('../../constants/text')
|
|
5
5
|
const { DROPPED_IO_COLLECTION_ERROR } = require('../../constants/tags')
|
|
6
6
|
const BaseWriter = require('../base')
|
|
7
|
+
const telemetry = require('../../telemetry')
|
|
7
8
|
const logger = require('../../../log')
|
|
8
9
|
|
|
9
10
|
const tracerVersion = require('../../../../../../package.json').version
|
|
@@ -18,11 +19,19 @@ class LLMObsSpanWriter extends BaseWriter {
|
|
|
18
19
|
|
|
19
20
|
append (event) {
|
|
20
21
|
const eventSizeBytes = Buffer.from(JSON.stringify(event)).byteLength
|
|
21
|
-
|
|
22
|
+
telemetry.recordLLMObsRawSpanSize(event, eventSizeBytes)
|
|
23
|
+
|
|
24
|
+
const shouldTruncate = eventSizeBytes > EVP_EVENT_SIZE_LIMIT
|
|
25
|
+
let processedEventSizeBytes = eventSizeBytes
|
|
26
|
+
|
|
27
|
+
if (shouldTruncate) {
|
|
22
28
|
logger.warn(`Dropping event input/output because its size (${eventSizeBytes}) exceeds the 1MB event size limit`)
|
|
23
29
|
event = this._truncateSpanEvent(event)
|
|
30
|
+
processedEventSizeBytes = Buffer.from(JSON.stringify(event)).byteLength
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
telemetry.recordLLMObsSpanSize(event, processedEventSizeBytes, shouldTruncate)
|
|
34
|
+
|
|
26
35
|
if (this._bufferSize + eventSizeBytes > EVP_PAYLOAD_SIZE_LIMIT) {
|
|
27
36
|
logger.debug('Flusing queue because queing next event will exceed EvP payload limit')
|
|
28
37
|
this.flush()
|
|
@@ -28,9 +28,6 @@ loadChannel.subscribe(({ name }) => {
|
|
|
28
28
|
maybeEnable(plugins[name])
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
// Always enabled
|
|
32
|
-
maybeEnable(require('../../datadog-plugin-dd-trace-api/src'))
|
|
33
|
-
|
|
34
31
|
function maybeEnable (Plugin) {
|
|
35
32
|
if (!Plugin || typeof Plugin !== 'function') return
|
|
36
33
|
if (!pluginClasses[Plugin.id]) {
|
|
@@ -28,9 +28,7 @@ const {
|
|
|
28
28
|
DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX,
|
|
29
29
|
DI_DEBUG_ERROR_FILE_SUFFIX,
|
|
30
30
|
DI_DEBUG_ERROR_LINE_SUFFIX,
|
|
31
|
-
|
|
32
|
-
DD_CAPABILITIES_AUTO_TEST_RETRIES,
|
|
33
|
-
DD_CAPABILITIES_TEST_IMPACT_ANALYSIS
|
|
31
|
+
getLibraryCapabilitiesTags
|
|
34
32
|
} = require('./util/test')
|
|
35
33
|
const Plugin = require('./plugin')
|
|
36
34
|
const { COMPONENT } = require('../constants')
|
|
@@ -41,23 +39,17 @@ const {
|
|
|
41
39
|
TELEMETRY_EVENT_CREATED,
|
|
42
40
|
TELEMETRY_ITR_SKIPPED
|
|
43
41
|
} = require('../ci-visibility/telemetry')
|
|
44
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
CI_PROVIDER_NAME,
|
|
44
|
+
GIT_REPOSITORY_URL,
|
|
45
|
+
GIT_COMMIT_SHA,
|
|
46
|
+
GIT_BRANCH,
|
|
47
|
+
CI_WORKSPACE_PATH,
|
|
48
|
+
GIT_COMMIT_MESSAGE
|
|
49
|
+
} = require('./util/tags')
|
|
45
50
|
const { OS_VERSION, OS_PLATFORM, OS_ARCHITECTURE, RUNTIME_NAME, RUNTIME_VERSION } = require('./util/env')
|
|
46
51
|
const getDiClient = require('../ci-visibility/dynamic-instrumentation')
|
|
47
52
|
|
|
48
|
-
const UNSUPPORTED_TIA_FRAMEWORKS = ['playwright', 'vitest']
|
|
49
|
-
const UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE = ['cucumber', 'mocha']
|
|
50
|
-
|
|
51
|
-
function isTiaSupported (testFramework, isParallel) {
|
|
52
|
-
if (UNSUPPORTED_TIA_FRAMEWORKS.includes(testFramework)) {
|
|
53
|
-
return false
|
|
54
|
-
}
|
|
55
|
-
if (isParallel && UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE.includes(testFramework)) {
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
return true
|
|
59
|
-
}
|
|
60
|
-
|
|
61
53
|
module.exports = class CiPlugin extends Plugin {
|
|
62
54
|
constructor (...args) {
|
|
63
55
|
super(...args)
|
|
@@ -75,17 +67,13 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
75
67
|
} else {
|
|
76
68
|
this.libraryConfig = libraryConfig
|
|
77
69
|
}
|
|
78
|
-
|
|
70
|
+
|
|
71
|
+
const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id, isParallel)
|
|
79
72
|
const metadataTags = {
|
|
80
73
|
test: {
|
|
81
|
-
|
|
82
|
-
[DD_CAPABILITIES_EARLY_FLAKE_DETECTION]: isEarlyFlakeDetectionEnabled ? 'true' : 'false',
|
|
83
|
-
[DD_CAPABILITIES_AUTO_TEST_RETRIES]: isFlakyTestRetriesEnabled ? 'true' : 'false'
|
|
74
|
+
...libraryCapabilitiesTags
|
|
84
75
|
}
|
|
85
76
|
}
|
|
86
|
-
if (!isTiaSupported(this.constructor.id, isParallel)) {
|
|
87
|
-
metadataTags.test[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS] = undefined
|
|
88
|
-
}
|
|
89
77
|
this.tracer._exporter.addMetadataTags(metadataTags)
|
|
90
78
|
onDone({ err, libraryConfig })
|
|
91
79
|
})
|
|
@@ -251,7 +239,8 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
251
239
|
[RUNTIME_VERSION]: runtimeVersion,
|
|
252
240
|
[GIT_BRANCH]: branch,
|
|
253
241
|
[CI_PROVIDER_NAME]: ciProviderName,
|
|
254
|
-
[CI_WORKSPACE_PATH]: repositoryRoot
|
|
242
|
+
[CI_WORKSPACE_PATH]: repositoryRoot,
|
|
243
|
+
[GIT_COMMIT_MESSAGE]: commitMessage
|
|
255
244
|
} = this.testEnvironmentMetadata
|
|
256
245
|
|
|
257
246
|
this.repositoryRoot = repositoryRoot || process.cwd()
|
|
@@ -269,7 +258,8 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
269
258
|
runtimeName,
|
|
270
259
|
runtimeVersion,
|
|
271
260
|
branch,
|
|
272
|
-
testLevel: 'suite'
|
|
261
|
+
testLevel: 'suite',
|
|
262
|
+
commitMessage
|
|
273
263
|
}
|
|
274
264
|
}
|
|
275
265
|
|
|
@@ -63,7 +63,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
63
63
|
return tracerService
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
createDbmComment (span, serviceName,
|
|
66
|
+
createDbmComment (span, serviceName, disableFullMode = false) {
|
|
67
67
|
const mode = this.config.dbmPropagationMode
|
|
68
68
|
const dbmService = this.getDbmServiceName(span, serviceName)
|
|
69
69
|
|
|
@@ -73,7 +73,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
73
73
|
|
|
74
74
|
const servicePropagation = this.createDBMPropagationCommentService(dbmService, span)
|
|
75
75
|
|
|
76
|
-
if (
|
|
76
|
+
if (disableFullMode || mode === 'service') {
|
|
77
77
|
return servicePropagation
|
|
78
78
|
} else if (mode === 'full') {
|
|
79
79
|
span.setTag('_dd.dbm_trace_injected', 'true')
|
|
@@ -82,8 +82,8 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
injectDbmQuery (span, query, serviceName,
|
|
86
|
-
const dbmTraceComment = this.createDbmComment(span, serviceName,
|
|
85
|
+
injectDbmQuery (span, query, serviceName, disableFullMode = false) {
|
|
86
|
+
const dbmTraceComment = this.createDbmComment(span, serviceName, disableFullMode)
|
|
87
87
|
|
|
88
88
|
if (!dbmTraceComment) {
|
|
89
89
|
return query
|
|
@@ -18,10 +18,12 @@ class Subscription {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
enable () {
|
|
21
|
+
// TODO: Once Node.js v18.6.0 is no longer supported, we should use `dc.subscribe(event, handler)` instead
|
|
21
22
|
this._channel.subscribe(this._handler)
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
disable () {
|
|
26
|
+
// TODO: Once Node.js v18.6.0 is no longer supported, we should use `dc.unsubscribe(event, handler)` instead
|
|
25
27
|
this._channel.unsubscribe(this._handler)
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -58,6 +58,7 @@ const TEST_IS_RETRY = 'test.is_retry'
|
|
|
58
58
|
const TEST_EARLY_FLAKE_ENABLED = 'test.early_flake.enabled'
|
|
59
59
|
const TEST_EARLY_FLAKE_ABORT_REASON = 'test.early_flake.abort_reason'
|
|
60
60
|
const TEST_RETRY_REASON = 'test.retry_reason'
|
|
61
|
+
const TEST_HAS_FAILED_ALL_RETRIES = 'test.has_failed_all_retries'
|
|
61
62
|
|
|
62
63
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
63
64
|
|
|
@@ -103,6 +104,12 @@ const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
|
|
|
103
104
|
const DD_CAPABILITIES_TEST_IMPACT_ANALYSIS = '_dd.library_capabilities.test_impact_analysis'
|
|
104
105
|
const DD_CAPABILITIES_EARLY_FLAKE_DETECTION = '_dd.library_capabilities.early_flake_detection'
|
|
105
106
|
const DD_CAPABILITIES_AUTO_TEST_RETRIES = '_dd.library_capabilities.auto_test_retries'
|
|
107
|
+
const DD_CAPABILITIES_TEST_MANAGEMENT_QUARANTINE = '_dd.library_capabilities.test_management.quarantine'
|
|
108
|
+
const DD_CAPABILITIES_TEST_MANAGEMENT_DISABLE = '_dd.library_capabilities.test_management.disable'
|
|
109
|
+
const DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX = '_dd.library_capabilities.test_management.attempt_to_fix'
|
|
110
|
+
const UNSUPPORTED_TIA_FRAMEWORKS = ['playwright', 'vitest']
|
|
111
|
+
const UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE = ['cucumber', 'mocha']
|
|
112
|
+
const UNSUPPORTED_ATTEMPT_TO_FIX_FRAMEWORKS_PARALLEL_MODE = ['mocha']
|
|
106
113
|
|
|
107
114
|
const TEST_LEVEL_EVENT_TYPES = [
|
|
108
115
|
'test',
|
|
@@ -120,9 +127,16 @@ const DI_DEBUG_ERROR_SNAPSHOT_ID_SUFFIX = 'snapshot_id'
|
|
|
120
127
|
const DI_DEBUG_ERROR_FILE_SUFFIX = 'file'
|
|
121
128
|
const DI_DEBUG_ERROR_LINE_SUFFIX = 'line'
|
|
122
129
|
|
|
130
|
+
// Test Management tags
|
|
131
|
+
const TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX = 'test.test_management.is_attempt_to_fix'
|
|
123
132
|
const TEST_MANAGEMENT_IS_DISABLED = 'test.test_management.is_test_disabled'
|
|
124
133
|
const TEST_MANAGEMENT_IS_QUARANTINED = 'test.test_management.is_quarantined'
|
|
125
134
|
const TEST_MANAGEMENT_ENABLED = 'test.test_management.enabled'
|
|
135
|
+
const TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED = 'test.test_management.attempt_to_fix_passed'
|
|
136
|
+
|
|
137
|
+
// Test Management utils strings
|
|
138
|
+
const ATTEMPT_TO_FIX_STRING = "Retried by Datadog's Test Management"
|
|
139
|
+
const ATTEMPT_TEST_NAME_REGEX = new RegExp(ATTEMPT_TO_FIX_STRING + ' \\(#\\d+\\): ', 'g')
|
|
126
140
|
|
|
127
141
|
module.exports = {
|
|
128
142
|
TEST_CODE_OWNERS,
|
|
@@ -155,6 +169,7 @@ module.exports = {
|
|
|
155
169
|
TEST_EARLY_FLAKE_ENABLED,
|
|
156
170
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
157
171
|
TEST_RETRY_REASON,
|
|
172
|
+
TEST_HAS_FAILED_ALL_RETRIES,
|
|
158
173
|
getTestEnvironmentMetadata,
|
|
159
174
|
getTestParametersString,
|
|
160
175
|
finishAllTraceSpans,
|
|
@@ -192,7 +207,9 @@ module.exports = {
|
|
|
192
207
|
EFD_STRING,
|
|
193
208
|
EFD_TEST_NAME_REGEX,
|
|
194
209
|
removeEfdStringFromTestName,
|
|
210
|
+
removeAttemptToFixStringFromTestName,
|
|
195
211
|
addEfdStringToTestName,
|
|
212
|
+
addAttemptToFixStringToTestName,
|
|
196
213
|
getIsFaultyEarlyFlakeDetection,
|
|
197
214
|
TEST_BROWSER_DRIVER,
|
|
198
215
|
TEST_BROWSER_DRIVER_VERSION,
|
|
@@ -202,6 +219,9 @@ module.exports = {
|
|
|
202
219
|
DD_CAPABILITIES_TEST_IMPACT_ANALYSIS,
|
|
203
220
|
DD_CAPABILITIES_EARLY_FLAKE_DETECTION,
|
|
204
221
|
DD_CAPABILITIES_AUTO_TEST_RETRIES,
|
|
222
|
+
DD_CAPABILITIES_TEST_MANAGEMENT_QUARANTINE,
|
|
223
|
+
DD_CAPABILITIES_TEST_MANAGEMENT_DISABLE,
|
|
224
|
+
DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX,
|
|
205
225
|
TEST_LEVEL_EVENT_TYPES,
|
|
206
226
|
getNumFromKnownTests,
|
|
207
227
|
getFileAndLineNumberFromError,
|
|
@@ -212,9 +232,12 @@ module.exports = {
|
|
|
212
232
|
DI_DEBUG_ERROR_LINE_SUFFIX,
|
|
213
233
|
getFormattedError,
|
|
214
234
|
DD_TEST_IS_USER_PROVIDED_SERVICE,
|
|
235
|
+
TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
|
|
215
236
|
TEST_MANAGEMENT_IS_DISABLED,
|
|
216
237
|
TEST_MANAGEMENT_IS_QUARANTINED,
|
|
217
|
-
TEST_MANAGEMENT_ENABLED
|
|
238
|
+
TEST_MANAGEMENT_ENABLED,
|
|
239
|
+
TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
|
|
240
|
+
getLibraryCapabilitiesTags
|
|
218
241
|
}
|
|
219
242
|
|
|
220
243
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -622,10 +645,18 @@ function addEfdStringToTestName (testName, numAttempt) {
|
|
|
622
645
|
return `${EFD_STRING} (#${numAttempt}): ${testName}`
|
|
623
646
|
}
|
|
624
647
|
|
|
648
|
+
function addAttemptToFixStringToTestName (testName, numAttempt) {
|
|
649
|
+
return `${ATTEMPT_TO_FIX_STRING} (#${numAttempt}): ${testName}`
|
|
650
|
+
}
|
|
651
|
+
|
|
625
652
|
function removeEfdStringFromTestName (testName) {
|
|
626
653
|
return testName.replace(EFD_TEST_NAME_REGEX, '')
|
|
627
654
|
}
|
|
628
655
|
|
|
656
|
+
function removeAttemptToFixStringFromTestName (testName) {
|
|
657
|
+
return testName.replace(ATTEMPT_TEST_NAME_REGEX, '')
|
|
658
|
+
}
|
|
659
|
+
|
|
629
660
|
function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faultyThresholdPercentage) {
|
|
630
661
|
let newSuites = 0
|
|
631
662
|
for (const suite of projectSuites) {
|
|
@@ -718,3 +749,33 @@ function getFormattedError (error, repositoryRoot) {
|
|
|
718
749
|
|
|
719
750
|
return newError
|
|
720
751
|
}
|
|
752
|
+
|
|
753
|
+
function getLibraryCapabilitiesTags (testFramework, isParallel) {
|
|
754
|
+
function isTiaSupported (testFramework, isParallel) {
|
|
755
|
+
if (UNSUPPORTED_TIA_FRAMEWORKS.includes(testFramework)) {
|
|
756
|
+
return false
|
|
757
|
+
}
|
|
758
|
+
if (isParallel && UNSUPPORTED_TIA_FRAMEWORKS_PARALLEL_MODE.includes(testFramework)) {
|
|
759
|
+
return false
|
|
760
|
+
}
|
|
761
|
+
return true
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function isAttemptToFixSupported (testFramework, isParallel) {
|
|
765
|
+
if (isParallel && UNSUPPORTED_ATTEMPT_TO_FIX_FRAMEWORKS_PARALLEL_MODE.includes(testFramework)) {
|
|
766
|
+
return false
|
|
767
|
+
}
|
|
768
|
+
return true
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return {
|
|
772
|
+
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: isTiaSupported(testFramework, isParallel) ? '1' : undefined,
|
|
773
|
+
[DD_CAPABILITIES_EARLY_FLAKE_DETECTION]: '1',
|
|
774
|
+
[DD_CAPABILITIES_AUTO_TEST_RETRIES]: '1',
|
|
775
|
+
[DD_CAPABILITIES_TEST_MANAGEMENT_QUARANTINE]: '1',
|
|
776
|
+
[DD_CAPABILITIES_TEST_MANAGEMENT_DISABLE]: '1',
|
|
777
|
+
[DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX]: isAttemptToFixSupported(testFramework, isParallel)
|
|
778
|
+
? '2'
|
|
779
|
+
: undefined
|
|
780
|
+
}
|
|
781
|
+
}
|
|
@@ -10,6 +10,7 @@ const { getExtraServices } = require('../service-naming/extra-services')
|
|
|
10
10
|
const { UNACKNOWLEDGED, ACKNOWLEDGED, ERROR } = require('./apply_states')
|
|
11
11
|
const Scheduler = require('./scheduler')
|
|
12
12
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
|
|
13
|
+
const tagger = require('../tagger')
|
|
13
14
|
|
|
14
15
|
const clientId = uuid()
|
|
15
16
|
|
|
@@ -34,6 +35,10 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
34
35
|
port: config.port
|
|
35
36
|
}))
|
|
36
37
|
|
|
38
|
+
tagger.add(config.tags, {
|
|
39
|
+
'_dd.rc.client_id': clientId
|
|
40
|
+
})
|
|
41
|
+
|
|
37
42
|
const tags = config.repositoryUrl
|
|
38
43
|
? {
|
|
39
44
|
...config.tags,
|
|
@@ -10,7 +10,8 @@ const Histogram = require('../histogram')
|
|
|
10
10
|
const { performance, PerformanceObserver } = require('perf_hooks')
|
|
11
11
|
|
|
12
12
|
const { NODE_MAJOR, NODE_MINOR } = require('../../../../version')
|
|
13
|
-
const
|
|
13
|
+
const { DD_RUNTIME_METRICS_FLUSH_INTERVAL = '10000' } = process.env
|
|
14
|
+
const INTERVAL = parseInt(DD_RUNTIME_METRICS_FLUSH_INTERVAL, 10)
|
|
14
15
|
|
|
15
16
|
// Node >=16 has PerformanceObserver with `gc` type, but <16.7 had a critical bug.
|
|
16
17
|
// See: https://github.com/nodejs/node/issues/39548
|
|
@@ -270,12 +271,12 @@ function captureNativeMetrics () {
|
|
|
270
271
|
})
|
|
271
272
|
|
|
272
273
|
for (let i = 0, l = spaces.length; i < l; i++) {
|
|
273
|
-
const
|
|
274
|
+
const tag = `heap_space:${spaces[i].space_name}`
|
|
274
275
|
|
|
275
|
-
client.gauge('runtime.node.heap.size.by.space', spaces[i].space_size,
|
|
276
|
-
client.gauge('runtime.node.heap.used_size.by.space', spaces[i].space_used_size,
|
|
277
|
-
client.gauge('runtime.node.heap.available_size.by.space', spaces[i].space_available_size,
|
|
278
|
-
client.gauge('runtime.node.heap.physical_size.by.space', spaces[i].physical_space_size,
|
|
276
|
+
client.gauge('runtime.node.heap.size.by.space', spaces[i].space_size, tag)
|
|
277
|
+
client.gauge('runtime.node.heap.used_size.by.space', spaces[i].space_used_size, tag)
|
|
278
|
+
client.gauge('runtime.node.heap.available_size.by.space', spaces[i].space_available_size, tag)
|
|
279
|
+
client.gauge('runtime.node.heap.physical_size.by.space', spaces[i].physical_space_size, tag)
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
|
|
@@ -100,7 +100,11 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
100
100
|
path: '/api/v2/apmtelemetry'
|
|
101
101
|
}
|
|
102
102
|
if (backendUrl) {
|
|
103
|
-
request(data, backendOptions, (error) => {
|
|
103
|
+
request(data, backendOptions, (error) => {
|
|
104
|
+
if (error) {
|
|
105
|
+
log.error('Error sending telemetry data', error)
|
|
106
|
+
}
|
|
107
|
+
})
|
|
104
108
|
} else {
|
|
105
109
|
log.error('Invalid Telemetry URL')
|
|
106
110
|
}
|