dd-trace 5.43.0 → 5.44.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/telemetry/common.js +6 -3
- package/packages/dd-trace/src/appsec/telemetry/index.js +20 -4
- 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/llmobs/constants/tags.js +2 -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 +82 -1
- 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/runtime_metrics/runtime_metrics.js +7 -6
- package/packages/dd-trace/src/telemetry/send-data.js +5 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const LLMObsPlugin = require('./base')
|
|
4
|
+
const {
|
|
5
|
+
extractModel,
|
|
6
|
+
extractSystemInstructions
|
|
7
|
+
} = require('../../../../datadog-plugin-google-cloud-vertexai/src/utils')
|
|
8
|
+
|
|
9
|
+
class VertexAILLMObsPlugin extends LLMObsPlugin {
|
|
10
|
+
static get id () { return 'vertexai' } // used for llmobs telemetry
|
|
11
|
+
static get prefix () {
|
|
12
|
+
return 'tracing:apm:vertexai:request'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getLLMObsSpanRegisterOptions (ctx) {
|
|
16
|
+
const history = ctx.instance?.historyInternal || []
|
|
17
|
+
ctx.history = history
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
kind: 'llm',
|
|
21
|
+
modelName: extractModel(ctx.instance),
|
|
22
|
+
modelProvider: 'google',
|
|
23
|
+
name: ctx.resource
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
setLLMObsTags (ctx) {
|
|
28
|
+
const span = ctx.currentStore?.span
|
|
29
|
+
if (!span) return
|
|
30
|
+
|
|
31
|
+
const { instance, result, request } = ctx
|
|
32
|
+
const history = ctx.history || []
|
|
33
|
+
const systemInstructions = extractSystemInstructions(instance)
|
|
34
|
+
|
|
35
|
+
const metadata = getMetadata(instance)
|
|
36
|
+
const inputMessages = extractInputMessages(request, history, systemInstructions)
|
|
37
|
+
const outputMessages = extractOutputMessages(result)
|
|
38
|
+
const metrics = extractMetrics(result)
|
|
39
|
+
|
|
40
|
+
this._tagger.tagLLMIO(span, inputMessages, outputMessages)
|
|
41
|
+
this._tagger.tagMetadata(span, metadata)
|
|
42
|
+
this._tagger.tagMetrics(span, metrics)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getMetadata (instance) {
|
|
47
|
+
const metadata = {}
|
|
48
|
+
|
|
49
|
+
const modelConfig = instance.generationConfig
|
|
50
|
+
if (!modelConfig) return metadata
|
|
51
|
+
|
|
52
|
+
for (const [parameter, parameterKey] of [
|
|
53
|
+
['temperature', 'temperature'],
|
|
54
|
+
['maxOutputTokens', 'max_output_tokens'],
|
|
55
|
+
['candidateCount', 'candidate_count'],
|
|
56
|
+
['topP', 'top_p'],
|
|
57
|
+
['topK', 'top_k']
|
|
58
|
+
]) {
|
|
59
|
+
if (modelConfig[parameter]) {
|
|
60
|
+
metadata[parameterKey] = modelConfig[parameter]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return metadata
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function extractInputMessages (request, history, systemInstructions) {
|
|
68
|
+
const contents = typeof request === 'string' || Array.isArray(request) ? request : request.contents
|
|
69
|
+
const messages = []
|
|
70
|
+
|
|
71
|
+
if (systemInstructions) {
|
|
72
|
+
for (const instruction of systemInstructions) {
|
|
73
|
+
messages.push({ content: instruction || '', role: 'system' })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const content of history) {
|
|
78
|
+
messages.push(...extractMessagesFromContent(content))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof contents === 'string') {
|
|
82
|
+
messages.push({ content: contents })
|
|
83
|
+
return messages
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isPart(contents)) {
|
|
87
|
+
messages.push(extractMessageFromPart(contents))
|
|
88
|
+
return messages
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!Array.isArray(contents)) {
|
|
92
|
+
messages.push({
|
|
93
|
+
content: '[Non-array content object: ' +
|
|
94
|
+
`${(typeof contents.toString === 'function' ? contents.toString() : String(contents))}]`
|
|
95
|
+
})
|
|
96
|
+
return messages
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const content of contents) {
|
|
100
|
+
if (typeof content === 'string') {
|
|
101
|
+
messages.push({ content })
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isPart(content)) {
|
|
106
|
+
messages.push(extractMessageFromPart(content))
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
messages.push(...extractMessagesFromContent(content))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return messages
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractOutputMessages (result) {
|
|
117
|
+
if (!result) return [{ content: '' }]
|
|
118
|
+
const { response } = result
|
|
119
|
+
|
|
120
|
+
if (!response) return [{ content: '' }]
|
|
121
|
+
|
|
122
|
+
const outputMessages = []
|
|
123
|
+
const candidates = response.candidates || []
|
|
124
|
+
for (const candidate of candidates) {
|
|
125
|
+
const content = candidate.content || ''
|
|
126
|
+
outputMessages.push(...extractMessagesFromContent(content))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return outputMessages
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function extractMessagesFromContent (content) {
|
|
133
|
+
const messages = []
|
|
134
|
+
|
|
135
|
+
const role = content.role || ''
|
|
136
|
+
const parts = content.parts || []
|
|
137
|
+
if (parts == null || parts.length === 0 || !Array.isArray(parts)) {
|
|
138
|
+
const message = {
|
|
139
|
+
content:
|
|
140
|
+
`[Non-text content object: ${(typeof content.toString === 'function' ? content.toString() : String(content))}]`
|
|
141
|
+
}
|
|
142
|
+
if (role) message.role = role
|
|
143
|
+
messages.push(message)
|
|
144
|
+
return messages
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const part of parts) {
|
|
148
|
+
const message = extractMessageFromPart(part, role)
|
|
149
|
+
messages.push(message)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return messages
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function extractMessageFromPart (part, role) {
|
|
156
|
+
const text = part.text || ''
|
|
157
|
+
const functionCall = part.functionCall
|
|
158
|
+
const functionResponse = part.functionResponse
|
|
159
|
+
|
|
160
|
+
const message = { content: text }
|
|
161
|
+
if (role) message.role = role
|
|
162
|
+
if (functionCall) {
|
|
163
|
+
message.toolCalls = [{
|
|
164
|
+
name: functionCall.name,
|
|
165
|
+
arguments: functionCall.args
|
|
166
|
+
}]
|
|
167
|
+
}
|
|
168
|
+
if (functionResponse) {
|
|
169
|
+
message.content = `[tool result: ${functionResponse.response}]`
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return message
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function extractMetrics (result) {
|
|
176
|
+
if (!result) return {}
|
|
177
|
+
const { response } = result
|
|
178
|
+
|
|
179
|
+
if (!response) return {}
|
|
180
|
+
|
|
181
|
+
const tokenCounts = response.usageMetadata
|
|
182
|
+
const metrics = {}
|
|
183
|
+
if (tokenCounts) {
|
|
184
|
+
metrics.inputTokens = tokenCounts.promptTokenCount
|
|
185
|
+
metrics.outputTokens = tokenCounts.candidatesTokenCount
|
|
186
|
+
metrics.totalTokens = tokenCounts.totalTokenCount
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return metrics
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function isPart (part) {
|
|
193
|
+
return part.text || part.functionCall || part.functionResponse
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = VertexAILLMObsPlugin
|
|
@@ -430,6 +430,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
430
430
|
modelProvider,
|
|
431
431
|
sessionId,
|
|
432
432
|
mlApp,
|
|
433
|
+
_decorator,
|
|
433
434
|
...spanOptions
|
|
434
435
|
} = options
|
|
435
436
|
|
|
@@ -438,6 +439,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
438
439
|
modelName,
|
|
439
440
|
modelProvider,
|
|
440
441
|
sessionId,
|
|
442
|
+
_decorator,
|
|
441
443
|
spanOptions
|
|
442
444
|
}
|
|
443
445
|
}
|
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
METADATA,
|
|
8
8
|
INPUT_MESSAGES,
|
|
9
9
|
INPUT_VALUE,
|
|
10
|
+
INTEGRATION,
|
|
10
11
|
OUTPUT_MESSAGES,
|
|
11
12
|
INPUT_DOCUMENTS,
|
|
12
13
|
OUTPUT_DOCUMENTS,
|
|
@@ -26,6 +27,8 @@ const {
|
|
|
26
27
|
ERROR_STACK
|
|
27
28
|
} = require('../constants')
|
|
28
29
|
|
|
30
|
+
const telemetry = require('./telemetry')
|
|
31
|
+
|
|
29
32
|
const LLMObsTagger = require('./tagger')
|
|
30
33
|
|
|
31
34
|
const tracerVersion = require('../../../../package.json').version
|
|
@@ -48,6 +51,7 @@ class LLMObsSpanProcessor {
|
|
|
48
51
|
|
|
49
52
|
try {
|
|
50
53
|
const formattedEvent = this.format(span)
|
|
54
|
+
telemetry.incrementLLMObsSpanFinishedCount(span)
|
|
51
55
|
this._writer.append(formattedEvent)
|
|
52
56
|
} catch (e) {
|
|
53
57
|
// this should be a rare case
|
|
@@ -186,6 +190,8 @@ class LLMObsSpanProcessor {
|
|
|
186
190
|
const errType = span.context()._tags[ERROR_TYPE] || error?.name
|
|
187
191
|
if (errType) tags.error_type = errType
|
|
188
192
|
if (sessionId) tags.session_id = sessionId
|
|
193
|
+
const integration = LLMObsTagger.tagMap.get(span)?.[INTEGRATION]
|
|
194
|
+
if (integration) tags.integration = integration
|
|
189
195
|
const existingTags = LLMObsTagger.tagMap.get(span)?.[TAGS] || {}
|
|
190
196
|
if (existingTags) tags = { ...tags, ...existingTags }
|
|
191
197
|
return Object.entries(tags).map(([key, value]) => `${key}:${value ?? ''}`)
|
|
@@ -22,7 +22,9 @@ const {
|
|
|
22
22
|
ROOT_PARENT_ID,
|
|
23
23
|
INPUT_TOKENS_METRIC_KEY,
|
|
24
24
|
OUTPUT_TOKENS_METRIC_KEY,
|
|
25
|
-
TOTAL_TOKENS_METRIC_KEY
|
|
25
|
+
TOTAL_TOKENS_METRIC_KEY,
|
|
26
|
+
INTEGRATION,
|
|
27
|
+
DECORATOR
|
|
26
28
|
} = require('./constants/tags')
|
|
27
29
|
|
|
28
30
|
// global registry of LLMObs spans
|
|
@@ -51,7 +53,9 @@ class LLMObsTagger {
|
|
|
51
53
|
mlApp,
|
|
52
54
|
parent,
|
|
53
55
|
kind,
|
|
54
|
-
name
|
|
56
|
+
name,
|
|
57
|
+
integration,
|
|
58
|
+
_decorator
|
|
55
59
|
} = {}) {
|
|
56
60
|
if (!this._config.llmobs.enabled) return
|
|
57
61
|
if (!kind) return // do not register it in the map if it doesn't have an llmobs span kind
|
|
@@ -66,6 +70,8 @@ class LLMObsTagger {
|
|
|
66
70
|
|
|
67
71
|
sessionId = sessionId || registry.get(parent)?.[SESSION_ID]
|
|
68
72
|
if (sessionId) this._setTag(span, SESSION_ID, sessionId)
|
|
73
|
+
if (integration) this._setTag(span, INTEGRATION, integration)
|
|
74
|
+
if (_decorator) this._setTag(span, DECORATOR, _decorator)
|
|
69
75
|
|
|
70
76
|
if (!mlApp) mlApp = registry.get(parent)?.[ML_APP] || this._config.llmobs.mlApp
|
|
71
77
|
this._setTag(span, ML_APP, mlApp)
|
|
@@ -1,12 +1,93 @@
|
|
|
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 recordLLMObsRawSpanSize (event, rawEventSize) {
|
|
78
|
+
const tags = extractTagsFromSpanEvent(event)
|
|
79
|
+
llmobsMetrics.distribution('span.raw_size', tags).track(rawEventSize)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function recordLLMObsSpanSize (event, eventSize, shouldTruncate) {
|
|
83
|
+
const tags = extractTagsFromSpanEvent(event)
|
|
84
|
+
tags.truncated = Number(shouldTruncate)
|
|
85
|
+
llmobsMetrics.distribution('span.size', tags).track(eventSize)
|
|
86
|
+
}
|
|
87
|
+
|
|
10
88
|
module.exports = {
|
|
11
|
-
incrementLLMObsSpanStartCount
|
|
89
|
+
incrementLLMObsSpanStartCount,
|
|
90
|
+
incrementLLMObsSpanFinishedCount,
|
|
91
|
+
recordLLMObsRawSpanSize,
|
|
92
|
+
recordLLMObsSpanSize
|
|
12
93
|
}
|
|
@@ -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
|
+
}
|