dd-trace 4.40.0 → 4.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 (74) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ext/exporters.d.ts +1 -1
  3. package/index.d.ts +54 -1
  4. package/init.js +40 -1
  5. package/initialize.mjs +8 -5
  6. package/package.json +24 -20
  7. package/packages/datadog-core/src/storage/index.js +1 -10
  8. package/packages/datadog-esbuild/index.js +5 -1
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
  10. package/packages/datadog-instrumentations/src/cucumber.js +76 -34
  11. package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  13. package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
  14. package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
  15. package/packages/datadog-instrumentations/src/http/server.js +98 -0
  16. package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
  17. package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
  18. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
  19. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  20. package/packages/datadog-instrumentations/src/undici.js +18 -0
  21. package/packages/datadog-instrumentations/src/vitest.js +303 -0
  22. package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
  23. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  24. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
  25. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
  26. package/packages/datadog-plugin-child_process/src/index.js +1 -1
  27. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  28. package/packages/datadog-plugin-mocha/src/index.js +25 -4
  29. package/packages/datadog-plugin-openai/src/index.js +52 -30
  30. package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
  31. package/packages/datadog-plugin-undici/src/index.js +12 -0
  32. package/packages/datadog-plugin-vitest/src/index.js +156 -0
  33. package/packages/dd-trace/src/appsec/blocking.js +4 -0
  34. package/packages/dd-trace/src/appsec/channels.js +1 -0
  35. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
  36. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
  37. package/packages/dd-trace/src/appsec/index.js +45 -11
  38. package/packages/dd-trace/src/appsec/rasp.js +32 -5
  39. package/packages/dd-trace/src/appsec/recommended.json +208 -3
  40. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
  41. package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
  42. package/packages/dd-trace/src/appsec/reporter.js +64 -20
  43. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
  44. package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
  45. package/packages/dd-trace/src/appsec/standalone.js +130 -0
  46. package/packages/dd-trace/src/appsec/telemetry.js +33 -1
  47. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  48. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
  49. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  50. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  51. package/packages/dd-trace/src/config.js +110 -40
  52. package/packages/dd-trace/src/constants.js +3 -1
  53. package/packages/dd-trace/src/datastreams/processor.js +2 -1
  54. package/packages/dd-trace/src/exporters/agent/index.js +2 -2
  55. package/packages/dd-trace/src/format.js +22 -2
  56. package/packages/dd-trace/src/opentelemetry/span.js +33 -7
  57. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
  58. package/packages/dd-trace/src/opentracing/span.js +42 -1
  59. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  60. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
  61. package/packages/dd-trace/src/plugins/index.js +3 -0
  62. package/packages/dd-trace/src/plugins/util/test.js +5 -1
  63. package/packages/dd-trace/src/priority_sampler.js +2 -5
  64. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  65. package/packages/dd-trace/src/proxy.js +3 -1
  66. package/packages/dd-trace/src/rate_limiter.js +2 -2
  67. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  69. package/packages/dd-trace/src/span_stats.js +4 -3
  70. package/packages/dd-trace/src/tagger.js +10 -1
  71. package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
  72. package/packages/dd-trace/src/tracer.js +2 -2
  73. package/packages/dd-trace/src/util.js +6 -1
  74. package/packages/datadog-core/src/storage/async_hooks.js +0 -49
