dd-trace 5.104.0 → 5.106.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 (159) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +82 -3
  3. package/package.json +15 -15
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +16 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
  11. package/packages/datadog-instrumentations/src/cucumber.js +390 -157
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  14. package/packages/datadog-instrumentations/src/graphql.js +188 -62
  15. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  18. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  19. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  20. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
  27. package/packages/datadog-instrumentations/src/hono.js +54 -3
  28. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  29. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  30. package/packages/datadog-instrumentations/src/jest.js +360 -150
  31. package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
  32. package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
  33. package/packages/datadog-instrumentations/src/nats.js +182 -0
  34. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  35. package/packages/datadog-instrumentations/src/openai.js +33 -18
  36. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  37. package/packages/datadog-instrumentations/src/pino.js +17 -5
  38. package/packages/datadog-instrumentations/src/playwright.js +515 -292
  39. package/packages/datadog-instrumentations/src/router.js +76 -32
  40. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  41. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  42. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
  43. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
  44. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  45. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  46. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  47. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  48. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  49. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  50. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
  51. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  52. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  53. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  54. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  55. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  56. package/packages/datadog-plugin-http/src/server.js +40 -15
  57. package/packages/datadog-plugin-jest/src/index.js +11 -3
  58. package/packages/datadog-plugin-jest/src/util.js +15 -8
  59. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  60. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
  61. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  62. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  63. package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
  64. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  65. package/packages/datadog-plugin-nats/src/index.js +20 -0
  66. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  67. package/packages/datadog-plugin-nats/src/util.js +33 -0
  68. package/packages/datadog-plugin-next/src/index.js +5 -3
  69. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  70. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  71. package/packages/datadog-plugin-pino/src/index.js +42 -0
  72. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  73. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  74. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  75. package/packages/datadog-plugin-router/src/index.js +33 -44
  76. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  77. package/packages/datadog-plugin-vitest/src/index.js +5 -13
  78. package/packages/datadog-plugin-winston/src/index.js +30 -0
  79. package/packages/datadog-shimmer/src/shimmer.js +33 -40
  80. package/packages/dd-trace/src/aiguard/index.js +1 -1
  81. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  82. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  83. package/packages/dd-trace/src/appsec/index.js +1 -1
  84. package/packages/dd-trace/src/appsec/reporter.js +5 -6
  85. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  86. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  87. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  88. package/packages/dd-trace/src/baggage.js +7 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  90. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  91. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  92. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
  93. package/packages/dd-trace/src/config/supported-configurations.json +27 -8
  94. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  95. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  96. package/packages/dd-trace/src/encode/0.4.js +124 -108
  97. package/packages/dd-trace/src/encode/0.5.js +114 -26
  98. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
  99. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  100. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  101. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  102. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  103. package/packages/dd-trace/src/id.js +15 -0
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +92 -6
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
  106. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  107. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  108. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  109. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  110. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  111. package/packages/dd-trace/src/llmobs/sdk.js +0 -16
  112. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  113. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  114. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  115. package/packages/dd-trace/src/llmobs/util.js +66 -3
  116. package/packages/dd-trace/src/log/index.js +1 -1
  117. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  118. package/packages/dd-trace/src/msgpack/index.js +96 -2
  119. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  120. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  121. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  122. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  123. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  124. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  125. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +22 -3
  126. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  127. package/packages/dd-trace/src/opentracing/propagation/text_map.js +64 -77
  128. package/packages/dd-trace/src/opentracing/span.js +59 -19
  129. package/packages/dd-trace/src/opentracing/span_context.js +50 -3
  130. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
  131. package/packages/dd-trace/src/plugins/database.js +7 -6
  132. package/packages/dd-trace/src/plugins/index.js +4 -0
  133. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  134. package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
  135. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  136. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  137. package/packages/dd-trace/src/plugins/tracing.js +43 -5
  138. package/packages/dd-trace/src/plugins/util/test.js +236 -13
  139. package/packages/dd-trace/src/plugins/util/web.js +79 -65
  140. package/packages/dd-trace/src/priority_sampler.js +2 -2
  141. package/packages/dd-trace/src/profiling/config.js +10 -23
  142. package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
  143. package/packages/dd-trace/src/profiling/profiler.js +21 -11
  144. package/packages/dd-trace/src/profiling/profilers/wall.js +12 -7
  145. package/packages/dd-trace/src/sampling_rule.js +7 -7
  146. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  147. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  148. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  149. package/packages/dd-trace/src/span_format.js +190 -58
  150. package/packages/dd-trace/src/spanleak.js +1 -1
  151. package/packages/dd-trace/src/standalone/index.js +3 -3
  152. package/packages/dd-trace/src/tagger.js +0 -2
  153. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  154. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  155. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  156. package/vendor/dist/protobufjs/index.js +1 -1
  157. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  158. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  159. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
