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.
Files changed (51) hide show
  1. package/package.json +4 -4
  2. package/packages/datadog-instrumentations/src/cucumber.js +61 -23
  3. package/packages/datadog-instrumentations/src/dd-trace-api.js +7 -0
  4. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  5. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  6. package/packages/datadog-instrumentations/src/jest.js +134 -48
  7. package/packages/datadog-instrumentations/src/mocha/main.js +20 -4
  8. package/packages/datadog-instrumentations/src/mocha/utils.js +89 -30
  9. package/packages/datadog-instrumentations/src/mocha/worker.js +3 -1
  10. package/packages/datadog-instrumentations/src/playwright.js +97 -17
  11. package/packages/datadog-instrumentations/src/router.js +1 -0
  12. package/packages/datadog-instrumentations/src/tedious.js +13 -10
  13. package/packages/datadog-instrumentations/src/vitest.js +77 -17
  14. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  15. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +69 -20
  16. package/packages/datadog-plugin-cypress/src/support.js +39 -10
  17. package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +8 -186
  18. package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +186 -0
  19. package/packages/datadog-plugin-google-cloud-vertexai/src/utils.js +19 -0
  20. package/packages/datadog-plugin-jest/src/index.js +38 -5
  21. package/packages/datadog-plugin-mocha/src/index.js +28 -5
  22. package/packages/datadog-plugin-playwright/src/index.js +22 -2
  23. package/packages/datadog-plugin-tedious/src/index.js +14 -9
  24. package/packages/datadog-plugin-vitest/src/index.js +46 -14
  25. package/packages/dd-trace/src/appsec/blocking.js +2 -0
  26. package/packages/dd-trace/src/appsec/graphql.js +3 -1
  27. package/packages/dd-trace/src/appsec/reporter.js +13 -8
  28. package/packages/dd-trace/src/appsec/telemetry/common.js +6 -3
  29. package/packages/dd-trace/src/appsec/telemetry/index.js +20 -4
  30. package/packages/dd-trace/src/appsec/telemetry/waf.js +29 -9
  31. package/packages/dd-trace/src/appsec/waf/waf_manager.js +16 -7
  32. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -2
  33. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -1
  34. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
  35. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  36. package/packages/dd-trace/src/llmobs/noop.js +3 -3
  37. package/packages/dd-trace/src/llmobs/plugins/base.js +1 -1
  38. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +2 -1
  39. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +196 -0
  40. package/packages/dd-trace/src/llmobs/sdk.js +2 -0
  41. package/packages/dd-trace/src/llmobs/span_processor.js +6 -0
  42. package/packages/dd-trace/src/llmobs/tagger.js +8 -2
  43. package/packages/dd-trace/src/llmobs/telemetry.js +82 -1
  44. package/packages/dd-trace/src/llmobs/writers/spans/base.js +10 -1
  45. package/packages/dd-trace/src/plugin_manager.js +0 -3
  46. package/packages/dd-trace/src/plugins/ci_plugin.js +16 -26
  47. package/packages/dd-trace/src/plugins/database.js +4 -4
  48. package/packages/dd-trace/src/plugins/plugin.js +2 -0
  49. package/packages/dd-trace/src/plugins/util/test.js +62 -1
  50. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -6
  51. package/packages/dd-trace/src/telemetry/send-data.js +5 -1
@@ -1,195 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const { MEASURED } = require('../../../ext/tags')
4
- const { storage } = require('../../datadog-core')
5
- const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
6
- const makeUtilities = require('../../dd-trace/src/plugins/util/llm')
3
+ const CompositePlugin = require('../../dd-trace/src/plugins/composite')
4
+ const GoogleVertexAITracingPlugin = require('./tracing')
5
+ const VertexAILLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/vertexai')
7
6
 
