dd-trace 5.41.1 → 5.42.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 (59) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/index.d.ts +8 -1
  3. package/package.json +6 -3
  4. package/packages/datadog-esbuild/index.js +3 -1
  5. package/packages/datadog-instrumentations/src/cucumber.js +37 -29
  6. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +102 -0
  7. package/packages/datadog-instrumentations/src/{check_require_cache.js → helpers/check-require-cache.js} +2 -2
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -2
  9. package/packages/datadog-instrumentations/src/helpers/register.js +4 -1
  10. package/packages/datadog-instrumentations/src/jest.js +72 -49
  11. package/packages/datadog-instrumentations/src/langchain.js +29 -10
  12. package/packages/datadog-instrumentations/src/mocha/main.js +53 -34
  13. package/packages/datadog-instrumentations/src/mocha/utils.js +34 -24
  14. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -8
  15. package/packages/datadog-instrumentations/src/openai.js +1 -1
  16. package/packages/datadog-instrumentations/src/playwright.js +37 -30
  17. package/packages/datadog-instrumentations/src/vitest.js +64 -29
  18. package/packages/datadog-plugin-cucumber/src/index.js +13 -4
  19. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +41 -35
  20. package/packages/datadog-plugin-cypress/src/plugin.js +10 -0
  21. package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +195 -0
  22. package/packages/datadog-plugin-jest/src/index.js +18 -6
  23. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +4 -1
  24. package/packages/datadog-plugin-mocha/src/index.js +13 -4
  25. package/packages/datadog-plugin-playwright/src/index.js +19 -5
  26. package/packages/datadog-plugin-vitest/src/index.js +41 -17
  27. package/packages/dd-trace/src/appsec/api_security_sampler.js +7 -3
  28. package/packages/dd-trace/src/appsec/blocking.js +23 -16
  29. package/packages/dd-trace/src/appsec/graphql.js +13 -6
  30. package/packages/dd-trace/src/appsec/rasp/utils.js +0 -1
  31. package/packages/dd-trace/src/appsec/reporter.js +35 -0
  32. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -3
  33. package/packages/dd-trace/src/appsec/telemetry/index.js +5 -1
  34. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -1
  35. package/packages/dd-trace/src/appsec/telemetry/waf.js +16 -1
  36. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +43 -13
  37. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  38. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +15 -14
  39. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -2
  40. package/packages/dd-trace/src/ci-visibility/telemetry.js +2 -1
  41. package/packages/dd-trace/src/ci-visibility/{quarantined-tests/get-quarantined-tests.js → test-management/get-test-management-tests.js} +5 -5
  42. package/packages/dd-trace/src/config.js +11 -1
  43. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -2
  44. package/packages/dd-trace/src/lambda/runtime/patch.js +5 -3
  45. package/packages/dd-trace/src/lambda/runtime/ritm.js +13 -18
  46. package/packages/dd-trace/src/llmobs/plugins/openai.js +27 -2
  47. package/packages/dd-trace/src/opentracing/span.js +3 -0
  48. package/packages/dd-trace/src/plugins/ci_plugin.js +38 -10
  49. package/packages/dd-trace/src/plugins/index.js +1 -0
  50. package/packages/dd-trace/src/plugins/util/git.js +7 -3
  51. package/packages/dd-trace/src/plugins/util/test.js +10 -0
  52. package/packages/dd-trace/src/plugins/util/web.js +5 -2
  53. package/packages/dd-trace/src/priority_sampler.js +116 -15
  54. package/packages/dd-trace/src/sampler.js +9 -0
  55. package/packages/dd-trace/src/standalone/product.js +6 -2
  56. package/packages/dd-trace/src/startup-log.js +2 -1
  57. package/packages/dd-trace/src/telemetry/metrics.js +0 -8
  58. package/packages/dd-trace/src/tracer.js +1 -1
  59. /package/packages/datadog-instrumentations/src/{utils/src → helpers}/extract-package-and-module-path.js +0 -0