@@ -18,6 +18,7 @@ module.exports = {
18
18
  get '@opensearch-project/opensearch' () { return require('../../../datadog-plugin-opensearch/src') },
19
19
  get '@redis/client' () { return require('../../../datadog-plugin-redis/src') },
20
20
  get '@smithy/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
21
+ get '@vitest/runner' () { return require('../../../datadog-plugin-vitest/src') },
21
22
  get aerospike () { return require('../../../datadog-plugin-aerospike/src') },
22
23
  get amqp10 () { return require('../../../datadog-plugin-amqp10/src') },
23
24
  get amqplib () { return require('../../../datadog-plugin-amqplib/src') },
@@ -54,6 +55,7 @@ module.exports = {
54
55
  get 'microgateway-core' () { return require('../../../datadog-plugin-microgateway-core/src') },
55
56
  get mocha () { return require('../../../datadog-plugin-mocha/src') },
56
57
  get 'mocha-each' () { return require('../../../datadog-plugin-mocha/src') },
58
+ get vitest () { return require('../../../datadog-plugin-vitest/src') },
57
59
  get workerpool () { return require('../../../datadog-plugin-mocha/src') },
58
60
  get moleculer () { return require('../../../datadog-plugin-moleculer/src') },
59
61
  get mongodb () { return require('../../../datadog-plugin-mongodb-core/src') },
@@ -81,5 +83,6 @@ module.exports = {
81
83
  get 'selenium-webdriver' () { return require('../../../datadog-plugin-selenium/src') },
82
84
  get sharedb () { return require('../../../datadog-plugin-sharedb/src') },
83
85
  get tedious () { return require('../../../datadog-plugin-tedious/src') },
86
+ get undici () { return require('../../../datadog-plugin-undici/src') },
84
87
  get winston () { return require('../../../datadog-plugin-winston/src') }
85
88
  }
@@ -95,6 +95,9 @@ const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
95
95
  const EFD_STRING = "Retried by Datadog's Early Flake Detection"
96
96
  const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
97
97
 
98
+ // Flaky test retries
99
+ const NUM_FAILED_TEST_RETRIES = 5
100
+
98
101
  module.exports = {
99
102
  TEST_CODE_OWNERS,
100
103
  TEST_FRAMEWORK,
@@ -167,7 +170,8 @@ module.exports = {
167
170
  TEST_BROWSER_DRIVER,
168
171
  TEST_BROWSER_DRIVER_VERSION,
169
172
  TEST_BROWSER_NAME,
170
- TEST_BROWSER_VERSION
173
+ TEST_BROWSER_VERSION,
174
+ NUM_FAILED_TEST_RETRIES
171
175
  }
172
176
 
173
177
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -4,6 +4,7 @@ const RateLimiter = require('./rate_limiter')
4
4
  const Sampler = require('./sampler')
5
5
  const { setSamplingRules } = require('./startup-log')
6
6
  const SamplingRule = require('./sampling_rule')
7
+ const { hasOwn } = require('./util')
7
8
 
8
9
  const {
9
10
  SAMPLING_MECHANISM_DEFAULT,
@@ -66,7 +67,7 @@ class PrioritySampler {
66
67
  if (context._sampling.priority !== undefined) return
67
68
  if (!root) return // noop span
68
69
 
69
- const tag = this._getPriorityFromTags(context._tags)
70
+ const tag = this._getPriorityFromTags(context._tags, context)
70
71
 
71
72
  if (this.validate(tag)) {
72
73
  context._sampling.priority = tag
@@ -202,8 +203,4 @@ class PrioritySampler {
202
203
  }
203
204
  }
204
205
 
205
- function hasOwn (object, prop) {
206
- return Object.prototype.hasOwnProperty.call(object, prop)
207
- }
208
-
209
206
  module.exports = PrioritySampler
@@ -69,7 +69,7 @@ class Profiler extends EventEmitter {
69
69
  setLogger(config.logger)
70
70
 
71
71
  mapper = await maybeSourceMap(config.sourceMap, SourceMapper, config.debugSourceMaps)
72
- if (config.SourceMap && config.debugSourceMaps) {
72
+ if (config.sourceMap && config.debugSourceMaps) {
73
73
  this._logger.debug(() => {
74
74
  return mapper.infoMap.size === 0
75
75
  ? 'Found no source maps'
@@ -15,6 +15,7 @@ const NoopDogStatsDClient = require('./noop/dogstatsd')
15
15
  const spanleak = require('./spanleak')
16
16
  const { SSIHeuristics } = require('./profiling/ssi-heuristics')
17
17
  const telemetryLog = require('dc-polyfill').channel('datadog:telemetry:log')
18
+ const appsecStandalone = require('./appsec/standalone')
18
19
 
19
20
  class LazyModule {
20
21
  constructor (provider) {
@@ -178,7 +179,8 @@ class Tracer extends NoopProxy {
178
179
  this._modules.appsec.enable(config)
179
180
  }
180
181
  if (!this._tracingInitialized) {
181
- this._tracer = new DatadogTracer(config)
182
+ const prioritySampler = appsecStandalone.configure(config)
183
+ this._tracer = new DatadogTracer(config, prioritySampler)
182
184
  this.appsec = new AppsecSdk(this._tracer, config)
183
185
  this._tracingInitialized = true
184
186
  }
@@ -3,9 +3,9 @@
3
3
  const limiter = require('limiter')
4
4
 
5
5
  class RateLimiter {
6
- constructor (rateLimit) {
6
+ constructor (rateLimit, interval = 'second') {
7
7
  this._rateLimit = parseInt(rateLimit)
8
- this._limiter = new limiter.RateLimiter(this._rateLimit, 'second')
8
+ this._limiter = new limiter.RateLimiter(this._rateLimit, interval)
9
9
  this._tokensRequested = 0
10
10
  this._prevIntervalTokens = 0
11
11
  this._prevTokensRequested = 0
@@ -30,6 +30,10 @@ const web = {
30
30
  lambda: {
31
31
  opName: () => 'aws.request',
32
32
  serviceName: awsServiceV0
33
+ },
34
+ undici: {
35
+ opName: () => 'undici.request',
36
+ serviceName: httpPluginClientService
33
37
  }
34
38
  },
35
39
  server: {
@@ -29,6 +29,10 @@ const web = {
29
29
  lambda: {
30
30
  opName: () => 'aws.lambda.invoke',
31
31
  serviceName: identityService
32
+ },
33
+ undici: {
34
+ opName: () => 'undici.request',
35
+ serviceName: httpPluginClientService
32
36
  }
33
37
  },
34
38
  server: {
@@ -126,7 +126,8 @@ class SpanStatsProcessor {
126
126
  port,
127
127
  url,
128
128
  env,
129
- tags
129
+ tags,
130
+ appsec
130
131
  } = {}) {
131
132
  this.exporter = new SpanStatsExporter({
132
133
  hostname,
@@ -138,12 +139,12 @@ class SpanStatsProcessor {
138
139
  this.bucketSizeNs = interval * 1e9
139
140
  this.buckets = new TimeBuckets()
140
141
  this.hostname = os.hostname()
141
- this.enabled = enabled
142
+ this.enabled = enabled && !appsec?.standalone?.enabled
142
143
  this.env = env
143
144
  this.tags = tags || {}
144
145
  this.sequence = 0
145
146
 
146
- if (enabled) {
147
+ if (this.enabled) {
147
148
  this.timer = setInterval(this.onInterval.bind(this), interval * 1e3)
148
149
  this.timer.unref()
149
150
  }
@@ -1,6 +1,10 @@
1
1
  'use strict'
2
2
 
3
+ const constants = require('./constants')
3
4
  const log = require('./log')
5
+ const ERROR_MESSAGE = constants.ERROR_MESSAGE
6
+ const ERROR_STACK = constants.ERROR_STACK
7
+ const ERROR_TYPE = constants.ERROR_TYPE
4
8
 
5
9
  const otelTagMap = {
6
10
  'deployment.environment': 'env',
@@ -14,7 +18,6 @@ function add (carrier, keyValuePairs, parseOtelTags = false) {
14
18
  if (Array.isArray(keyValuePairs)) {
15
19
  return keyValuePairs.forEach(tags => add(carrier, tags))
16
20
  }
17
-
18
21
  try {
19
22
  if (typeof keyValuePairs === 'string') {
20
23
  const segments = keyValuePairs.split(',')
@@ -32,6 +35,12 @@ function add (carrier, keyValuePairs, parseOtelTags = false) {
32
35
  carrier[key.trim()] = value.trim()
33
36
  }
34
37
  } else {
38
+ // HACK: to ensure otel.recordException does not influence trace.error
39
+ if (ERROR_MESSAGE in keyValuePairs || ERROR_STACK in keyValuePairs || ERROR_TYPE in keyValuePairs) {
40
+ if (!('doNotSetTraceError' in keyValuePairs)) {
41
+ carrier.setTraceError = true
42
+ }
43
+ }
35
44
  Object.assign(carrier, keyValuePairs)
36
45
  }
37
46
  } catch (e) {
@@ -0,0 +1,75 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const { spawn } = require('child_process')
5
+ const tracerVersion = require('../../../../package.json').version
6
+ const log = require('../log')
7
+
8
+ module.exports = sendTelemetry
9
+
10
+ if (!process.env.DD_INJECTION_ENABLED) {
11
+ module.exports = () => {}
12
+ }
13
+
14
+ if (!process.env.DD_TELEMETRY_FORWARDER_PATH) {
15
+ module.exports = () => {}
16
+ }
17
+
18
+ if (!fs.existsSync(process.env.DD_TELEMETRY_FORWARDER_PATH)) {
19
+ module.exports = () => {}
20
+ }
21
+
22
+ const metadata = {
23
+ language_name: 'nodejs',
24
+ language_version: process.versions.node,
25
+ runtime_name: 'nodejs',
26
+ runtime_version: process.versions.node,
27
+ tracer_version: tracerVersion,
28
+ pid: process.pid
29
+ }
30
+
31
+ const seen = []
32
+ function hasSeen (point) {
33
+ if (point.name === 'abort') {
34
+ // This one can only be sent once, regardless of tags
35
+ return seen.includes('abort')
36
+ }
37
+ if (point.name === 'abort.integration') {
38
+ // For now, this is the only other one we want to dedupe
39
+ const compiledPoint = point.name + point.tags.join('')
40
+ return seen.includes(compiledPoint)
41
+ }
42
+ return false
43
+ }
44
+
45
+ function sendTelemetry (name, tags = []) {
46
+ let points = name
47
+ if (typeof name === 'string') {
48
+ points = [{ name, tags }]
49
+ }
50
+ if (['1', 'true', 'True'].includes(process.env.DD_INJECT_FORCE)) {
51
+ points = points.filter(p => ['error', 'complete'].includes(p.name))
52
+ }
53
+ points = points.filter(p => !hasSeen(p))
54
+ points.forEach(p => {
55
+ p.name = `library_entrypoint.${p.name}`
56
+ })
57
+ if (points.length === 0) {
58
+ return
59
+ }
60
+ const proc = spawn(process.env.DD_TELEMETRY_FORWARDER_PATH, ['library_entrypoint'], {
61
+ stdio: 'pipe'
62
+ })
63
+ proc.on('error', () => {
64
+ log.error('Failed to spawn telemetry forwarder')
65
+ })
66
+ proc.on('exit', (code) => {
67
+ if (code !== 0) {
68
+ log.error(`Telemetry forwarder exited with code ${code}`)
69
+ }
70
+ })
71
+ proc.stdin.on('error', () => {
72
+ log.error('Failed to write telemetry data to telemetry forwarder')
73
+ })
74
+ proc.stdin.end(JSON.stringify({ metadata, points }))
75
+ }
@@ -20,8 +20,8 @@ const SERVICE_NAME = tags.SERVICE_NAME
20
20
  const MEASURED = tags.MEASURED
21
21
 
22
22
  class DatadogTracer extends Tracer {
23
- constructor (config) {
24
- super(config)
23
+ constructor (config, prioritySampler) {
24
+ super(config, prioritySampler)
25
25
  this._dataStreamsProcessor = new DataStreamsProcessor(config)
26
26
  this._scope = new Scope()
27
27
  setStartupLogConfig(config)
@@ -69,10 +69,15 @@ function calculateDDBasePath (dirname) {
69
69
  return dirSteps.slice(0, packagesIndex + 1).join(path.sep) + path.sep
70
70
  }
71
71
 
72
+ function hasOwn (object, prop) {
73
+ return Object.prototype.hasOwnProperty.call(object, prop)
74
+ }
75
+
72
76
  module.exports = {
73
77
  isTrue,
74
78
  isFalse,
75
79
  isError,
76
80
  globMatch,
77
- calculateDDBasePath
81
+ calculateDDBasePath,
82
+ hasOwn
78
83
  }
@@ -1,49 +0,0 @@
1
- 'use strict'
2
-
3
- const { executionAsyncId } = require('async_hooks')
4
- const AsyncResourceStorage = require('./async_resource')
5
-
6
- class AsyncHooksStorage extends AsyncResourceStorage {
7
- constructor () {
8
- super()
9
-
10
- this._resources = new Map()
11
- }
12
-
13
- disable () {
14
- super.disable()
15
-
16
- this._resources.clear()
17
- }
18
-
19
- _createHook () {
20
- return {
21
- ...super._createHook(),
22
- destroy: this._destroy.bind(this)
23
- }
24
- }
25
-
26
- _init (asyncId, type, triggerAsyncId, resource) {
27
- super._init.apply(this, arguments)
28
-
29
- this._resources.set(asyncId, resource)
30
- }
31
-
32
- _destroy (asyncId) {
33
- this._resources.delete(asyncId)
34
- }
35
-
36
- _executionAsyncResource () {
37
- const asyncId = executionAsyncId()
38
-
39
- let resource = this._resources.get(asyncId)
40
-
41
- if (!resource) {
42
- this._resources.set(asyncId, resource = {})
43
- }
44
-
45
- return resource
46
- }
47
- }
48
-
49
- module.exports = AsyncHooksStorage