8
- class GoogleCloudVertexAIPlugin extends TracingPlugin {
7
+ class GoogleCloudVertexAIPlugin extends CompositePlugin {
9
8
  static get id () { return 'google-cloud-vertexai' }
10
- static get prefix () {
11
- return 'tracing:apm:vertexai:request'
12
- }
13
-
14
- constructor () {
15
- super(...arguments)
16
-
17
- Object.assign(this, makeUtilities('vertexai', this._tracerConfig))
18
- }
19
-
20
- bindStart (ctx) {
21
- const { instance, request, resource, stream } = ctx
22
-
23
- const tags = this.tagRequest(request, instance, stream)
24
-
25
- const span = this.startSpan('vertexai.request', {
26
- service: this.config.service,
27
- resource,
28
- kind: 'client',
29
- meta: {
30
- [MEASURED]: 1,
31
- ...tags
32
- }
33
- }, false)
34
-
35
- const store = storage('legacy').getStore() || {}
36
- ctx.currentStore = { ...store, span }
37
-
38
- return ctx.currentStore
39
- }
40
-
41
- asyncEnd (ctx) {
42
- const span = ctx.currentStore?.span
43
- if (!span) return
44
-
45
- const { result } = ctx
46
-
47
- const response = result?.response
48
- if (response) {
49
- const tags = this.tagResponse(response)
50
- span.addTags(tags)
51
- }
52
-
53
- span.finish()
54
- }
55
-
56
- tagRequest (request, instance, stream) {
57
- const model = extractModel(instance)
58
- const tags = {
59
- 'vertexai.request.model': model
60
- }
61
-
62
- const history = instance.historyInternal
63
- let contents = typeof request === 'string' || Array.isArray(request) ? request : request.contents
64
- if (history) {
65
- contents = [...history, ...(Array.isArray(contents) ? contents : [contents])]
66
- }
67
-
68
- const generationConfig = instance.generationConfig || {}
69
- for (const key of Object.keys(generationConfig)) {
70
- const transformedKey = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()
71
- tags[`vertexai.request.generation_config.${transformedKey}`] = JSON.stringify(generationConfig[key])
72
- }
73
-
74
- if (stream) {
75
- tags['vertexai.request.stream'] = true
76
- }
77
-
78
- if (!this.isPromptCompletionSampled()) return tags
79
-
80
- const systemInstructions = extractSystemInstructions(instance)
81
-
82
- for (const [idx, systemInstruction] of systemInstructions.entries()) {
83
- tags[`vertexai.request.system_instruction.${idx}.text`] = systemInstruction
84
- }
85
-
86
- if (typeof contents === 'string') {
87
- tags['vertexai.request.contents.0.text'] = contents
88
- return tags
89
- }
90
-
91
- for (const [contentIdx, content] of contents.entries()) {
92
- this.tagRequestContent(tags, content, contentIdx)
9
+ static get plugins () {
10
+ return {
11
+ llmobs: VertexAILLMObsPlugin,
12
+ tracing: GoogleVertexAITracingPlugin
93
13
  }
94
-
95
- return tags
96
14
  }
97
-
98
- tagRequestPart (part, tags, partIdx, contentIdx) {
99
- tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.text`] = this.normalize(part.text)
100
-
101
- const functionCall = part.functionCall
102
- const functionResponse = part.functionResponse
103
-
104
- if (functionCall) {
105
- tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.name`] = functionCall.name
106
- tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.args`] =
107
- this.normalize(JSON.stringify(functionCall.args))
108
- }
109
- if (functionResponse) {
110
- tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.name`] =
111
- functionResponse.name
112
- tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.response`] =
113
- this.normalize(JSON.stringify(functionResponse.response))
114
- }
115
- }
116
-
117
- tagRequestContent (tags, content, contentIdx) {
118
- if (typeof content === 'string') {
119
- tags[`vertexai.request.contents.${contentIdx}.text`] = this.normalize(content)
120
- return
121
- }
122
-
123
- if (content.text || content.functionCall || content.functionResponse) {
124
- this.tagRequestPart(content, tags, 0, contentIdx)
125
- return
126
- }
127
-
128
- const { role, parts } = content
129
- if (role) {
130
- tags[`vertexai.request.contents.${contentIdx}.role`] = role
131
- }
132
-
133
- for (const [partIdx, part] of parts.entries()) {
134
- this.tagRequestPart(part, tags, partIdx, contentIdx)
135
- }
136
- }
137
-
138
- tagResponse (response) {
139
- const tags = {}
140
-
141
- const candidates = response.candidates
142
- for (const [candidateIdx, candidate] of candidates.entries()) {
143
- const finishReason = candidate.finishReason
144
- if (finishReason) {
145
- tags[`vertexai.response.candidates.${candidateIdx}.finish_reason`] = finishReason
146
- }
147
- const candidateContent = candidate.content
148
- const role = candidateContent.role
149
- tags[`vertexai.response.candidates.${candidateIdx}.content.role`] = role
150
-
151
- if (!this.isPromptCompletionSampled()) continue
152
-
153
- const parts = candidateContent.parts
154
- for (const [partIdx, part] of parts.entries()) {
155
- const text = part.text
156
- tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.text`] =
157
- this.normalize(String(text))
158
-
159
- const functionCall = part.functionCall
160
- if (!functionCall) continue
161
-
162
- tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.name`] =
163
- functionCall.name
164
- tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.args`] =
165
- this.normalize(JSON.stringify(functionCall.args))
166
- }
167
- }
168
-
169
- const tokenCounts = response.usageMetadata
170
- if (tokenCounts) {
171
- tags['vertexai.response.usage.prompt_tokens'] = tokenCounts.promptTokenCount
172
- tags['vertexai.response.usage.completion_tokens'] = tokenCounts.candidatesTokenCount
173
- tags['vertexai.response.usage.total_tokens'] = tokenCounts.totalTokenCount
174
- }
175
-
176
- return tags
177
- }
178
- }
179
-
180
- function extractModel (instance) {
181
- const model = instance.model || instance.resourcePath || instance.publisherModelEndpoint
182
- return model?.split('/').pop()
183
- }
184
-
185
- function extractSystemInstructions (instance) {
186
- // systemInstruction is either a string or a Content object
187
- // Content objects have parts (Part[]) and a role
188
- const systemInstruction = instance.systemInstruction
189
- if (!systemInstruction) return []
190
- if (typeof systemInstruction === 'string') return [systemInstruction]
191
-
192
- return systemInstruction.parts?.map(part => part.text)
193
15
  }