@@ -103,16 +103,34 @@ function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
103
103
  function reportMetrics (metrics, raspRule) {
104
104
  const store = storage('legacy').getStore()
105
105
  const rootSpan = store?.req && web.root(store.req)
106
+
106
107
  if (!rootSpan) return
107
108
 
108
109
  if (metrics.rulesVersion) {
109
110
  rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
110
111
  }
112
+
111
113
  if (raspRule) {
112
114
  updateRaspRequestsMetricTags(metrics, store.req, raspRule)
113
115
  } else {
114
116
  updateWafRequestsMetricTags(metrics, store.req)
115
117
  }
118
+
119
+ reportTruncationMetrics(rootSpan, metrics)
120
+ }
121
+
122
+ function reportTruncationMetrics (rootSpan, metrics) {
123
+ if (metrics.maxTruncatedString) {
124
+ rootSpan.setTag('_dd.appsec.truncated.string_length', metrics.maxTruncatedString)
125
+ }
126
+
127
+ if (metrics.maxTruncatedContainerSize) {
128
+ rootSpan.setTag('_dd.appsec.truncated.container_size', metrics.maxTruncatedContainerSize)
129
+ }
130
+
131
+ if (metrics.maxTruncatedContainerDepth) {
132
+ rootSpan.setTag('_dd.appsec.truncated.container_depth', metrics.maxTruncatedContainerDepth)
133
+ }
116
134
  }
117
135
 
118
136
  function reportAttack (attackData) {
@@ -189,6 +207,7 @@ function finishRequest (req, res) {
189
207
  }
190
208
 
191
209
  const metrics = getRequestMetrics(req)
210
+
192
211
  if (metrics?.duration) {
193
212
  rootSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
194
213
  }
@@ -197,6 +216,14 @@ function finishRequest (req, res) {
197
216
  rootSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
198
217
  }
199
218
 
219
+ if (metrics?.wafErrorCode) {
220
+ rootSpan.setTag('_dd.appsec.waf.error', metrics.wafErrorCode)
221
+ }
222
+
223
+ if (metrics?.wafTimeouts) {
224
+ rootSpan.setTag('_dd.appsec.waf.timeouts', metrics.wafTimeouts)
225
+ }
226
+
200
227
  if (metrics?.raspDuration) {
201
228
  rootSpan.setTag('_dd.appsec.rasp.duration', metrics.raspDuration)
202
229
  }
@@ -205,6 +232,14 @@ function finishRequest (req, res) {
205
232
  rootSpan.setTag('_dd.appsec.rasp.duration_ext', metrics.raspDurationExt)
206
233
  }
207
234
 
235
+ if (metrics?.raspErrorCode) {
236
+ rootSpan.setTag('_dd.appsec.rasp.error', metrics.raspErrorCode)
237
+ }
238
+
239
+ if (metrics?.raspTimeouts) {
240
+ rootSpan.setTag('_dd.appsec.rasp.timeout', metrics.raspTimeouts)
241
+ }
242
+
208
243
  if (metrics?.raspEvalCount) {
209
244
  rootSpan.setTag('_dd.appsec.rasp.rule.eval', metrics.raspEvalCount)
210
245
  }
@@ -52,9 +52,7 @@ function blockRequest (tracer, req, res) {
52
52
  return false
53
53
  }
54
54
 
55
- block(req, res, rootSpan)
56
-
57
- return true
55
+ return block(req, res, rootSpan)
58
56
  }
59
57
 
60
58
  module.exports = {
@@ -30,7 +30,11 @@ function newStore () {
30
30
  durationExt: 0,
31
31
  raspDuration: 0,
32
32
  raspDurationExt: 0,
33
- raspEvalCount: 0
33
+ raspEvalCount: 0,
34
+ wafTimeouts: 0,
35
+ raspTimeouts: 0,
36
+ wafErrorCode: null,
37
+ raspErrorCode: null
34
38
  }
35
39
  }
36
40
  }
@@ -5,10 +5,25 @@ const { DD_TELEMETRY_REQUEST_METRICS } = require('./common')
5
5
 
6
6
  const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
7
7
 
