dd-trace 5.54.0 → 5.56.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 (191) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/ci/cypress/plugin.js +8 -0
  3. package/ci/cypress/polyfills.js +23 -0
  4. package/ci/init.js +8 -7
  5. package/initialize.mjs +2 -2
  6. package/package.json +10 -9
  7. package/packages/datadog-code-origin/index.js +22 -4
  8. package/packages/datadog-core/src/utils/src/kebabcase.js +3 -3
  9. package/packages/datadog-core/src/utils/src/set.js +8 -10
  10. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -6
  11. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +2 -3
  12. package/packages/datadog-instrumentations/src/cookie-parser.js +1 -1
  13. package/packages/datadog-instrumentations/src/couchbase.js +3 -6
  14. package/packages/datadog-instrumentations/src/cucumber.js +21 -28
  15. package/packages/datadog-instrumentations/src/dns.js +4 -4
  16. package/packages/datadog-instrumentations/src/elasticsearch.js +9 -10
  17. package/packages/datadog-instrumentations/src/fastify.js +7 -9
  18. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +14 -16
  19. package/packages/datadog-instrumentations/src/hapi.js +10 -11
  20. package/packages/datadog-instrumentations/src/helpers/fetch.js +4 -5
  21. package/packages/datadog-instrumentations/src/helpers/hook.js +1 -2
  22. package/packages/datadog-instrumentations/src/helpers/register.js +6 -5
  23. package/packages/datadog-instrumentations/src/jest.js +421 -376
  24. package/packages/datadog-instrumentations/src/koa.js +2 -3
  25. package/packages/datadog-instrumentations/src/mariadb.js +11 -4
  26. package/packages/datadog-instrumentations/src/mocha/main.js +79 -75
  27. package/packages/datadog-instrumentations/src/mocha.js +3 -1
  28. package/packages/datadog-instrumentations/src/mysql.js +11 -2
  29. package/packages/datadog-instrumentations/src/nyc.js +2 -1
  30. package/packages/datadog-instrumentations/src/openai.js +2 -2
  31. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -3
  32. package/packages/datadog-instrumentations/src/pg.js +2 -3
  33. package/packages/datadog-instrumentations/src/playwright.js +19 -22
  34. package/packages/datadog-instrumentations/src/protobufjs.js +3 -4
  35. package/packages/datadog-instrumentations/src/redis.js +1 -1
  36. package/packages/datadog-instrumentations/src/restify.js +9 -13
  37. package/packages/datadog-instrumentations/src/router.js +12 -11
  38. package/packages/datadog-instrumentations/src/tedious.js +1 -2
  39. package/packages/datadog-instrumentations/src/vitest.js +15 -29
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +12 -12
  41. package/packages/datadog-plugin-aws-sdk/src/base.js +12 -8
  42. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +3 -5
  43. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +12 -20
  44. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -5
  45. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +3 -5
  46. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +3 -5
  47. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +3 -5
  48. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -2
  49. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -10
  50. package/packages/datadog-plugin-azure-functions/src/index.js +5 -4
  51. package/packages/datadog-plugin-cucumber/src/index.js +3 -2
  52. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -1
  53. package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -1
  54. package/packages/datadog-plugin-elasticsearch/src/index.js +1 -1
  55. package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +1 -1
  56. package/packages/datadog-plugin-graphql/src/index.js +3 -2
  57. package/packages/datadog-plugin-graphql/src/resolve.js +17 -10
  58. package/packages/datadog-plugin-http/src/client.js +5 -6
  59. package/packages/datadog-plugin-http2/src/client.js +7 -8
  60. package/packages/datadog-plugin-jest/src/index.js +3 -2
  61. package/packages/datadog-plugin-mocha/src/index.js +6 -1
  62. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -1
  63. package/packages/datadog-plugin-mysql/src/index.js +11 -0
  64. package/packages/datadog-plugin-next/src/index.js +1 -1
  65. package/packages/datadog-plugin-openai/src/tracing.js +2 -4
  66. package/packages/datadog-plugin-oracledb/src/index.js +2 -1
  67. package/packages/datadog-plugin-playwright/src/index.js +3 -2
  68. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +8 -9
  69. package/packages/datadog-plugin-redis/src/index.js +1 -3
  70. package/packages/datadog-plugin-vitest/src/index.js +5 -4
  71. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +0 -1
  72. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +0 -1
  73. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +0 -1
  74. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -1
  75. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +1 -2
  76. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  77. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +12 -13
  78. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +44 -1
  79. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -1
  80. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +8 -3
  81. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -1
  82. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +1 -1
  83. package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +1 -2
  84. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +10 -11
  85. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +0 -4
  86. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +0 -1
  87. package/packages/dd-trace/src/appsec/index.js +16 -5
  88. package/packages/dd-trace/src/appsec/reporter.js +11 -11
  89. package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -2
  90. package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -3
  91. package/packages/dd-trace/src/appsec/telemetry/index.js +31 -1
  92. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -2
  93. package/packages/dd-trace/src/azure_metadata.js +8 -3
  94. package/packages/dd-trace/src/baggage.js +2 -2
  95. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +8 -7
  96. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -1
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -1
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -1
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -1
  100. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +4 -3
  101. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +7 -6
  102. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  103. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -3
  104. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -3
  105. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -1
  106. package/packages/dd-trace/src/config-helper.js +89 -0
  107. package/packages/dd-trace/src/config.js +120 -115
  108. package/packages/dd-trace/src/config_stable.js +7 -4
  109. package/packages/dd-trace/src/datastreams/fnv.js +1 -1
  110. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +6 -6
  111. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +1 -2
  112. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -2
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -1
  114. package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -3
  115. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +1 -2
  116. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +3 -4
  117. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +1 -1
  118. package/packages/dd-trace/src/debugger/devtools_client/status.js +5 -1
  119. package/packages/dd-trace/src/debugger/index.js +1 -0
  120. package/packages/dd-trace/src/dogstatsd.js +2 -2
  121. package/packages/dd-trace/src/encode/0.4.js +5 -2
  122. package/packages/dd-trace/src/encode/0.5.js +3 -5
  123. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +5 -5
  124. package/packages/dd-trace/src/exporter.js +2 -1
  125. package/packages/dd-trace/src/exporters/agent/writer.js +3 -1
  126. package/packages/dd-trace/src/exporters/common/docker.js +3 -2
  127. package/packages/dd-trace/src/exporters/common/request.js +4 -1
  128. package/packages/dd-trace/src/exporters/common/util.js +3 -1
  129. package/packages/dd-trace/src/id.js +3 -3
  130. package/packages/dd-trace/src/index.js +4 -3
  131. package/packages/dd-trace/src/lambda/handler.js +2 -1
  132. package/packages/dd-trace/src/lambda/index.js +2 -1
  133. package/packages/dd-trace/src/lambda/runtime/patch.js +3 -2
  134. package/packages/dd-trace/src/lambda/runtime/ritm.js +3 -2
  135. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  136. package/packages/dd-trace/src/llmobs/index.js +21 -5
  137. package/packages/dd-trace/src/llmobs/noop.js +18 -20
  138. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +11 -13
  139. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -2
  140. package/packages/dd-trace/src/llmobs/sdk.js +2 -1
  141. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  142. package/packages/dd-trace/src/llmobs/tagger.js +19 -6
  143. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  144. package/packages/dd-trace/src/log/index.js +5 -4
  145. package/packages/dd-trace/src/log/writer.js +1 -2
  146. package/packages/dd-trace/src/msgpack/encoder.js +3 -3
  147. package/packages/dd-trace/src/noop/span.js +1 -1
  148. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
  149. package/packages/dd-trace/src/opentracing/propagation/log.js +4 -5
  150. package/packages/dd-trace/src/opentracing/propagation/text_map.js +35 -42
  151. package/packages/dd-trace/src/opentracing/span.js +7 -6
  152. package/packages/dd-trace/src/payload-tagging/config/index.js +17 -21
  153. package/packages/dd-trace/src/plugin_manager.js +4 -3
  154. package/packages/dd-trace/src/plugins/ci_plugin.js +25 -1
  155. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  156. package/packages/dd-trace/src/plugins/util/ci.js +7 -7
  157. package/packages/dd-trace/src/plugins/util/git.js +1 -1
  158. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  159. package/packages/dd-trace/src/plugins/util/stacktrace.js +8 -1
  160. package/packages/dd-trace/src/plugins/util/test.js +4 -3
  161. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -1
  162. package/packages/dd-trace/src/plugins/util/web.js +3 -4
  163. package/packages/dd-trace/src/priority_sampler.js +46 -35
  164. package/packages/dd-trace/src/profiling/config.js +12 -32
  165. package/packages/dd-trace/src/profiling/exporter_cli.js +20 -20
  166. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  167. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +2 -1
  168. package/packages/dd-trace/src/profiling/index.js +2 -1
  169. package/packages/dd-trace/src/profiling/profiler.js +7 -4
  170. package/packages/dd-trace/src/profiling/profilers/events.js +10 -2
  171. package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +3 -1
  172. package/packages/dd-trace/src/profiling/tagger.js +22 -12
  173. package/packages/dd-trace/src/proxy.js +2 -1
  174. package/packages/dd-trace/src/ritm.js +4 -4
  175. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +3 -2
  176. package/packages/dd-trace/src/sampler.js +10 -2
  177. package/packages/dd-trace/src/serverless.js +11 -4
  178. package/packages/dd-trace/src/span_processor.js +2 -1
  179. package/packages/dd-trace/src/standalone/tracesource.js +1 -2
  180. package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +1 -2
  181. package/packages/dd-trace/src/startup-log.js +5 -17
  182. package/packages/dd-trace/src/supported-configurations.json +440 -0
  183. package/packages/dd-trace/src/telemetry/dependencies.js +62 -57
  184. package/packages/dd-trace/src/telemetry/send-data.js +7 -6
  185. package/packages/dd-trace/src/telemetry/telemetry.js +16 -26
  186. package/packages/dd-trace/src/tracer.js +3 -7
  187. package/packages/dd-trace/src/util.js +0 -5
  188. package/packages/datadog-core/src/utils/src/get.js +0 -11
  189. package/packages/datadog-core/src/utils/src/has.js +0 -14
  190. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +0 -120
  191. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +0 -20