194
16
 
195
17
  module.exports = GoogleCloudVertexAIPlugin
@@ -0,0 +1,186 @@
1
+ 'use strict'
2
+
3
+ const { MEASURED } = require('../../../ext/tags')
4
+ const { storage } = require('../../datadog-core')
5
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
6
+ const makeUtilities = require('../../dd-trace/src/plugins/util/llm')
7
+
8
+ const {
9
+ extractModel,
10
+ extractSystemInstructions
11
+ } = require('./utils')
12
+
13
+ class GoogleCloudVertexAITracingPlugin extends TracingPlugin {
14
+ static get id () { return 'google-cloud-vertexai' }
15
+ static get prefix () {
16
+ return 'tracing:apm:vertexai:request'
17
+ }
18
+
19
+ constructor () {
20
+ super(...arguments)
21
+
22
+ Object.assign(this, makeUtilities('vertexai', this._tracerConfig))
23
+ }
24
+
25
+ bindStart (ctx) {
26
+ const { instance, request, resource, stream } = ctx
27
+
28
+ const tags = this.tagRequest(request, instance, stream)
29
+
30
+ const span = this.startSpan('vertexai.request', {
31
+ service: this.config.service,
32
+ resource,
33
+ kind: 'client',
34
+ meta: {
35
+ [MEASURED]: 1,
36
+ ...tags
37
+ }
38
+ }, false)
39
+
40
+ const store = storage('legacy').getStore() || {}
41
+ ctx.currentStore = { ...store, span }
42
+
43
+ return ctx.currentStore
44
+ }
45
+
46
+ asyncEnd (ctx) {
47
+ const span = ctx.currentStore?.span
48
+ if (!span) return
49
+
50
+ const { result } = ctx
51
+
52
+ const response = result?.response
53
+ if (response) {
54
+ const tags = this.tagResponse(response)
55
+ span.addTags(tags)
56
+ }
57
+
58
+ span.finish()
59
+ }
60
+
61
+ tagRequest (request, instance, stream) {
62
+ const model = extractModel(instance)
63
+ const tags = {
64
+ 'vertexai.request.model': model
65
+ }
66
+
67
+ const history = instance.historyInternal
68
+
69
+ let contents = typeof request === 'string' || Array.isArray(request) ? request : request.contents
70
+ if (history) {
71
+ contents = [...history, ...(Array.isArray(contents) ? contents : [contents])]
72
+ }
73
+
74
+ const generationConfig = instance.generationConfig || {}
75
+ for (const key of Object.keys(generationConfig)) {
76
+ const transformedKey = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()
77
+ tags[`vertexai.request.generation_config.${transformedKey}`] = JSON.stringify(generationConfig[key])
78
+ }
79
+
80
+ if (stream) {
81
+ tags['vertexai.request.stream'] = true
82
+ }
83
+
84
+ if (!this.isPromptCompletionSampled()) return tags
85
+
86
+ const systemInstructions = extractSystemInstructions(instance)
87
+
88
+ for (const [idx, systemInstruction] of systemInstructions.entries()) {
89
+ tags[`vertexai.request.system_instruction.${idx}.text`] = systemInstruction
90
+ }
91
+
92
+ if (typeof contents === 'string') {
93
+ tags['vertexai.request.contents.0.text'] = contents
94
+ return tags
95
+ }
96
+
97
+ for (const [contentIdx, content] of contents.entries()) {
98
+ this.tagRequestContent(tags, content, contentIdx)
99
+ }
100
+
101
+ return tags
102
+ }
103
+
104
+ tagRequestPart (part, tags, partIdx, contentIdx) {
105
+ tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.text`] = this.normalize(part.text)
106
+
107
+ const functionCall = part.functionCall
108
+ const functionResponse = part.functionResponse
109
+
110
+ if (functionCall) {
111
+ tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.name`] = functionCall.name
112
+ tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_call.args`] =
113
+ this.normalize(JSON.stringify(functionCall.args))
114
+ }
115
+ if (functionResponse) {
116
+ tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.name`] =
117
+ functionResponse.name
118
+ tags[`vertexai.request.contents.${contentIdx}.parts.${partIdx}.function_response.response`] =
119
+ this.normalize(JSON.stringify(functionResponse.response))
120
+ }
121
+ }
122
+
123
+ tagRequestContent (tags, content, contentIdx) {
124
+ if (typeof content === 'string') {
125
+ tags[`vertexai.request.contents.${contentIdx}.text`] = this.normalize(content)
126
+ return
127
+ }
128
+
129
+ if (content.text || content.functionCall || content.functionResponse) {
130
+ this.tagRequestPart(content, tags, 0, contentIdx)
131
+ return
132
+ }
133
+
134
+ const { role, parts } = content
135
+ if (role) {
136
+ tags[`vertexai.request.contents.${contentIdx}.role`] = role
137
+ }
138
+
139
+ for (const [partIdx, part] of parts.entries()) {
140
+ this.tagRequestPart(part, tags, partIdx, contentIdx)
141
+ }
142
+ }
143
+
144
+ tagResponse (response) {
145
+ const tags = {}
146
+
147
+ const candidates = response.candidates
148
+ for (const [candidateIdx, candidate] of candidates.entries()) {
149
+ const finishReason = candidate.finishReason
150
+ if (finishReason) {
151
+ tags[`vertexai.response.candidates.${candidateIdx}.finish_reason`] = finishReason
152
+ }
153
+ const candidateContent = candidate.content
154
+ const role = candidateContent.role
155
+ tags[`vertexai.response.candidates.${candidateIdx}.content.role`] = role
156
+
157
+ if (!this.isPromptCompletionSampled()) continue
158
+
159
+ const parts = candidateContent.parts
160
+ for (const [partIdx, part] of parts.entries()) {
161
+ const text = part.text
162
+ tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.text`] =
163
+ this.normalize(String(text))
164
+
165
+ const functionCall = part.functionCall
166
+ if (!functionCall) continue
167
+
168
+ tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.name`] =
169
+ functionCall.name
170
+ tags[`vertexai.response.candidates.${candidateIdx}.content.parts.${partIdx}.function_call.args`] =
171
+ this.normalize(JSON.stringify(functionCall.args))
172
+ }
173
+ }
174
+
175
+ const tokenCounts = response.usageMetadata
176
+ if (tokenCounts) {
177
+ tags['vertexai.response.usage.prompt_tokens'] = tokenCounts.promptTokenCount
178
+ tags['vertexai.response.usage.completion_tokens'] = tokenCounts.candidatesTokenCount
179
+ tags['vertexai.response.usage.total_tokens'] = tokenCounts.totalTokenCount
180
+ }
181
+
182
+ return tags
183
+ }
184
+ }
185
+
186
+ module.exports = GoogleCloudVertexAITracingPlugin
@@ -0,0 +1,19 @@
1
+ function extractModel (instance) {
2
+ const model = instance.model || instance.resourcePath || instance.publisherModelEndpoint
3
+ return model?.split('/').pop()
4
+ }
5
+
6
+ function extractSystemInstructions (instance) {
7
+ // systemInstruction is either a string or a Content object
8
+ // Content objects have parts (Part[]) and a role
9
+ const systemInstruction = instance.systemInstruction
10
+ if (!systemInstruction) return []
11
+ if (typeof systemInstruction === 'string') return [systemInstruction]
12
+
13
+ return systemInstruction.parts?.map(part => part.text)
14
+ }
15
+
16
+ module.exports = {
17
+ extractModel,
18
+ extractSystemInstructions
19
+ }
@@ -27,7 +27,10 @@ const {
27
27
  TEST_RETRY_REASON,
28
28
  TEST_MANAGEMENT_ENABLED,
29
29
  TEST_MANAGEMENT_IS_QUARANTINED,
30
- TEST_MANAGEMENT_IS_DISABLED
30
+ TEST_MANAGEMENT_IS_DISABLED,
31
+ TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
32
+ TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
33
+ TEST_HAS_FAILED_ALL_RETRIES
31
34
  } = require('../../dd-trace/src/plugins/util/test')