8
- function addRaspRequestMetrics (store, { duration, durationExt }) {
8
+ function addRaspRequestMetrics (store, { duration, durationExt, wafTimeout, errorCode }) {
9
9
  store[DD_TELEMETRY_REQUEST_METRICS].raspDuration += duration || 0
10
10
  store[DD_TELEMETRY_REQUEST_METRICS].raspDurationExt += durationExt || 0
11
11
  store[DD_TELEMETRY_REQUEST_METRICS].raspEvalCount++
12
+
13
+ if (wafTimeout) {
14
+ store[DD_TELEMETRY_REQUEST_METRICS].raspTimeouts++
15
+ }
16
+
17
+ if (errorCode) {
18
+ if (store[DD_TELEMETRY_REQUEST_METRICS].raspErrorCode) {
19
+ store[DD_TELEMETRY_REQUEST_METRICS].raspErrorCode = Math.max(
20
+ errorCode,
21
+ store[DD_TELEMETRY_REQUEST_METRICS].raspErrorCode
22
+ )
23
+ } else {
24
+ store[DD_TELEMETRY_REQUEST_METRICS].raspErrorCode = errorCode
25
+ }
26
+ }
12
27
  }
13
28
 
14
29
  function trackRaspMetrics (metrics, raspRule) {
@@ -7,9 +7,24 @@ const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
7
7
 
8
8
  const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
9
9
 
10
- function addWafRequestMetrics (store, { duration, durationExt }) {
10
+ function addWafRequestMetrics (store, { duration, durationExt, wafTimeout, errorCode }) {
11
11
  store[DD_TELEMETRY_REQUEST_METRICS].duration += duration || 0
12
12
  store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
13
+
14
+ if (wafTimeout) {
15
+ store[DD_TELEMETRY_REQUEST_METRICS].wafTimeouts++
16
+ }
17
+
18
+ if (errorCode) {
19
+ if (store[DD_TELEMETRY_REQUEST_METRICS].wafErrorCode) {
20
+ store[DD_TELEMETRY_REQUEST_METRICS].wafErrorCode = Math.max(
21
+ errorCode,
22
+ store[DD_TELEMETRY_REQUEST_METRICS].wafErrorCode
23
+ )
24
+ } else {
25
+ store[DD_TELEMETRY_REQUEST_METRICS].wafErrorCode = errorCode
26
+ }
27
+ }
13
28
  }
14
29
 
15
30
  function trackWafDurations ({ duration, durationExt }, versionsTags) {
@@ -17,8 +17,8 @@ class WAFContextWrapper {
17
17
  this.wafTimeout = wafTimeout
18
18
  this.wafVersion = wafVersion
19
19
  this.rulesVersion = rulesVersion
20
- this.addressesToSkip = new Set()
21
20
  this.knownAddresses = knownAddresses
21
+ this.addressesToSkip = new Set()
22
22
  this.cachedUserIdActions = new Map()
23
23
  }
24
24
 
@@ -77,6 +77,20 @@ class WAFContextWrapper {
77
77
 
78
78
  if (!payloadHasData) return
79
79
 
80
+ const metrics = {
81
+ rulesVersion: this.rulesVersion,
82
+ wafVersion: this.wafVersion,
83
+ wafTimeout: false,
84
+ duration: 0,
85
+ durationExt: 0,
86
+ blockTriggered: false,
87
+ ruleTriggered: false,
88
+ errorCode: null,
89
+ maxTruncatedString: null,
90
+ maxTruncatedContainerSize: null,
91
+ maxTruncatedContainerDepth: null
92
+ }
93
+
80
94
  try {
81
95
  const start = process.hrtime.bigint()
82
96
 
@@ -84,6 +98,23 @@ class WAFContextWrapper {
84
98
 
85
99
  const end = process.hrtime.bigint()
86
100
 
101
+ metrics.durationExt = parseInt(end - start) / 1e3
102
+
103
+ if (typeof result.errorCode === 'number' && result.errorCode < 0) {
104
+ const error = new Error('WAF code error')
105
+ error.errorCode = result.errorCode
106
+
107
+ throw error
108
+ }
109
+
110
+ if (result.metrics) {
111
+ const { maxTruncatedString, maxTruncatedContainerSize, maxTruncatedContainerDepth } = result.metrics
112
+
113
+ if (maxTruncatedString) metrics.maxTruncatedString = maxTruncatedString
114
+ if (maxTruncatedContainerSize) metrics.maxTruncatedContainerSize = maxTruncatedContainerSize
115
+ if (maxTruncatedContainerDepth) metrics.maxTruncatedContainerDepth = maxTruncatedContainerDepth
116
+ }
117
+
87
118
  this.addressesToSkip = newAddressesToSkip
88
119
 
89
120
  const ruleTriggered = !!result.events?.length
@@ -96,15 +127,10 @@ class WAFContextWrapper {
96
127
  this.setUserIdCache(userId, result)
97
128
  }
98
129
 
99
- Reporter.reportMetrics({
100
- duration: result.totalRuntime / 1e3,
101
- durationExt: parseInt(end - start) / 1e3,
102
- rulesVersion: this.rulesVersion,
103
- ruleTriggered,
104
- blockTriggered,
105
- wafVersion: this.wafVersion,
106
- wafTimeout: result.timeout
107
- }, raspRule)
130
+ metrics.duration = result.totalRuntime / 1e3
131
+ metrics.blockTriggered = blockTriggered
132
+ metrics.ruleTriggered = ruleTriggered
133
+ metrics.wafTimeout = result.timeout
108
134
 
109
135
  if (ruleTriggered) {
110
136
  Reporter.reportAttack(JSON.stringify(result.events))
@@ -112,13 +138,17 @@ class WAFContextWrapper {
112
138
 
113
139
  Reporter.reportDerivatives(result.derivatives)
114
140
 
141
+ return result.actions
142
+ } catch (err) {
143
+ log.error('[ASM] Error while running the AppSec WAF', err)
144
+
145
+ metrics.errorCode = err.errorCode ?? -127
146
+ } finally {
115
147
  if (wafRunFinished.hasSubscribers) {
116
148
  wafRunFinished.publish({ payload })
117
149
  }
118
150
 
119
- return result.actions
120
- } catch (err) {
121
- log.error('[ASM] Error while running the AppSec WAF', err)
151
+ Reporter.reportMetrics(metrics, raspRule)
122
152
  }
123
153
  }
124
154
 
@@ -73,8 +73,8 @@ class Writer extends BaseWriter {
73
73
  })
74
74
  }
75
75
 
76
- setMetadataTags (tags) {
77
- this._encoder.setMetadataTags(tags)
76
+ addMetadataTags (tags) {
77
+ this._encoder.addMetadataTags(tags)
78
78
  }
79
79
  }
80
80
 
@@ -6,7 +6,8 @@ const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata'
6
6
  const { getLibraryConfiguration: getLibraryConfigurationRequest } = require('../requests/get-library-configuration')
7
7
  const { getSkippableSuites: getSkippableSuitesRequest } = require('../intelligent-test-runner/get-skippable-suites')
8
8
  const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detection/get-known-tests')
9
- const { getQuarantinedTests: getQuarantinedTestsRequest } = require('../quarantined-tests/get-quarantined-tests')
9
+ const { getTestManagementTests: getTestManagementTestsRequest } =
10
+ require('../test-management/get-test-management-tests')
10
11
  const log = require('../../log')
11
12
  const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
12
13
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
@@ -93,11 +94,11 @@ class CiVisibilityExporter extends AgentInfoExporter {
93
94
  )
94
95
  }
95
96
 
96
- shouldRequestQuarantinedTests () {
97
+ shouldRequestTestManagementTests () {
97
98
  return !!(
98
99
  this._canUseCiVisProtocol &&
99
100
  this._config.isTestManagementEnabled &&
100
- this._libraryConfig?.isQuarantinedTestsEnabled
101
+ this._libraryConfig?.isTestManagementEnabled
101
102
  )
102
103
  }
103
104
 
@@ -147,11 +148,11 @@ class CiVisibilityExporter extends AgentInfoExporter {
147
148
  getKnownTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
148
149
  }
149
150
 
150
- getQuarantinedTests (testConfiguration, callback) {
151
- if (!this.shouldRequestQuarantinedTests()) {
151
+ getTestManagementTests (testConfiguration, callback) {
152
+ if (!this.shouldRequestTestManagementTests()) {
152
153
  return callback(null)
153
154
  }
154
- getQuarantinedTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
155
+ getTestManagementTestsRequest(this.getRequestConfiguration(testConfiguration), callback)
155
156
  }
156
157
 
157
158
  /**
@@ -214,7 +215,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
214
215
  isFlakyTestRetriesEnabled,
215
216
  isDiEnabled,
216
217
  isKnownTestsEnabled,
217
- isQuarantinedTestsEnabled
218
+ isTestManagementEnabled
218
219
  } = remoteConfiguration
219
220
  return {
220
221
  isCodeCoverageEnabled,
@@ -228,7 +229,7 @@ class CiVisibilityExporter extends AgentInfoExporter {
228
229
  flakyTestRetriesCount: this._config.flakyTestRetriesCount,
229
230
  isDiEnabled: isDiEnabled && this._config.isTestDynamicInstrumentationEnabled,
230
231
  isKnownTestsEnabled,
231
- isQuarantinedTestsEnabled: isQuarantinedTestsEnabled && this._config.isTestManagementEnabled
232
+ isTestManagementEnabled: isTestManagementEnabled && this._config.isTestManagementEnabled
232
233
  }
233
234
  }
234
235
 
@@ -374,14 +375,14 @@ class CiVisibilityExporter extends AgentInfoExporter {
374
375
  return this._url
375
376
  }
376
377
 
377
- // By the time setMetadataTags is called, the agent info request might not have finished
378
- setMetadataTags (tags) {
379
- if (this._writer?.setMetadataTags) {
380
- this._writer.setMetadataTags(tags)
378
+ // By the time addMetadataTags is called, the agent info request might not have finished
379
+ addMetadataTags (tags) {
380
+ if (this._writer?.addMetadataTags) {
381
+ this._writer.addMetadataTags(tags)
381
382
  } else {
382
383
  this._canUseCiVisProtocolPromise.then(() => {
383
- if (this._writer?.setMetadataTags) {
384
- this._writer.setMetadataTags(tags)
384
+ if (this._writer?.addMetadataTags) {
385
+ this._writer.addMetadataTags(tags)
385
386
  }
386
387
  })
387
388
  }
@@ -113,8 +113,7 @@ function getLibraryConfiguration ({
113
113
  isFlakyTestRetriesEnabled,
114
114
  isDiEnabled: isDiEnabled && isFlakyTestRetriesEnabled,
115
115
  isKnownTestsEnabled,
116
- // TODO: should it be test management?
117
- isQuarantinedTestsEnabled: (testManagementConfig?.enabled ?? false)
116
+ isTestManagementEnabled: (testManagementConfig?.enabled ?? false)
118
117
  }
119
118
 
120
119
  log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
@@ -13,7 +13,8 @@ const formattedTags = {
13
13
  isUnsupportedCIProvider: 'is_unsupported_ci',
14
14
  isNew: 'is_new',
15
15
  isRum: 'is_rum',
16
- browserDriver: 'browser_driver'
16
+ browserDriver: 'browser_driver',
17
+ autoInjected: 'auto_injected'
17
18
  }
18
19
 
19
20
  // Transform tags dictionary to array of strings.
@@ -1,7 +1,7 @@
1
1
  const request = require('../../exporters/common/request')
2
2
  const id = require('../../id')
3
3
 
4
- function getQuarantinedTests ({
4
+ function getTestManagementTests ({
5
5
  url,
6
6
  isEvpProxy,
7
7
  evpProxyPrefix,
@@ -28,7 +28,7 @@ function getQuarantinedTests ({
28
28
  } else {
29
29
  const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
30
30
  if (!apiKey) {
31
- return done(new Error('Quarantined tests were not fetched because Datadog API key is not defined.'))
31
+ return done(new Error('Test management tests were not fetched because Datadog API key is not defined.'))
32
32
  }
33
33
 
34
34
  options.headers['dd-api-key'] = apiKey
@@ -49,9 +49,9 @@ function getQuarantinedTests ({
49
49
  done(err)
50
50
  } else {
51
51
  try {
52
- const { data: { attributes: { modules: quarantinedTests } } } = JSON.parse(res)
52
+ const { data: { attributes: { modules: testManagementTests } } } = JSON.parse(res)
53
53
 
54
- done(null, quarantinedTests)
54
+ done(null, testManagementTests)
55
55
  } catch (err) {
56
56
  done(err)
57
57
  }
@@ -59,4 +59,4 @@ function getQuarantinedTests ({
59
59
  })
60
60
  }
61
61
 
62
- module.exports = { getQuarantinedTests }
62
+ module.exports = { getTestManagementTests }
@@ -591,8 +591,10 @@ class Config {
591
591
  this._setValue(defaults, 'url', undefined)
592
592
  this._setValue(defaults, 'version', pkg.version)
593
593
  this._setValue(defaults, 'instrumentation_config_id', undefined)
594
+ this._setValue(defaults, 'aws.dynamoDb.tablePrimaryKeys', undefined)
595
+ this._setValue(defaults, 'vertexai.spanCharLimit', 128)
596
+ this._setValue(defaults, 'vertexai.spanPromptCompletionSampleRate', 1.0)
594
597
  this._setValue(defaults, 'trace.aws.addSpanPointers', true)
595
- this._setValue(defaults, 'trace.dynamoDb.tablePrimaryKeys', undefined)
596
598
  }
597
599
 
598
600
  _applyLocalStableConfig () {
@@ -760,6 +762,8 @@ class Config {
760
762
  DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
761
763
  DD_TRACING_ENABLED,
762
764
  DD_VERSION,
765
+ DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE,
766
+ DD_VERTEXAI_SPAN_CHAR_LIMIT,
763
767
  DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED,
764
768
  OTEL_METRICS_EXPORTER,
765
769
  OTEL_PROPAGATORS,
@@ -973,6 +977,12 @@ class Config {
973
977
  this._setBoolean(env, 'trace.aws.addSpanPointers', DD_TRACE_AWS_ADD_SPAN_POINTERS)
974
978
  this._setString(env, 'trace.dynamoDb.tablePrimaryKeys', DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS)
975
979
  this._setArray(env, 'graphqlErrorExtensions', DD_TRACE_GRAPHQL_ERROR_EXTENSIONS)
980
+ this._setValue(
981
+ env,
982
+ 'vertexai.spanPromptCompletionSampleRate',
983
+ maybeFloat(DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE)
984
+ )
985
+ this._setValue(env, 'vertexai.spanCharLimit', maybeInt(DD_VERTEXAI_SPAN_CHAR_LIMIT))
976
986
  }
977
987
 
978
988
  _applyOptions (options) {
@@ -48,8 +48,15 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
48
48
  this.reset()
49
49
  }
50
50
 
51
- setMetadataTags (tags) {
52
- this.metadataTags = tags
51
+ addMetadataTags (tags) {
52
+ ALLOWED_CONTENT_TYPES.forEach(type => {
53
+ if (tags[type]) {
54
+ this.metadataTags[type] = {
55
+ ...this.metadataTags[type],
56
+ ...tags[type]
57
+ }
58
+ }
59
+ })
53
60
  }
54
61
 
55
62
  _encodeTestSuite (bytes, content) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const path = require('path')
4
4
 
5
- const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePath } = require('./ritm')
5
+ const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePaths } = require('./ritm')
6
6
  const { datadog } = require('../handler')
7
7
  const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
8
8
  const shimmer = require('../../../../datadog-shimmer')
@@ -65,9 +65,11 @@ if (originalLambdaHandler !== undefined) {
65
65
  const [_module, handlerPath] = _extractModuleNameAndHandlerPath(moduleAndHandler)
66
66
 
67
67
  const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
68
- const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
68
+ const lambdaFilePaths = _getLambdaFilePaths(lambdaStylePath)
69
69
 
70
- addHook({ name: lambdaFilePath }, patchLambdaModule(handlerPath))
70
+ for (const lambdaFilePath of lambdaFilePaths) {
71
+ addHook({ name: lambdaFilePath }, patchLambdaModule(handlerPath))
72
+ }
71
73
  } else {
72
74
  // Instrumentation is done manually.
73
75
  addHook({ name: 'datadog-lambda-js' }, patchDatadogLambdaModule)
@@ -7,7 +7,6 @@
7
7
  */
8
8
  'use strict'
9
9
 
10
- const fs = require('fs')
11
10
  const path = require('path')
12
11
 
13
12
  const log = require('../../log')
@@ -60,23 +59,18 @@ function _extractModuleNameAndHandlerPath (handler) {
60
59
  }
61
60
 
62
61
  /**
63
- * Returns the correct path of the file to be patched
64
- * when required.
62
+ * Returns all possible paths of the files to be patched when required.
65
63
  *
66
64
  * @param {*} lambdaStylePath the path comprised of the `LAMBDA_TASK_ROOT`,
67
65
  * the root of the module of the Lambda handler, and the module name.
68
- * @returns the lambdaStylePath with the appropiate extension for the hook.
66
+ * @returns the lambdaStylePath with appropiate extensions for the hook.
69
67
  */
70
- function _getLambdaFilePath (lambdaStylePath) {
71
- let lambdaFilePath = lambdaStylePath
72
- if (fs.existsSync(lambdaStylePath + '.js')) {
73
- lambdaFilePath += '.js'
74
- } else if (fs.existsSync(lambdaStylePath + '.mjs')) {
75
- lambdaFilePath += '.mjs'
76
- } else if (fs.existsSync(lambdaStylePath + '.cjs')) {
77
- lambdaFilePath += '.cjs'
78
- }
79
- return lambdaFilePath
68
+ function _getLambdaFilePaths (lambdaStylePath) {
69
+ return [
70
+ `${lambdaStylePath}.js`,
71
+ `${lambdaStylePath}.mjs`,
72
+ `${lambdaStylePath}.cjs`
73
+ ]
80
74
  }
81
75
 
82
76
  /**
@@ -92,12 +86,13 @@ const registerLambdaHook = () => {
92
86
  const [_module] = _extractModuleNameAndHandlerPath(moduleAndHandler)
93
87
 
94
88
  const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
95
- const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
89
+ const lambdaFilePaths = _getLambdaFilePaths(lambdaStylePath)
96
90
 
97
- Hook([lambdaFilePath], (moduleExports) => {
91
+ // TODO: Redo this like any other instrumentation.
92
+ Hook(lambdaFilePaths, (moduleExports, name) => {
98
93
  require('./patch')
99
94
 
100
- for (const { hook } of instrumentations[lambdaFilePath]) {
95
+ for (const { hook } of instrumentations[name]) {
101
96
  try {
102
97
  moduleExports = hook(moduleExports)
103
98
  } catch (e) {
@@ -133,6 +128,6 @@ const registerLambdaHook = () => {
133
128
  module.exports = {
134
129
  _extractModuleRootAndHandler,
135
130
  _extractModuleNameAndHandlerPath,
136
- _getLambdaFilePath,
131
+ _getLambdaFilePaths,
137
132
  registerLambdaHook
138
133
  }
@@ -2,6 +2,13 @@
2
2
 
3
3
  const LLMObsPlugin = require('./base')
4
4
 
5
+ function isIterable (obj) {
6
+ if (obj == null) {
7
+ return false
8
+ }
9
+ return typeof obj[Symbol.iterator] === 'function'
10
+ }
11
+
5
12
  class OpenAiLLMObsPlugin extends LLMObsPlugin {
6
13
  static get id () { return 'openai' }
7
14
  static get prefix () {
@@ -16,10 +23,13 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
16
23
  const inputs = ctx.args[0] // completion, chat completion, and embeddings take one argument
17
24
  const operation = getOperation(methodName)
18
25
  const kind = operation === 'embedding' ? 'embedding' : 'llm'
19
- const name = `openai.${methodName}`
26
+
27
+ const { modelProvider, client } = this._getModelProviderAndClient(ctx.basePath)
28
+
29
+ const name = `${client}.${methodName}`
20
30
 
21
31
  return {
22
- modelProvider: 'openai',
32
+ modelProvider,
23
33
  modelName: inputs.model,
24
34
  kind,
25
35
  name
@@ -52,6 +62,16 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
52
62
  }
53
63
  }
54
64
 
65
+ _getModelProviderAndClient (baseUrl = '') {
66
+ if (baseUrl.includes('azure')) {
67
+ return { modelProvider: 'azure_openai', client: 'AzureOpenAI' }
68
+ } else if (baseUrl.includes('deepseek')) {
69
+ return { modelProvider: 'deepseek', client: 'DeepSeek' }
70
+ } else {
71
+ return { modelProvider: 'openai', client: 'OpenAI' }
72
+ }
73
+ }
74
+
55
75
  _extractMetrics (response) {
56
76
  const metrics = {}
57
77
  const tokenUsage = response.usage
@@ -122,6 +142,11 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
122
142
 
123
143
  const outputMessages = []
124
144
  const { choices } = response
145
+ if (!isIterable(choices)) {
146
+ this._tagger.tagLLMIO(span, messages, [{ content: '' }])
147
+ return
148
+ }
149
+
125
150
  for (const choice of choices) {
126
151
  const message = choice.message || choice.delta
127
152
  const content = message.content || ''
@@ -143,6 +143,9 @@ class DatadogSpan {
143
143
  return `Span${json}`
144
144
  }
145
145
 
146
+ /**
147
+ * @returns {DatadogSpanContext}
148
+ */
146
149
  context () {
147
150
  return this._spanContext
148
151
  }