@@ -421,7 +421,7 @@ function addAllowHeaders (req, res, headers) {
421
421
  ]
422
422
 
423
423
  for (const header of contextHeaders) {
424
- if (~requestHeaders.indexOf(header)) {
424
+ if (requestHeaders.includes(header)) {
425
425
  allowHeaders.push(header)
426
426
  }
427
427
  }
@@ -530,10 +530,9 @@ function extractURL (req) {
530
530
 
531
531
  if (req.stream) {
532
532
  return `${headers[HTTP2_HEADER_SCHEME]}://${headers[HTTP2_HEADER_AUTHORITY]}${headers[HTTP2_HEADER_PATH]}`
533
- } else {
534
- const protocol = getProtocol(req)
535
- return `${protocol}://${req.headers.host}${req.originalUrl || req.url}`
536
533
  }
534
+ const protocol = getProtocol(req)
535
+ return `${protocol}://${req.headers.host}${req.originalUrl || req.url}`
537
536
  }
538
537
 
539
538
  function getProtocol (req) {
@@ -5,7 +5,6 @@ const RateLimiter = require('./rate_limiter')
5
5
  const Sampler = require('./sampler')
6
6
  const { setSamplingRules } = require('./startup-log')
7
7
  const SamplingRule = require('./sampling_rule')
8
- const { hasOwn } = require('./util')
9
8
 
10
9
  const {
11
10
  SAMPLING_MECHANISM_DEFAULT,
@@ -40,16 +39,28 @@ const DEFAULT_KEY = 'service:,env:'
40
39
  const defaultSampler = new Sampler(AUTO_KEEP)
41
40
 
42
41
  /**
43
- * from config.js
44
- * @typedef { sampleRate: number, provenance: string, rateLimit: number, rules: SamplingRule[] } SamplingConfig
42
+ * PrioritySampler is responsible for determining whether a span should be sampled
43
+ * based on various rules, rate limits, and priorities. It supports manual and
44
+ * automatic sampling mechanisms and integrates with Datadog's tracing system.
45
45
  *
46
- * empirically defined
47
- * @typedef {2|-1|1|0} SamplingPriority
46
+ * @class PrioritySampler
47
+ * @typedef {import('./opentracing/span')} DatadogSpan
48
+ * @typedef {import('./opentracing/span_context')} DatadogSpanContext
49
+ * @typedef {import('./standalone/product')} PRODUCTS
50
+ * @typedef {2|-1|1|0} SamplingPriority Empirically defined sampling priorities.
48
51
  */
49
52
  class PrioritySampler {
50
53
  /**
51
- * @param env {string}
52
- * @param config {SamplingConfig}
54
+ * Creates an instance of PrioritySampler.
55
+ *
56
+ * @typedef {Object} SamplingConfig
57
+ * @property {number} [sampleRate] - The default sample rate for traces.
58
+ * @property {string} [provenance] - The provenance of the sampling rule (e.g., "customer", "dynamic").
59
+ * @property {number} [rateLimit=100] - The maximum number of traces to sample per second.
60
+ * @property {Array<SamplingRule>} [rules=[]] - An array of sampling rules to apply.
61
+ *
62
+ * @param {string} env - The environment name (e.g., "production", "staging").
63
+ * @param {SamplingConfig} config - The configuration object for sampling.
53
64
  */
54
65
  constructor (env, config) {
55
66
  this.configure(env, config)
@@ -62,9 +73,9 @@ class PrioritySampler {
62
73
  * @param opts {SamplingConfig}
63
74
  */
64
75
  configure (env, opts = {}) {
65
- const { sampleRate, provenance, rateLimit = 100, rules = [] } = opts
76
+ const { sampleRate, provenance, rateLimit = 100, rules } = opts
66
77
  this._env = env
67
- this._rules = this.#normalizeRules(rules, sampleRate, rateLimit, provenance)
78
+ this._rules = this.#normalizeRules(rules || [], sampleRate, rateLimit, provenance)
68
79
  this._limiter = new RateLimiter(rateLimit)
69
80
 
70
81
  log.trace(env, opts)
@@ -154,7 +165,7 @@ class PrioritySampler {
154
165
  *
155
166
  * @param span {DatadogSpan}
156
167
  * @param samplingPriority {SamplingPriority}
157
- * @param product {import('./standalone/product').PRODUCTS}
168
+ * @param product {import('./standalone/product')}
158
169
  */
159
170
  setPriority (span, samplingPriority, product) {
160
171
  if (!span || !this.validate(samplingPriority)) return
@@ -208,18 +219,17 @@ class PrioritySampler {
208
219
  * @returns {SamplingPriority}
209
220
  */
210
221
  _getPriorityFromTags (tags, _context) {
211
- if (hasOwn(tags, MANUAL_KEEP) && tags[MANUAL_KEEP] !== false) {
222
+ if (Object.hasOwn(tags, MANUAL_KEEP) && tags[MANUAL_KEEP] !== false) {
212
223
  return USER_KEEP
213
- } else if (hasOwn(tags, MANUAL_DROP) && tags[MANUAL_DROP] !== false) {
224
+ } else if (Object.hasOwn(tags, MANUAL_DROP) && tags[MANUAL_DROP] !== false) {
214
225
  return USER_REJECT
215
- } else {
216
- const priority = Number.parseInt(tags[SAMPLING_PRIORITY], 10)
226
+ }
227
+ const priority = Number.parseInt(tags[SAMPLING_PRIORITY], 10)
217
228
 
218
- if (priority === 1 || priority === 2) {
219
- return USER_KEEP
220
- } else if (priority === 0 || priority === -1) {
221
- return USER_REJECT
222
- }
229
+ if (priority === 1 || priority === 2) {
230
+ return USER_KEEP
231
+ } else if (priority === 0 || priority === -1) {
232
+ return USER_REJECT
223
233
  }
224
234
  }
225
235
 
@@ -258,7 +268,6 @@ class PrioritySampler {
258
268
  *
259
269
  * @param context {DatadogSpanContext}
260
270
  * @returns {SamplingPriority}
261
- * @private
262
271
  */
263
272
  #getPriorityByAgent (context) {
264
273
  const key = `service:${context._tags[SERVICE_NAME]},env:${this._env}`
@@ -274,7 +283,6 @@ class PrioritySampler {
274
283
  /**
275
284
  *
276
285
  * @param span {DatadogSpan}
277
- * @private
278
286
  * @returns {void}
279
287
  */
280
288
  #addDecisionMaker (span) {
@@ -293,30 +301,33 @@ class PrioritySampler {
293
301
  }
294
302
 
295
303
  /**
296
- *
297
- * @param rules {SamplingRule[]}
298
- * @param sampleRate {number}
299
- * @param rateLimit {number}
300
- * @param provenance {string}
304
+ * @param {Record<string, unknown>[] | Record<string, unknown>} rules - The sampling rules to normalize.
305
+ * @param {number} sampleRate
306
+ * @param {number} rateLimit
307
+ * @param {string} provenance
301
308
  * @returns {SamplingRule[]}
302
- * @private
303
309
  */
304
310
  #normalizeRules (rules, sampleRate, rateLimit, provenance) {
305
- rules = [].concat(rules || [])
311
+ rules = Array.isArray(rules) ? rules.flat() : [rules]
306
312
 
307
313
  rules.push({ sampleRate, maxPerSecond: rateLimit, provenance })
308
314
 
309
- return rules
310
- .map(rule => ({ ...rule, sampleRate: Number.parseFloat(rule.sampleRate) }))
311
- .filter(rule => !Number.isNaN(rule.sampleRate))
312
- .map(SamplingRule.from)
315
+ const result = []
316
+ for (const rule of rules) {
317
+ const sampleRate = Number.parseFloat(rule.sampleRate)
318
+ // TODO(BridgeAR): Debug logging invalid rules fails our tests.
319
+ // Should we definitely not know about these?
320
+ if (!Number.isNaN(sampleRate)) {
321
+ result.push(SamplingRule.from({ ...rule, sampleRate }))
322
+ }
323
+ }
324
+ return result
313
325
  }
314
326
 
315
327
  /**
316
328
  *
317
329
  * @param span {DatadogSpan}
318
- * @returns {SamplingRule}
319
- * @private
330
+ * @returns {SamplingRule|undefined}
320
331
  */
321
332
  #findRule (span) {
322
333
  for (const rule of this._rules) {
@@ -330,7 +341,7 @@ class PrioritySampler {
330
341
  /**
331
342
  *
332
343
  * @param span {DatadogSpan}
333
- * @param product {import('./standalone/product').PRODUCTS}
344
+ * @param product {import('./standalone/product')}
334
345
  */
335
346
  static keepTrace (span, product) {
336
347
  span?._prioritySampler?.setPriority(span, USER_KEEP, product)
@@ -15,10 +15,12 @@ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
15
15
  const { tagger } = require('./tagger')
16
16
  const { isFalse, isTrue } = require('../util')
17
17
  const { getAzureTagsFromMetadata, getAzureAppMetadata } = require('../azure_metadata')
18
+ const { getEnvironmentVariables } = require('../config-helper')
18
19
 
19
20
  class Config {
20
21
  constructor (options = {}) {
21
22
  const {
23
+ AWS_LAMBDA_FUNCTION_NAME: functionname,
22
24
  DD_AGENT_HOST,
23
25
  DD_ENV,
24
26
  DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED, // used for testing
@@ -27,14 +29,10 @@ class Config {
27
29
  DD_PROFILING_DEBUG_SOURCE_MAPS,
28
30
  DD_PROFILING_DEBUG_UPLOAD_COMPRESSION,
29
31
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
30
- DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
31
- DD_PROFILING_EXPERIMENTAL_CPU_ENABLED,
32
- DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED,
33
32
  DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
34
33
  DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
35
34
  DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
36
35
  DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
37
- DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED,
38
36
  DD_PROFILING_HEAP_ENABLED,
39
37
  DD_PROFILING_HEAP_SAMPLING_INTERVAL,
40
38
  DD_PROFILING_PPROF_PREFIX,
@@ -50,13 +48,12 @@ class Config {
50
48
  DD_TRACE_AGENT_PORT,
51
49
  DD_TRACE_AGENT_URL,
52
50
  DD_VERSION
53
- } = process.env
51
+ } = getEnvironmentVariables()
54
52
 
55
53
  const env = coalesce(options.env, DD_ENV)
56
54
  const service = options.service || DD_SERVICE || 'node'
57
55
  const host = os.hostname()
58
56
  const version = coalesce(options.version, DD_VERSION)
59
- const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
60
57
  // Must be longer than one minute so pad with five seconds
61
58
  const flushInterval = coalesce(options.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
62
59
  const uploadTimeout = coalesce(options.uploadTimeout,
@@ -86,16 +83,6 @@ class Config {
86
83
  }
87
84
 
88
85
  this.logger = ensureLogger(options.logger)
89
- const logger = this.logger
90
- function logExperimentalVarDeprecation (shortVarName) {
91
- const deprecatedEnvVarName = `DD_PROFILING_EXPERIMENTAL_${shortVarName}`
92
- const v = process.env[deprecatedEnvVarName]
93
- // not null, undefined, or NaN -- same logic as koalas.hasValue
94
- // eslint-disable-next-line no-self-compare
95
- if (v != null && v === v) {
96
- logger.warn(`${deprecatedEnvVarName} is deprecated. Use DD_PROFILING_${shortVarName} instead.`)
97
- }
98
- }
99
86
  // Profiler sampling contexts are not available on Windows, so features
100
87
  // depending on those (code hotspots and endpoint collection) need to default
101
88
  // to false on Windows.
@@ -119,9 +106,7 @@ class Config {
119
106
  this.sourceMap = sourceMap
120
107
  this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
121
108
  this.endpointCollectionEnabled = isTrue(coalesce(options.endpointCollection,
122
- DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
123
- DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, samplingContextsAvailable))
124
- logExperimentalVarDeprecation('ENDPOINT_COLLECTION_ENABLED')
109
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, samplingContextsAvailable))
125
110
  checkOptionWithSamplingContextAllowed(this.endpointCollectionEnabled, 'Endpoint collection')
126
111
 
127
112
  this.pprofPrefix = pprofPrefix
@@ -172,23 +157,18 @@ class Config {
172
157
  })
173
158
 
174
159
  this.timelineEnabled = isTrue(coalesce(options.timelineEnabled,
175
- DD_PROFILING_TIMELINE_ENABLED,
176
- DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED, samplingContextsAvailable))
177
- logExperimentalVarDeprecation('TIMELINE_ENABLED')
160
+ DD_PROFILING_TIMELINE_ENABLED, samplingContextsAvailable))
178
161
  checkOptionWithSamplingContextAllowed(this.timelineEnabled, 'Timeline view')
179
162
  this.timelineSamplingEnabled = isTrue(coalesce(options.timelineSamplingEnabled,
180
163
  DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED, true))
181
164
 
182
165
  this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
183
- DD_PROFILING_CODEHOTSPOTS_ENABLED,
184
- DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, samplingContextsAvailable))
185
- logExperimentalVarDeprecation('CODEHOTSPOTS_ENABLED')
166
+ DD_PROFILING_CODEHOTSPOTS_ENABLED, samplingContextsAvailable))
186
167
  checkOptionWithSamplingContextAllowed(this.codeHotspotsEnabled, 'Code hotspots')
187
168
 
188
169
  this.cpuProfilingEnabled = isTrue(coalesce(options.cpuProfilingEnabled,
189
170
  DD_PROFILING_CPU_ENABLED,
190
- DD_PROFILING_EXPERIMENTAL_CPU_ENABLED, samplingContextsAvailable))
191
- logExperimentalVarDeprecation('CPU_ENABLED')
171
+ samplingContextsAvailable))
192
172
  checkOptionWithSamplingContextAllowed(this.cpuProfilingEnabled, 'CPU profiling')
193
173
 
194
174
  this.heapSamplingInterval = coalesce(options.heapSamplingInterval,
@@ -196,25 +176,25 @@ class Config {
196
176
  const uploadCompression0 = coalesce(options.uploadCompression, DD_PROFILING_DEBUG_UPLOAD_COMPRESSION, 'on')
197
177
  let [uploadCompression, level0] = uploadCompression0.split('-')
198
178
  if (!['on', 'off', 'gzip', 'zstd'].includes(uploadCompression)) {
199
- logger.warn(`Invalid profile upload compression method "${uploadCompression0}". Will use "on".`)
179
+ this.logger.warn(`Invalid profile upload compression method "${uploadCompression0}". Will use "on".`)
200
180
  uploadCompression = 'on'
201
181
  }
202
182
  let level = level0 ? Number.parseInt(level0, 10) : undefined
203
183
  if (level !== undefined) {
204
184
  if (['on', 'off'].includes(uploadCompression)) {
205
- logger.warn(`Compression levels are not supported for "${uploadCompression}".`)
185
+ this.logger.warn(`Compression levels are not supported for "${uploadCompression}".`)
206
186
  level = undefined
207
187
  } else if (Number.isNaN(level)) {
208
- logger.warn(
188
+ this.logger.warn(
209
189
  `Invalid compression level "${level0}". Will use default level.`)
210
190
  level = undefined
211
191
  } else if (level < 1) {
212
- logger.warn(`Invalid compression level ${level}. Will use 1.`)
192
+ this.logger.warn(`Invalid compression level ${level}. Will use 1.`)
213
193
  level = 1
214
194
  } else {
215
195
  const maxLevel = { gzip: 9, zstd: 22 }[uploadCompression]
216
196
  if (level > maxLevel) {
217
- logger.warn(`Invalid compression level ${level}. Will use ${maxLevel}.`)
197
+ this.logger.warn(`Invalid compression level ${level}. Will use ${maxLevel}.`)
218
198
  level = maxLevel
219
199
  }
220
200
  }
@@ -8,6 +8,7 @@ const { ConsoleLogger } = require('./loggers/console')
8
8
  const { tagger } = require('./tagger')
9
9
  const fs = require('fs')
10
10
  const { fileURLToPath } = require('url')
11
+ const { getEnvironmentVariable } = require('../config-helper')
11
12
 
12
13
  const logger = new ConsoleLogger()
13
14
  const timeoutMs = 15 * 1000
@@ -15,25 +16,24 @@ const timeoutMs = 15 * 1000
15
16
  function exporterFromURL (url) {
16
17
  if (url.protocol === 'file:') {
17
18
  return new FileExporter({ pprofPrefix: fileURLToPath(url) })
18
- } else {
19
- const injectionEnabled = (process.env.DD_INJECTION_ENABLED || '').split(',')
20
- const libraryInjected = injectionEnabled.length > 0
21
- const profilingEnabled = (process.env.DD_PROFILING_ENABLED || '').toLowerCase()
22
- const activation = ['true', '1'].includes(profilingEnabled)
23
- ? 'manual'
24
- : profilingEnabled === 'auto'
25
- ? 'auto'
26
- : injectionEnabled.includes('profiling')
27
- ? 'injection'
28
- : 'unknown'
29
- return new AgentExporter({
30
- url,
31
- logger,
32
- uploadTimeout: timeoutMs,
33
- libraryInjected,
34
- activation
35
- })
36
19
  }
20
+ const injectionEnabled = (getEnvironmentVariable('DD_INJECTION_ENABLED') ?? '').split(',')
21
+ const libraryInjected = injectionEnabled.length > 0
22
+ const profilingEnabled = (getEnvironmentVariable('DD_PROFILING_ENABLED') ?? '').toLowerCase()
23
+ const activation = ['true', '1'].includes(profilingEnabled)
24
+ ? 'manual'
25
+ : profilingEnabled === 'auto'
26
+ ? 'auto'
27
+ : injectionEnabled.includes('profiling')
28
+ ? 'injection'
29
+ : 'unknown'
30
+ return new AgentExporter({
31
+ url,
32
+ logger,
33
+ uploadTimeout: timeoutMs,
34
+ libraryInjected,
35
+ activation
36
+ })
37
37
  }
38
38
 
39
39
  async function exportProfile (urls, tags, profileType, profile) {
@@ -46,7 +46,7 @@ async function exportProfile (urls, tags, profileType, profile) {
46
46
 
47
47
  const encodedProfile = await encode(heap.convertProfile(profile, undefined, mapper))
48
48
  const start = new Date()
49
- for (const url of urls) {
49
+ await Promise.all(urls.map(async (url) => {
50
50
  const exporter = exporterFromURL(url)
51
51
 
52
52
  await exporter.export({
@@ -57,7 +57,7 @@ async function exportProfile (urls, tags, profileType, profile) {
57
57
  end: start,
58
58
  tags
59
59
  })
60
- }
60
+ }))
61
61
  }
62
62
 
63
63
  /** Expected command line arguments are:
@@ -150,7 +150,7 @@ class AgentExporter extends EventSerializer {
150
150
  'DD-EVP-ORIGIN-VERSION': version,
151
151
  ...form.getHeaders()
152
152
  },
153
- timeout: this._backoffTime * Math.pow(2, attempt)
153
+ timeout: this._backoffTime * 2 ** attempt
154
154
  }
155
155
 
156
156
  docker.inject(options.headers)
@@ -1,9 +1,10 @@
1
1
  const os = require('os')
2
2
  const perf = require('perf_hooks').performance
3
3
  const version = require('../../../../../package.json').version
4
+ const { getEnvironmentVariable } = require('../../config-helper')
4
5
 
5
6
  const libuvThreadPoolSize = (() => {
6
- const ss = process.env.UV_THREADPOOL_SIZE
7
+ const ss = getEnvironmentVariable('UV_THREADPOOL_SIZE')
7
8
  if (ss === undefined) {
8
9
  // Backend will apply the default size based on Node version.
9
10
  return
@@ -6,8 +6,9 @@ const SpaceProfiler = require('./profilers/space')
6
6
  const { AgentExporter } = require('./exporters/agent')
7
7
  const { FileExporter } = require('./exporters/file')
8
8
  const { ConsoleLogger } = require('./loggers/console')
9
+ const { getEnvironmentVariable } = require('../config-helper')
9
10
 
10
- const profiler = process.env.AWS_LAMBDA_FUNCTION_NAME ? new ServerlessProfiler() : new Profiler()
11
+ const profiler = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') ? new ServerlessProfiler() : new Profiler()
11
12
 
12
13
  module.exports = {
13
14
  profiler,
@@ -226,7 +226,7 @@ class Profiler extends EventEmitter {
226
226
  const encodedProfiles = {}
227
227
 
228
228
  try {
229
- if (Object.keys(this._config.profilers).length === 0) {
229
+ if (this._config.profilers.length === 0) {
230
230
  throw new Error('No profile types configured.')
231
231
  }
232
232
 
@@ -246,8 +246,10 @@ class Profiler extends EventEmitter {
246
246
  this._capture(this._timeoutInterval, endDate)
247
247
  }
248
248
 
249
+ let hasEncoded = false
250
+
249
251
  // encode and export asynchronously
250
- for (const { profiler, profile } of profiles) {
252
+ await Promise.all(profiles.map(async ({ profiler, profile }) => {
251
253
  try {
252
254
  const encoded = await profiler.encode(profile)
253
255
  const compressed = encoded instanceof Buffer && this._compressionFn !== undefined
@@ -260,14 +262,15 @@ class Profiler extends EventEmitter {
260
262
  })
261
263
  return `Collected ${profiler.type} profile: ` + profileJson
262
264
  })
265
+ hasEncoded = true
263
266
  } catch (err) {
264
267
  // If encoding one of the profile types fails, we should still try to
265
268
  // encode and submit the other profile types.
266
269
  this._logError(err)
267
270
  }
268
- }
271
+ }))
269
272
 
270
- if (Object.keys(encodedProfiles).length > 0) {
273
+ if (hasEncoded) {
271
274
  await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
272
275
  profileSubmittedChannel.publish()
273
276
  this._logger.debug('Submitted profiles')
@@ -291,8 +291,16 @@ class NodeApiEventSource {
291
291
 
292
292
  class DatadogInstrumentationEventSource {
293
293
  constructor (eventHandler, eventFilter) {
294
- this.plugins = ['dns_lookup', 'dns_lookupservice', 'dns_resolve', 'dns_reverse', 'fs', 'net'].map(m => {
295
- const Plugin = require(`./event_plugins/${m}`)
294
+ // List all entries explicitly for bundlers to pick up the require calls correctly.
295
+ const plugins = [
296
+ require('./event_plugins/dns_lookup'),
297
+ require('./event_plugins/dns_lookupservice'),
298
+ require('./event_plugins/dns_resolve'),
299
+ require('./event_plugins/dns_reverse'),
300
+ require('./event_plugins/fs'),
301
+ require('./event_plugins/net')
302
+ ]
303
+ this.plugins = plugins.map((Plugin) => {
296
304
  return new Plugin(eventHandler, eventFilter)
297
305
  })
298
306
 
@@ -3,7 +3,9 @@
3
3
  const dc = require('dc-polyfill')
4
4
  const coalesce = require('koalas')
5
5
  const profileSubmittedChannel = dc.channel('datadog:profiling:mock-profile-submitted')
6
- const { DD_PROFILING_UPLOAD_PERIOD } = process.env
6
+ const { getEnvironmentVariable } = require('../config-helper')
7
+
8
+ const DD_PROFILING_UPLOAD_PERIOD = getEnvironmentVariable('DD_PROFILING_UPLOAD_PERIOD')
7
9
 
8
10
  let timerId
9
11
 
@@ -5,20 +5,30 @@ const tagger = {
5
5
  if (!tags) return {}
6
6
 
7
7
  switch (typeof tags) {
8
- case 'object':
9
- return Array.isArray(tags)
10
- ? tags.reduce((prev, next) => {
11
- const parts = next.split(':')
12
- const key = parts.shift().trim()
13
- const value = parts.join(':').trim()
8
+ case 'object': {
9
+ if (Array.isArray(tags)) {
10
+ const tagObject = {}
11
+ for (const tag of tags) {
12
+ const colon = tag.indexOf(':')
13
+ if (colon === -1) continue
14
+ const key = tag.slice(0, colon).trim()
15
+ const value = tag.slice(colon + 1).trim()
16
+ if (key.length !== 0 && value.length !== 0) {
17
+ tagObject[key] = value
18
+ }
19
+ }
20
+ return tagObject
21
+ }
14
22
 
15
- if (!key || !value) return prev
23
+ const tagsArray = []
24
+ for (const [key, value] of Object.entries(tags)) {
25
+ if (value != null) {
26
+ tagsArray.push(`${key}:${value}`)
27
+ }
28
+ }
16
29
 
17
- return Object.assign(prev, { [key]: value })
18
- }, {})
19
- : tagger.parse(Object.keys(tags)
20
- .filter(key => tags[key] !== undefined && tags[key] !== null)
21
- .map(key => `${key}:${tags[key]}`))
30
+ return tagger.parse(tagsArray)
31
+ }
22
32
  case 'string':
23
33
  return tagger.parse(tags.split(','))
24
34
  default:
@@ -10,6 +10,7 @@ const telemetry = require('./telemetry')
10
10
  const nomenclature = require('./service-naming')
11
11
  const PluginManager = require('./plugin_manager')
12
12
  const NoopDogStatsDClient = require('./noop/dogstatsd')
13
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
13
14
  const {
14
15
  setBaggageItem,
15
16
  getBaggageItem,
@@ -198,7 +199,7 @@ class Tracer extends NoopProxy {
198
199
  this._testApiManualPlugin.configure({ ...config, enabled: true }, false)
199
200
  }
200
201
  if (config.ciVisAgentlessLogSubmissionEnabled) {
201
- if (process.env.DD_API_KEY) {
202
+ if (getEnvironmentVariable('DD_API_KEY')) {
202
203
  const LogSubmissionPlugin = require('./ci-visibility/log-submission/log-submission-plugin')
203
204
  const automaticLogPlugin = new LogSubmissionPlugin(this)
204
205
  automaticLogPlugin.configure({ ...config, enabled: true })
@@ -4,6 +4,7 @@ const path = require('path')
4
4
  const Module = require('module')
5
5
  const parse = require('module-details-from-path')
6
6
  const dc = require('dc-polyfill')
7
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
7
8
 
8
9
  const origRequire = Module.prototype.require
9
10
 
@@ -82,9 +83,8 @@ function Hook (modules, options, onrequire) {
82
83
  if (patched) {
83
84
  // If it's already patched, just return it as-is.
84
85
  return origRequire.apply(this, arguments)
85
- } else {
86
- patching[filename] = true
87
86
  }
87
+ patching[filename] = true
88
88
 
89
89
  const payload = {
90
90
  filename,
@@ -110,8 +110,8 @@ function Hook (modules, options, onrequire) {
110
110
  if (!hooks) return exports // abort if module name isn't on whitelist
111
111
  name = filename
112
112
  } else {
113
- const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
114
- const hasLambdaHandler = process.env.DD_LAMBDA_HANDLER !== undefined
113
+ const inAWSLambda = getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined
114
+ const hasLambdaHandler = getEnvironmentVariable('DD_LAMBDA_HANDLER') !== undefined
115
115
  const segments = filename.split(path.sep)
116
116
  const filenameFromNodeModule = segments.includes('node_modules')
117
117
  // decide how to assign the stat
@@ -8,9 +8,10 @@ const { DogStatsDClient, MetricsAggregationClient } = require('../dogstatsd')
8
8
  const log = require('../log')
9
9
  const Histogram = require('../histogram')
10
10
  const { performance, PerformanceObserver } = require('perf_hooks')
11
+ const { getEnvironmentVariable } = require('../config-helper')
11
12
 
12
13
  const { NODE_MAJOR, NODE_MINOR } = require('../../../../version')
13
- const { DD_RUNTIME_METRICS_FLUSH_INTERVAL = '10000' } = process.env
14
+ const DD_RUNTIME_METRICS_FLUSH_INTERVAL = getEnvironmentVariable('DD_RUNTIME_METRICS_FLUSH_INTERVAL') ?? '10000'
14
15
  const INTERVAL = Number.parseInt(DD_RUNTIME_METRICS_FLUSH_INTERVAL, 10)
15
16
 
16
17
  // Node >=16 has PerformanceObserver with `gc` type, but <16.7 had a critical bug.
@@ -202,7 +203,7 @@ function captureGCMetrics () {
202
203
  const pause = {}
203
204
 
204
205
  for (const stat of profile.statistics) {
205
- const type = stat.gcType.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase()
206
+ const type = stat.gcType.replaceAll(/([a-z])([A-Z])/g, '$1_$2').toLowerCase()
206
207
 
207
208
  pause[type] = pause[type] || new Histogram()
208
209
  pause[type].record(stat.cost)
@@ -16,12 +16,16 @@ const SAMPLING_KNUTH_FACTOR = 1_111_111_111_111_111_111n
16
16
  * This class uses a deterministic sampling algorithm that is consistent across all languages.
17
17
  */
18
18
  class Sampler {
19
+ #threshold = 0n
20
+
19
21
  /**
20
22
  * @param {number} rate
21
23
  */
22
24
  constructor (rate) {
25
+ // TODO: Should this be moved up to the calling parts?
26
+ rate = Math.min(Math.max(rate, 0), 1)
23
27
  this._rate = rate
24
- this._threshold = BigInt(Math.floor(rate * MAX_TRACE_ID))
28
+ this.#threshold = BigInt(Math.floor(rate * MAX_TRACE_ID))
25
29
  }
26
30
 
27
31
  /**
@@ -31,6 +35,10 @@ class Sampler {
31
35
  return this._rate
32
36
  }
33
37
 
38
+ get threshold () {
39
+ return this.#threshold
40
+ }
41
+
34
42
  /**
35
43
  * Determines whether a trace/span should be sampled based on the configured sampling rate.
36
44
  *
@@ -48,7 +56,7 @@ class Sampler {
48
56
 
49
57
  span = typeof span.context === 'function' ? span.context() : span
50
58
 
51
- return (span._traceId.toBigInt() * SAMPLING_KNUTH_FACTOR) % UINT64_MODULO <= this._threshold
59
+ return (span._traceId.toBigInt() * SAMPLING_KNUTH_FACTOR) % UINT64_MODULO <= this.#threshold
52
60
  }
53
61
  }
54
62