32
35
  const { COMPONENT } = require('../../dd-trace/src/constants')
33
36
  const id = require('../../dd-trace/src/id')
@@ -177,6 +180,7 @@ class JestPlugin extends CiPlugin {
177
180
  config._ddRepositoryRoot = this.repositoryRoot
178
181
  config._ddIsFlakyTestRetriesEnabled = this.libraryConfig?.isFlakyTestRetriesEnabled ?? false
179
182
  config._ddIsTestManagementTestsEnabled = this.libraryConfig?.isTestManagementEnabled ?? false
183
+ config._ddTestManagementAttemptToFixRetries = this.libraryConfig?.testManagementAttemptToFixRetries ?? 0
180
184
  config._ddFlakyTestRetriesCount = this.libraryConfig?.flakyTestRetriesCount
181
185
  config._ddIsDiEnabled = this.libraryConfig?.isDiEnabled ?? false
182
186
  config._ddIsKnownTestsEnabled = this.libraryConfig?.isKnownTestsEnabled ?? false
@@ -336,14 +340,22 @@ class JestPlugin extends CiPlugin {
336
340
  this.activeTestSpan = span
337
341
  })
338
342
 
339
- this.addSub('ci:jest:test:finish', ({ status, testStartLine, isQuarantined }) => {
343
+ this.addSub('ci:jest:test:finish', ({
344
+ status,
345
+ testStartLine,
346
+ attemptToFixPassed,
347
+ failedAllTests
348
+ }) => {
340
349
  const span = storage('legacy').getStore().span
341
350
  span.setTag(TEST_STATUS, status)
342
351
  if (testStartLine) {
343
352
  span.setTag(TEST_SOURCE_START, testStartLine)
344
353
  }
345
- if (isQuarantined) {
346
- span.setTag(TEST_MANAGEMENT_IS_QUARANTINED, 'true')
354
+ if (attemptToFixPassed) {
355
+ span.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'true')
356
+ }
357
+ if (failedAllTests) {
358
+ span.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
347
359
  }
348
360
 
349
361
  const spanTags = span.context()._tags
@@ -407,7 +419,11 @@ class JestPlugin extends CiPlugin {
407
419
  testSourceFile,
408
420
  isNew,
409
421
  isEfdRetry,
410
- isJestRetry
422
+ isAttemptToFix,
423
+ isAttemptToFixRetry,
424
+ isJestRetry,
425
+ isDisabled,
426
+ isQuarantined
411
427
  } = test
412
428
 
413
429
  const extraTags = {
@@ -425,6 +441,23 @@ class JestPlugin extends CiPlugin {
425
441
  extraTags[JEST_DISPLAY_NAME] = displayName
426
442
  }
427
443
 
444
+ if (isAttemptToFix) {
445
+ extraTags[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX] = 'true'
446
+ }
447
+
448
+ if (isAttemptToFixRetry) {
449
+ extraTags[TEST_IS_RETRY] = 'true'
450
+ extraTags[TEST_RETRY_REASON] = 'attempt_to_fix'
451
+ }
452
+
453
+ if (isDisabled) {
454
+ extraTags[TEST_MANAGEMENT_IS_DISABLED] = 'true'
455
+ }
456
+
457
+ if (isQuarantined) {
458
+ extraTags[TEST_MANAGEMENT_IS_QUARANTINED] = 'true'
459
+ }
460
+
428
461
  if (isNew) {
429
462
  extraTags[TEST_IS_NEW] = 'true'
430
463
  if (isEfdRetry) {
@@ -17,7 +17,6 @@ const {
17
17
  TEST_CODE_OWNERS,
18
18
  ITR_CORRELATION_ID,
19
19
  TEST_SOURCE_FILE,
20
- removeEfdStringFromTestName,
21
20
  TEST_IS_NEW,
22
21
  TEST_IS_RETRY,
23
22
  TEST_EARLY_FLAKE_ENABLED,
@@ -34,7 +33,10 @@ const {
34
33
  TEST_RETRY_REASON,
35
34
  TEST_MANAGEMENT_ENABLED,
36
35
  TEST_MANAGEMENT_IS_QUARANTINED,
37
- TEST_MANAGEMENT_IS_DISABLED
36
+ TEST_MANAGEMENT_IS_DISABLED,
37
+ TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
38
+ TEST_HAS_FAILED_ALL_RETRIES,
39
+ TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED
38
40
  } = require('../../dd-trace/src/plugins/util/test')
39
41
  const { COMPONENT } = require('../../dd-trace/src/constants')
40
42
  const {
@@ -199,7 +201,14 @@ class MochaPlugin extends CiPlugin {
199
201
  this.tracer._exporter.flush()
200
202
  })
201
203
 
202
- this.addSub('ci:mocha:test:finish', ({ status, hasBeenRetried, isLastRetry }) => {
204
+ this.addSub('ci:mocha:test:finish', ({
205
+ status,
206
+ hasBeenRetried,
207
+ isLastRetry,
208
+ hasFailedAllRetries,
209
+ attemptToFixPassed,
210
+ isAttemptToFixRetry
211
+ }) => {
203
212
  const store = storage('legacy').getStore()
204
213
  const span = store?.span
205
214
 
@@ -208,6 +217,16 @@ class MochaPlugin extends CiPlugin {
208
217
  if (hasBeenRetried) {
209
218
  span.setTag(TEST_IS_RETRY, 'true')
210
219
  }
220
+ if (hasFailedAllRetries) {
221
+ span.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
222
+ }
223
+ if (attemptToFixPassed) {
224
+ span.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'true')
225
+ }
226
+ if (isAttemptToFixRetry) {
227
+ span.setTag(TEST_IS_RETRY, 'true')
228
+ span.setTag(TEST_RETRY_REASON, 'attempt_to_fix')
229
+ }
211
230
 
212
231
  const spanTags = span.context()._tags
213
232
  this.telemetry.ciVisEvent(
@@ -403,18 +422,18 @@ class MochaPlugin extends CiPlugin {
403
422
 
404
423
  startTestSpan (testInfo) {
405
424
  const {
425
+ testName,
406
426
  testSuiteAbsolutePath,
407
427
  title,
408
428
  isNew,
409
429
  isEfdRetry,
410
430
  testStartLine,
411
431
  isParallel,
432
+ isAttemptToFix,
412
433
  isDisabled,
413
434
  isQuarantined
414
435
  } = testInfo
415
436
 
416
- const testName = removeEfdStringFromTestName(testInfo.testName)
417
-
418
437
  const extraTags = {}
419
438
  const testParametersString = getTestParametersString(this._testTitleToParams, title)
420
439
  if (testParametersString) {
@@ -429,6 +448,10 @@ class MochaPlugin extends CiPlugin {
429
448
  extraTags[MOCHA_IS_PARALLEL] = 'true'
430
449
  }
431
450
 
451
+ if (isAttemptToFix) {
452
+ extraTags[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX] = 'true'
453
+ }
454
+
432
455
  if (isDisabled) {
433
456
  extraTags[TEST_MANAGEMENT_IS_DISABLED] = 'true'
434
457
  }
@@ -20,7 +20,10 @@ const {
20
20
  TEST_MANAGEMENT_IS_QUARANTINED,
21
21
  TEST_MANAGEMENT_ENABLED,
22
22
  TEST_BROWSER_NAME,
23
- TEST_MANAGEMENT_IS_DISABLED
23
+ TEST_MANAGEMENT_IS_DISABLED,
24
+ TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
25
+ TEST_HAS_FAILED_ALL_RETRIES,
26
+ TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED
24
27
  } = require('../../dd-trace/src/plugins/util/test')
25
28
  const { RESOURCE_NAME } = require('../../../ext/tags')
26
29
  const { COMPONENT } = require('../../dd-trace/src/constants')
@@ -162,7 +165,11 @@ class PlaywrightPlugin extends CiPlugin {
162
165
  isNew,
163
166
  isEfdRetry,
164
167
  isRetry,
165
- isQuarantined
168
+ isAttemptToFix,
169
+ isQuarantined,
170
+ isAttemptToFixRetry,
171
+ hasFailedAllRetries,
172
+ hasPassedAttemptToFixRetries
166
173
  }) => {
167
174
  const store = storage('legacy').getStore()
168
175
  const span = store && store.span
@@ -186,6 +193,19 @@ class PlaywrightPlugin extends CiPlugin {
186
193
  if (isRetry) {
187
194
  span.setTag(TEST_IS_RETRY, 'true')
188
195
  }
196
+ if (hasFailedAllRetries) {
197
+ span.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
198
+ }
199
+ if (isAttemptToFix) {
200
+ span.setTag(TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX, 'true')
201
+ }
202
+ if (isAttemptToFixRetry) {
203
+ span.setTag(TEST_IS_RETRY, 'true')
204
+ span.setTag(TEST_RETRY_REASON, 'attempt_to_fix')
205
+ }
206
+ if (hasPassedAttemptToFixRetries) {
207
+ span.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'true')
208
+ }
189
209
  if (isQuarantined) {
190
210
  span.setTag(TEST_MANAGEMENT_IS_QUARANTINED, 'true')
191
211
  }
@@ -8,22 +8,27 @@ class TediousPlugin extends DatabasePlugin {
8
8
  static get operation () { return 'request' } // TODO: change to match other database plugins
9
9
  static get system () { return 'mssql' }
10
10
 
11
- start ({ queryOrProcedure, connectionConfig }) {
12
- this.startSpan(this.operationName(), {
13
- service: this.serviceName({ pluginConfig: this.config, system: this.system }),
14
- resource: queryOrProcedure,
11
+ start (payload) {
12
+ const service = this.serviceName({ pluginConfig: this.config, system: this.system })
13
+ const span = this.startSpan(this.operationName(), {
14
+ service,
15
+ resource: payload.queryOrProcedure,
15
16
  type: 'sql',
16
17
  kind: 'client',
17
18
  meta: {
18
19
  'db.type': 'mssql',
19
20
  component: 'tedious',
20
- 'out.host': connectionConfig.server,
21
- [CLIENT_PORT_KEY]: connectionConfig.options.port,
22
- 'db.user': connectionConfig.userName || connectionConfig.authentication.options.userName,
23
- 'db.name': connectionConfig.options.database,
24
- 'db.instance': connectionConfig.options.instanceName
21
+ 'out.host': payload.connectionConfig.server,
22
+ [CLIENT_PORT_KEY]: payload.connectionConfig.options.port,
23
+ 'db.user': payload.connectionConfig.userName || payload.connectionConfig.authentication.options.userName,
24
+ 'db.name': payload.connectionConfig.options.database,
25
+ 'db.instance': payload.connectionConfig.options.instanceName
25
26
  }
26
27
  })
28
+
29
+ // SQL Server includes comments when caching queries
30
+ // For that reason we allow service mode but not full mode
31
+ payload.sql = this.injectDbmQuery(span, payload.queryOrProcedure, service, true)
27
32
  }
28
33
  }
29
34