@@ -12,9 +12,9 @@ const {
12
12
  TEST_BROWSER_NAME,
13
13
  TEST_BROWSER_VERSION,
14
14
  TEST_CODE_OWNERS,
15
- TEST_COMMAND,
16
15
  TEST_EARLY_FLAKE_ABORT_REASON,
17
16
  TEST_EARLY_FLAKE_ENABLED,
17
+ TEST_FRAMEWORK_VERSION,
18
18
  TEST_HAS_FAILED_ALL_RETRIES,
19
19
  TEST_IS_MODIFIED,
20
20
  TEST_IS_NEW,
@@ -234,7 +234,7 @@ class PlaywrightPlugin extends CiPlugin {
234
234
  formattedSpan.meta[TEST_SESSION_ID] = this.testSessionSpan.context().toTraceId()
235
235
  formattedSpan.meta[TEST_MODULE_ID] = this.testModuleSpan.context().toSpanId()
236
236
  Object.assign(formattedSpan.meta, this.getSessionRequestErrorTags())
237
- formattedSpan.meta[TEST_COMMAND] = this.command
237
+ formattedSpan.meta[TEST_FRAMEWORK_VERSION] = this.frameworkVersion
238
238
  formattedSpan.meta[TEST_MODULE] = this.constructor.id
239
239
  // MISSING _trace.startTime and _trace.ticks - because by now the suite is already serialized
240
240
  const testSuite = this._testSuiteSpansByTestSuiteAbsolutePath.get(
@@ -326,7 +326,7 @@ class PlaywrightPlugin extends CiPlugin {
326
326
  }) => {
327
327
  if (!span) return
328
328
 
329
- const isRUMActive = span.context()._tags[TEST_IS_RUM_ACTIVE]
329
+ const isRUMActive = span.context().getTag(TEST_IS_RUM_ACTIVE)
330
330
 
331
331
  span.setTag(TEST_STATUS, testStatus)
332
332
 
@@ -416,7 +416,7 @@ class PlaywrightPlugin extends CiPlugin {
416
416
  TELEMETRY_EVENT_FINISHED,
417
417
  'test',
418
418
  {
419
- hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS],
419
+ hasCodeOwners: !!span.context().getTag(TEST_CODE_OWNERS),
420
420
  isNew,
421
421
  isRum: isRUMActive,
422
422
  browserDriver: 'playwright',
@@ -135,7 +135,7 @@ class SchemaExtractor {
135
135
  return
136
136
  }
137
137
 
138
- if (span.context()._tags[SCHEMA_TYPE] && operation === 'serialization') {
138
+ if (span.context().getTag(SCHEMA_TYPE) && operation === 'serialization') {
139
139
  // we have already added a schema to this span, this call is an encode of nested schema types
140
140
  return
141
141
  }
@@ -42,7 +42,7 @@ function addDeliveryAnnotations (msg, tracer, span) {
42
42
  tracer.inject(span, 'text_map', msg.delivery_annotations)
43
43
 
44
44
  if (tracer._config.dsmEnabled) {
45
- const targetName = span.context()._tags['amqp.link.target.address']
45
+ const targetName = span.context().getTag('amqp.link.target.address')
46
46
  const payloadSize = getAmqpMessageSize({ content: msg.body, headers: msg.delivery_annotations })
47
47
  const dataStreamsContext = tracer
48
48
  .setCheckpoint(['direction:out', `exchange:${targetName}`, 'type:rabbitmq'], span, payloadSize)
@@ -9,31 +9,35 @@ const { COMPONENT } = require('../../dd-trace/src/constants')
9
9
  class RouterPlugin extends WebPlugin {
10
10
  static id = 'router'
11
11
 
12
- #storeStacks = new WeakMap()
13
12
  #contexts = new WeakMap()
14
13
 
15
14
  constructor (...args) {
16
15
  super(...args)
17
16
 
18
17
  this.addSub(`apm:${this.constructor.id}:middleware:enter`, ({ req, name, route }) => {
19
- const childOf = this.#getActive(req) || this.#getStoreSpan()
20
-
18
+ // One ALS hop covers both the parent-span fallback (when no
19
+ // per-request context exists yet) and the `storeStack` push below.
20
+ // The previous shape paid an ALS read inside `#getStoreSpan` and a
21
+ // second one here for the saved-store push.
22
+ const store = storage('legacy').getStore()
23
+ let context = this.#contexts.get(req)
24
+ let childOf
25
+ if (context !== undefined) {
26
+ const middleware = context.middleware
27
+ childOf = middleware.length === 0 ? context.span : middleware[middleware.length - 1]
28
+ } else if (store) {
29
+ childOf = store.span
30
+ }
21
31
  if (!childOf) return
22
32
 
23
33
  const span = this.#getMiddlewareSpan(name, childOf)
24
- const context = this.#createContext(req, route, childOf)
34
+ context = this.#updateContext(req, context, route, childOf)
25
35
 
26
36
  if (childOf !== span) {
27
37
  context.middleware.push(span)
28
38
  }
29
39
 
30
- const store = storage('legacy').getStore()
31
- let storeStack = this.#storeStacks.get(req)
32
- if (!storeStack) {
33
- storeStack = []
34
- this.#storeStacks.set(req, storeStack)
35
- }
36
- storeStack.push(store)
40
+ context.storeStack.push(store)
37
41
  this.enter(span, store)
38
42
 
39
43
  web.patch(req)
@@ -57,11 +61,8 @@ class RouterPlugin extends WebPlugin {
57
61
  })
58
62
 
59
63
  this.addSub(`apm:${this.constructor.id}:middleware:exit`, ({ req }) => {
60
- const storeStack = this.#storeStacks.get(req)
61
- const savedStore = storeStack && storeStack.pop()
62
- if (storeStack && storeStack.length === 0) {
63
- this.#storeStacks.delete(req)
64
- }
64
+ const context = this.#contexts.get(req)
65
+ const savedStore = context && context.storeStack.pop()
65
66
  const span = savedStore && savedStore.span
66
67
  this.enter(span, savedStore)
67
68
  })
@@ -71,8 +72,10 @@ class RouterPlugin extends WebPlugin {
71
72
 
72
73
  if (!this.config.middleware) return
73
74
 
74
- const span = this.#getActive(req)
75
-
75
+ const context = this.#contexts.get(req)
76
+ if (!context) return
77
+ const middleware = context.middleware
78
+ const span = middleware.length === 0 ? context.span : middleware[middleware.length - 1]
76
79
  if (!span) return
77
80
 
78
81
  span.setTag('error', error)
@@ -91,21 +94,6 @@ class RouterPlugin extends WebPlugin {
91
94
  })
92
95
  }
93
96
 
94
- #getActive (req) {
95
- const context = this.#contexts.get(req)
96
-
97
- if (!context) return
98
- if (context.middleware.length === 0) return context.span
99
-
100
- return context.middleware.at(-1)
101
- }
102
-
103
- #getStoreSpan () {
104
- const store = storage('legacy').getStore()
105
-
106
- return store && store.span
107
- }
108
-
109
97
  #getMiddlewareSpan (name, childOf) {
110
98
  if (this.config.middleware === false) {
111
99
  return childOf
@@ -125,9 +113,7 @@ class RouterPlugin extends WebPlugin {
125
113
  return span
126
114
  }
127
115
 
128
- #createContext (req, route, span) {
129
- let context = this.#contexts.get(req)
130
-
116
+ #updateContext (req, context, route, span) {
131
117
  if (!route || route === '/' || route === '*') {
132
118
  route = ''
133
119
  }
@@ -141,17 +127,20 @@ class RouterPlugin extends WebPlugin {
141
127
  if (isMoreSpecificThan(route, context.route)) {
142
128
  context.route = route
143
129
  }
144
- } else {
145
- context = {
146
- span,
147
- stack: [route],
148
- route,
149
- middleware: [],
150
- }
130
+ return context
131
+ }
151
132
 
152
- this.#contexts.set(req, context)
133
+ // Five-property shape pinned at allocation so every request shares the
134
+ // same hidden class — no per-field transitions after construction.
135
+ context = {
136
+ span,
137
+ stack: [route],
138
+ route,
139
+ middleware: [],
140
+ storeStack: [],
153
141
  }
154
142
 
143
+ this.#contexts.set(req, context)
155
144
  return context
156
145
  }
157
146
  }
@@ -14,7 +14,7 @@ const {
14
14
  const { SPAN_TYPE } = require('../../../ext/tags')
15
15
 
16
16
  function isTestSpan (span) {
17
- return span.context()._tags[SPAN_TYPE] === 'test'
17
+ return span.context().getTag(SPAN_TYPE) === 'test'
18
18
  }
19
19
 
20
20
  function getTestSpanFromTrace (trace) {
@@ -15,7 +15,7 @@ const {
15
15
  TEST_IS_RETRY,
16
16
  TEST_CODE_COVERAGE_LINES_PCT,
17
17
  TEST_CODE_OWNERS,
18
- TEST_LEVEL_EVENT_TYPES,
18
+ TEST_COMMAND,
19
19
  TEST_SESSION_NAME,
20
20
  TEST_SOURCE_START,
21
21
  TEST_IS_NEW,
@@ -323,19 +323,11 @@ class VitestPlugin extends CiPlugin {
323
323
  const trimmedCommand = DD_MAJOR < 6 ? this.command : 'vitest run'
324
324
  // test suites run in a different process, so they also need to init the metadata dictionary
325
325
  const testSessionName = getTestSessionName(this.config, trimmedCommand, this.testEnvironmentMetadata)
326
- const metadataTags = {}
327
- for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
328
- metadataTags[testLevel] = {
329
- [TEST_SESSION_NAME]: testSessionName,
330
- }
331
- }
332
326
  if (this.tracer._exporter.addMetadataTags) {
333
- const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id)
334
- metadataTags.test = {
335
- ...metadataTags.test,
336
- ...libraryCapabilitiesTags,
337
- }
338
- this.tracer._exporter.addMetadataTags(metadataTags)
327
+ this.tracer._exporter.addMetadataTags({
328
+ '*': { [TEST_COMMAND]: testCommand, [TEST_SESSION_NAME]: testSessionName },
329
+ test: getLibraryCapabilitiesTags(this.constructor.id),
330
+ })
339
331
  }
340
332
 
341
333
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
@@ -1,8 +1,38 @@
1
1
  'use strict'
2
2
 
3
+ const { buildLogHolder, messageProxy } = require('../../dd-trace/src/plugins/log_injection')
3
4
  const LogPlugin = require('../../dd-trace/src/plugins/log_plugin')
4
5
 
5
6
  class WinstonPlugin extends LogPlugin {
6
7
  static id = 'winston'
8
+
9
+ constructor (...args) {
10
+ super(...args)
11
+ this.addSub('apm:winston:log', (arg) => this.handleLog(arg))
12
+ }
13
+
14
+ /**
15
+ * The prototype + extensibility check is load-bearing. The Proxy
16
+ * fallback keeps `dd` off caller-owned objects (Error, Set, Map, any
17
+ * user class) and out of non-extensible records, where a strict-mode
18
+ * write would throw and `Plugin.addSub` would react by disabling the
19
+ * plugin for the rest of the process.
20
+ *
21
+ * @param {{ message: unknown }} arg
22
+ */
23
+ handleLog (arg) {
24
+ const info = arg.message
25
+ if (info === null || typeof info !== 'object' || Object.hasOwn(info, 'dd')) return
26
+
27
+ const logHolder = buildLogHolder(this.tracer)
28
+ if (!logHolder) return
29
+
30
+ if (Object.getPrototypeOf(info) === Object.prototype && Object.isExtensible(info)) {
31
+ info.dd = logHolder.dd
32
+ } else {
33
+ arg.message = messageProxy(info, logHolder)
34
+ }
35
+ }
7
36
  }
37
+
8
38
  module.exports = WinstonPlugin
@@ -13,11 +13,16 @@ const skipMethodSize = skipMethods.size
13
13
 
14
14
  const nonConfigurableModuleExports = new WeakMap()
15
15
 
16
+ // Reused descriptor scratch space for the `name` and `length` slots that
17
+ // `copyProperties` and `wrapCallback` rewrite per wrap. `Object.defineProperty`
18
+ // reads the descriptor's slots synchronously and does not retain the object,
19
+ // so mutating `value` between calls is safe.
20
+ const lengthDescriptor = { value: 0, configurable: true }
21
+ const nameDescriptor = { value: '', configurable: true }
22
+
16
23
  /**
17
- * Copies properties from the original function to the wrapped function.
18
- *
19
- * @param {Function} original - The original function.
20
- * @param {Function} wrapped - The wrapped function.
24
+ * @param {Function} original
25
+ * @param {Function} wrapped
21
26
  */
22
27
  function copyProperties (original, wrapped) {
23
28
  if (original.constructor !== wrapped.constructor) {
@@ -26,11 +31,15 @@ function copyProperties (original, wrapped) {
26
31
  }
27
32
 
28
33
  const ownKeys = Reflect.ownKeys(original)
29
- if (original.length !== wrapped.length) {
30
- Object.defineProperty(wrapped, 'length', { value: original.length, configurable: true })
34
+ const originalLength = original.length
35
+ if (originalLength !== wrapped.length) {
36
+ lengthDescriptor.value = originalLength
37
+ Object.defineProperty(wrapped, 'length', lengthDescriptor)
31
38
  }
32
- if (original.name !== wrapped.name) {
33
- Object.defineProperty(wrapped, 'name', { value: original.name, configurable: true })
39
+ const originalName = original.name
40
+ if (originalName !== wrapped.name) {
41
+ nameDescriptor.value = originalName
42
+ Object.defineProperty(wrapped, 'name', nameDescriptor)
34
43
  }
35
44
  if (ownKeys.length !== 2) {
36
45
  for (const key of ownKeys) {
@@ -46,11 +55,9 @@ function copyProperties (original, wrapped) {
46
55
  }
47
56
 
48
57
  /**
49
- * Copies properties from the original object to the wrapped object, skipping a specific key.
50
- *
51
- * @param {Record<string | symbol, unknown>} original - The original object.
52
- * @param {Record<string | symbol, unknown>} wrapped - The wrapped object.
53
- * @param {string | symbol} skipKey - The key to skip during copying.
58
+ * @param {Record<string | symbol, unknown>} original
59
+ * @param {Record<string | symbol, unknown>} wrapped
60
+ * @param {string | symbol} skipKey
54
61
  */
55
62
  function copyObjectProperties (original, wrapped, skipKey) {
56
63
  const ownKeys = Reflect.ownKeys(original)
@@ -66,11 +73,8 @@ function copyObjectProperties (original, wrapped, skipKey) {
66
73
  }
67
74
 
68
75
  /**
69
- * Wraps a function with a wrapper function.
70
- *
71
- * @param {Function} original - The original function to wrap.
72
- * @param {(original: Function) => Function} wrapper - The wrapper function.
73
- * @returns {Function} The wrapped function.
76
+ * @param {Function} original
77
+ * @param {(original: Function) => Function} wrapper
74
78
  */
75
79
  function wrapFunction (original, wrapper) {
76
80
  if (typeof original !== 'function') return original
@@ -83,24 +87,14 @@ function wrapFunction (original, wrapper) {
83
87
  }
84
88
 
85
89
  /**
86
- * Lean variant of `wrapFunction` for the case where the wrapped value is a
87
- * user-supplied callback that the user cannot reasonably introspect beyond
88
- * `name` and `length`, and the wrapper closure is fully controlled by us.
89
- *
90
- * Compared to `wrapFunction`, this skips the prototype copy, the
91
- * `assertNotClass` guard, and the `Reflect.ownKeys` descriptor-copy loop.
92
- * Only `name` and `length` are preserved, and only when the wrapper's
93
- * autogenerated values differ -- a wrapper whose closure already has the
94
- * right arity / name pays no overhead.
95
- *
96
- * Use `wrapFunction` instead when any of the following is true: the wrapped
97
- * function needs to keep its prototype, has custom own properties the caller
98
- * may read, or is `new`-ed.
90
+ * Lean variant of `wrapFunction` for tracer-owned closures wrapping a
91
+ * user-supplied callback. Preserves `name` and `length` only; skips the
92
+ * prototype copy, `assertNotClass`, and the `Reflect.ownKeys` descriptor
93
+ * walk. Use `wrapFunction` instead when the wrapped value needs its
94
+ * prototype, has own properties the caller may read, or is `new`-ed.
99
95
  *
100
- * @param {Function} original - User-supplied callback being wrapped.
101
- * @param {(original: Function) => Function} wrapper - Factory that receives
102
- * `original` and returns the wrapper closure.
103
- * @returns {Function} The wrapper closure with `name` and `length` preserved.
96
+ * @param {Function} original
97
+ * @param {(original: Function) => Function} wrapper
104
98
  */
105
99
  function wrapCallback (original, wrapper) {
106
100
  if (typeof original !== 'function') {
@@ -108,10 +102,12 @@ function wrapCallback (original, wrapper) {
108
102
  }
109
103
  const wrapped = wrapper(original)
110
104
  if (wrapped.name !== original.name) {
111
- Object.defineProperty(wrapped, 'name', { value: original.name, configurable: true })
105
+ nameDescriptor.value = original.name
106
+ Object.defineProperty(wrapped, 'name', nameDescriptor)
112
107
  }
113
108
  if (wrapped.length !== original.length) {
114
- Object.defineProperty(wrapped, 'length', { value: original.length, configurable: true })
109
+ lengthDescriptor.value = original.length
110
+ Object.defineProperty(wrapped, 'length', lengthDescriptor)
115
111
  }
116
112
  return wrapped
117
113
  }
@@ -174,7 +170,6 @@ function wrap (target, name, wrapper, options) {
174
170
  copyProperties(original, wrapped)
175
171
 
176
172
  if (descriptor.writable) {
177
- // Fast path for assigned properties.
178
173
  if (descriptor.configurable && descriptor.enumerable) {
179
174
  target[name] = wrapped
180
175
  return target
@@ -209,8 +204,6 @@ function wrap (target, name, wrapper, options) {
209
204
  // with this code. That way it would be possible to directly pass through
210
205
  // the entries.
211
206
 
212
- // In case more than a single property is not configurable and writable,
213
- // Just reuse the already created object.
214
207
  let moduleExports = nonConfigurableModuleExports.get(target)
215
208
  if (!moduleExports) {
216
209
  if (typeof target === 'function') {
@@ -44,7 +44,7 @@ function disable () {
44
44
  /**
45
45
  * Handles channel messages with pre-converted messages.
46
46
  *
47
- * @param {{messages: Array<object>, resolve: Function, reject: Function}} ctx
47
+ * @param {{messages: Array<object>, integration?: string, resolve: Function, reject: Function}} ctx
48
48
  */
49
49
  function onEvaluate (ctx) {
50
50
  if (!ctx.messages?.length) {
@@ -148,7 +148,7 @@ class AIGuard extends NoopAIGuard {
148
148
  #setRootSpanClientIpTags (rootSpan) {
149
149
  if (!rootSpan) return
150
150
 
151
- const currentTags = rootSpan.context()._tags
151
+ const currentTags = rootSpan.context().getTags()
152
152
  const needsHttpClientIp = !Object.hasOwn(currentTags, HTTP_CLIENT_IP)
153
153
  const needsNetworkClientIp = !Object.hasOwn(currentTags, NETWORK_CLIENT_IP)
154
154
 
@@ -79,7 +79,7 @@ function getRouteOrEndpoint (context, statusCode) {
79
79
 
80
80
  // If route is not available, fallback to http.endpoint
81
81
  if (statusCode !== 404) {
82
- const endpoint = context?.span?.context()?._tags?.['http.endpoint']
82
+ const endpoint = context?.span?.context()?.getTag('http.endpoint')
83
83
  if (endpoint) {
84
84
  return endpoint
85
85
  }
@@ -289,7 +289,7 @@ function onExpressSession ({ req, res, sessionId, abortController }) {
289
289
  return
290
290
  }
291
291
 
292
- const isSdkCalled = rootSpan.context()._tags['usr.session_id']
292
+ const isSdkCalled = rootSpan.context().getTag('usr.session_id')
293
293
  if (isSdkCalled) return
294
294
 
295
295
  const results = waf.run({
@@ -361,18 +361,18 @@ function reportAttack ({ events: attackData, actions }, req) {
361
361
  const rootSpan = web.root(req)
362
362
  if (!rootSpan) return
363
363
 
364
- const currentTags = rootSpan.context()._tags
364
+ const spanContext = rootSpan.context()
365
365
 
366
366
  const newTags = {
367
367
  'appsec.event': 'true',
368
368
  }
369
369
 
370
370
  // TODO: maybe add this to format.js later (to take decision as late as possible)
371
- if (!currentTags['_dd.origin']) {
371
+ if (!spanContext.getTag('_dd.origin')) {
372
372
  newTags['_dd.origin'] = 'appsec'
373
373
  }
374
374
 
375
- const currentJson = currentTags['_dd.appsec.json']
375
+ const currentJson = spanContext.getTag('_dd.appsec.json')
376
376
 
377
377
  // merge JSON arrays without parsing them
378
378
  const attackDataStr = JSON.stringify(attackData)
@@ -469,8 +469,7 @@ function reportRequestBody (rootSpan, requestBody, comesFromRaspAction = false)
469
469
 
470
470
  if (rootSpan.meta_struct['http.request.body']) {
471
471
  // If the rasp.exceed metric exists, set also the same for the new tag
472
- const currentTags = rootSpan.context()._tags
473
- const sizeExceedTagValue = currentTags['_dd.appsec.rasp.request_body_size.exceeded']
472
+ const sizeExceedTagValue = rootSpan.context().getTag('_dd.appsec.rasp.request_body_size.exceeded')
474
473
 
475
474
  if (sizeExceedTagValue) {
476
475
  rootSpan.setTag('_dd.appsec.request_body_size.exceeded', sizeExceedTagValue)
@@ -572,7 +571,7 @@ function finishRequest (req, res, storedResponseHeaders, requestBody) {
572
571
 
573
572
  incrementWafRequestsMetric(req)
574
573
 
575
- const tags = rootSpan.context()._tags
574
+ const tags = rootSpan.context().getTags()
576
575
 
577
576
  const extendedDataCollection = extendedDataCollectionRequest.get(req)
578
577
  const newTags = getCollectedHeaders(
@@ -22,7 +22,7 @@ function checkUserAndSetUser (tracer, user) {
22
22
 
23
23
  const rootSpan = getRootSpan()
24
24
  if (rootSpan) {
25
- if (!rootSpan.context()._tags['usr.id']) {
25
+ if (!rootSpan.context().getTag('usr.id')) {
26
26
  setUserTags(user, rootSpan)
27
27
  }
28
28
  } else {
@@ -18,7 +18,7 @@ function getRootSpan () {
18
18
 
19
19
  parentId = pContext._parentId
20
20
 
21
- if (!pContext._tags?._inferred_span) {
21
+ if (!pContext.getTag('_inferred_span')) {
22
22
  span = parent
23
23
  }
24
24
  }
@@ -91,12 +91,13 @@ function trackLogin (framework, login, user, success, rootSpan) {
91
91
  [addresses.USER_LOGIN]: login,
92
92
  }
93
93
 
94
- const currentTags = rootSpan.context()._tags
95
- const isSdkCalled = currentTags[`_dd.appsec.events.users.login.${success ? 'success' : 'failure'}.sdk`] === 'true'
94
+ const spanContext = rootSpan.context()
95
+ const sdkTag = `_dd.appsec.events.users.login.${success ? 'success' : 'failure'}.sdk`
96
+ const isSdkCalled = spanContext.getTag(sdkTag) === 'true'
96
97
 
97
98
  // used to not overwrite tags set by SDK
98
99
  function shouldSetTag (tag) {
99
- return !(isSdkCalled && currentTags[tag])
100
+ return !(isSdkCalled && spanContext.getTag(tag))
100
101
  }
101
102
 
102
103
  if (success) {
@@ -167,7 +168,7 @@ function trackUser (user, rootSpan) {
167
168
 
168
169
  rootSpan.setTag('_dd.appsec.usr.id', userId)
169
170
 
170
- const isSdkCalled = rootSpan.context()._tags['_dd.appsec.user.collection_mode'] === 'sdk'
171
+ const isSdkCalled = rootSpan.context().getTag('_dd.appsec.user.collection_mode') === 'sdk'
171
172
  // do not override SDK
172
173
  if (!isSdkCalled) {
173
174
  rootSpan.addTags({
@@ -62,7 +62,13 @@ function removeBaggageItem (keyToRemove) {
62
62
  }
63
63
 
64
64
  function removeAllBaggageItems () {
65
- baggageStorage.enterWith(EMPTY_STORE)
65
+ // Skip `enterWith` (a real ALS frame switch) when the store is already
66
+ // the empty sentinel. Entry-point services without active baggage hit this
67
+ // on every extract.
68
+ const store = baggageStorage.getStore()
69
+ if (store !== undefined && store !== EMPTY_STORE) {
70
+ baggageStorage.enterWith(EMPTY_STORE)
71
+ }
66
72
  return EMPTY_STORE
67
73
  }
68
74
 
@@ -236,7 +236,6 @@ class CiVisibilityExporter extends BufferingExporter {
236
236
  testManagementAttemptToFixRetries ?? this._config.testManagementAttemptToFixRetries,
237
237
  isImpactedTestsEnabled: isImpactedTestsEnabled && this._config.isImpactedTestsEnabled,
238
238
  isCoverageReportUploadEnabled,
239
- DD_TEST_TIA_KEEP_COV_CONFIG: this._config.DD_TEST_TIA_KEEP_COV_CONFIG,
240
239
  }
241
240
  }
242
241
 
@@ -31,10 +31,11 @@ function getSkippableSuites ({
31
31
  runtimeVersion,
32
32
  custom,
33
33
  testLevel = 'suite',
34
+ isCoverageReportUploadEnabled = false,
34
35
  }, done) {
35
36
  const cacheKey = buildCacheKey('skippable', [
36
37
  sha, service, env, repositoryUrl, osPlatform, osVersion, osArchitecture,
37
- runtimeName, runtimeVersion, testLevel, custom,
38
+ runtimeName, runtimeVersion, testLevel, custom, isCoverageReportUploadEnabled,
38
39
  ])
39
40
 
40
41
  withCache(cacheKey, (activeCacheKey, cb) => {
@@ -54,11 +55,12 @@ function getSkippableSuites ({
54
55
  runtimeVersion,
55
56
  custom,
56
57
  testLevel,
58
+ isCoverageReportUploadEnabled,
57
59
  cacheKey: activeCacheKey,
58
60
  }, cb)
59
61
  }, (err, data) => {
60
62
  if (err) return done(err)
61
- done(null, data.skippableSuites, data.correlationId)
63
+ done(null, data.skippableSuites, data.correlationId, data.coverage)
62
64
  })
63
65
  }
64
66
 
@@ -81,6 +83,7 @@ function getSkippableSuites ({
81
83
  * @param {string} params.runtimeVersion
82
84
  * @param {object} [params.custom]
83
85
  * @param {string} [params.testLevel]
86
+ * @param {boolean} [params.isCoverageReportUploadEnabled]
84
87
  * @param {string | null} params.cacheKey
85
88
  * @param {Function} done
86
89
  */
@@ -100,6 +103,7 @@ function fetchFromApi ({
100
103
  runtimeVersion,
101
104
  custom,
102
105
  testLevel,
106
+ isCoverageReportUploadEnabled,
103
107
  cacheKey,
104
108
  }, done) {
105
109
  const options = {
@@ -148,7 +152,6 @@ function fetchFromApi ({
148
152
  },
149
153
  },
150
154
  })
151
-
152
155
  incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS)
153
156
 
154
157
  const startTime = Date.now()
@@ -161,27 +164,36 @@ function fetchFromApi ({
161
164
  } else {
162
165
  try {
163
166
  const parsedResponse = JSON.parse(res)
164
- const skippableSuites = parsedResponse
167
+ const coverage = parsedResponse.meta?.coverage || {}
168
+
169
+ const skippableItems = parsedResponse
165
170
  .data
166
171
  .filter(({ type }) => type === testLevel)
167
- .map(({ attributes: { suite, name } }) => {
168
- if (testLevel === 'suite') {
169
- return suite
170
- }
171
- return { suite, name }
172
- })
173
- const { meta: { correlation_id: correlationId } } = parsedResponse
172
+ const skippableSuites = []
173
+ for (const {
174
+ attributes: {
175
+ suite,
176
+ name,
177
+ _is_missing_line_code_coverage: isMissingLineCodeCoverage,
178
+ },
179
+ } of skippableItems) {
180
+ // Only reject candidates without backend line coverage when we need that coverage to backfill reports.
181
+ if (isCoverageReportUploadEnabled && isMissingLineCodeCoverage) continue
182
+
183
+ skippableSuites.push(testLevel === 'suite' ? suite : { suite, name })
184
+ }
185
+ const correlationId = parsedResponse.meta?.correlation_id
174
186
  incrementCountMetric(
175
187
  testLevel === 'test'
176
188
  ? TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS
177
189
  : TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
178
190
  {},
179
- skippableSuites.length
191
+ skippableItems.length
180
192
  )
181
193
  distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
182
194
  log.debug('Number of received skippable %ss:', testLevel, skippableSuites.length)
183
195
 
184
- const result = { skippableSuites, correlationId }
196
+ const result = { skippableSuites, correlationId, coverage }
185
197
  writeToCache(cacheKey, result)
186
198
 
187
199
  done(null, result)