dd-trace 5.101.0 → 5.103.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 (235) hide show
  1. package/ext/exporters.js +1 -0
  2. package/package.json +20 -17
  3. package/packages/datadog-esbuild/src/utils.js +2 -2
  4. package/packages/datadog-instrumentations/src/aerospike.js +2 -2
  5. package/packages/datadog-instrumentations/src/ai.js +9 -9
  6. package/packages/datadog-instrumentations/src/amqplib.js +6 -7
  7. package/packages/datadog-instrumentations/src/anthropic.js +10 -10
  8. package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
  9. package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
  10. package/packages/datadog-instrumentations/src/avsc.js +6 -6
  11. package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
  12. package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
  13. package/packages/datadog-instrumentations/src/bluebird.js +2 -2
  14. package/packages/datadog-instrumentations/src/body-parser.js +2 -2
  15. package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
  16. package/packages/datadog-instrumentations/src/child_process.js +12 -12
  17. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +41 -24
  18. package/packages/datadog-instrumentations/src/connect.js +7 -7
  19. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  20. package/packages/datadog-instrumentations/src/cookie.js +2 -2
  21. package/packages/datadog-instrumentations/src/couchbase.js +73 -238
  22. package/packages/datadog-instrumentations/src/crypto.js +4 -4
  23. package/packages/datadog-instrumentations/src/cucumber.js +78 -17
  24. package/packages/datadog-instrumentations/src/dns.js +0 -3
  25. package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
  26. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  27. package/packages/datadog-instrumentations/src/electron.js +240 -0
  28. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
  29. package/packages/datadog-instrumentations/src/express-session.js +4 -4
  30. package/packages/datadog-instrumentations/src/express.js +10 -11
  31. package/packages/datadog-instrumentations/src/fastify.js +2 -2
  32. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  33. package/packages/datadog-instrumentations/src/fs.js +14 -14
  34. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
  35. package/packages/datadog-instrumentations/src/google-genai.js +4 -4
  36. package/packages/datadog-instrumentations/src/graphql.js +13 -12
  37. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  38. package/packages/datadog-instrumentations/src/hapi.js +2 -2
  39. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +9 -9
  40. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  41. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  42. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  43. package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
  44. package/packages/datadog-instrumentations/src/helpers/promise.js +2 -2
  45. package/packages/datadog-instrumentations/src/hono.js +2 -2
  46. package/packages/datadog-instrumentations/src/http/client.js +6 -6
  47. package/packages/datadog-instrumentations/src/http/server.js +9 -9
  48. package/packages/datadog-instrumentations/src/ioredis.js +16 -12
  49. package/packages/datadog-instrumentations/src/jest.js +382 -81
  50. package/packages/datadog-instrumentations/src/kafkajs.js +165 -174
  51. package/packages/datadog-instrumentations/src/knex.js +17 -17
  52. package/packages/datadog-instrumentations/src/koa.js +12 -12
  53. package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
  54. package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
  55. package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
  56. package/packages/datadog-instrumentations/src/lodash.js +4 -4
  57. package/packages/datadog-instrumentations/src/mariadb.js +13 -13
  58. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  59. package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
  60. package/packages/datadog-instrumentations/src/mocha/common.js +3 -3
  61. package/packages/datadog-instrumentations/src/mocha/main.js +85 -11
  62. package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
  63. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
  64. package/packages/datadog-instrumentations/src/mongodb-core.js +42 -30
  65. package/packages/datadog-instrumentations/src/mongodb.js +5 -5
  66. package/packages/datadog-instrumentations/src/mongoose.js +21 -21
  67. package/packages/datadog-instrumentations/src/mquery.js +5 -5
  68. package/packages/datadog-instrumentations/src/multer.js +4 -4
  69. package/packages/datadog-instrumentations/src/mysql.js +16 -16
  70. package/packages/datadog-instrumentations/src/mysql2.js +4 -4
  71. package/packages/datadog-instrumentations/src/net.js +14 -8
  72. package/packages/datadog-instrumentations/src/nyc.js +5 -5
  73. package/packages/datadog-instrumentations/src/openai.js +19 -19
  74. package/packages/datadog-instrumentations/src/oracledb.js +6 -6
  75. package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
  76. package/packages/datadog-instrumentations/src/pg.js +39 -25
  77. package/packages/datadog-instrumentations/src/pino.js +6 -10
  78. package/packages/datadog-instrumentations/src/playwright.js +445 -68
  79. package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
  80. package/packages/datadog-instrumentations/src/redis.js +20 -12
  81. package/packages/datadog-instrumentations/src/restify.js +2 -2
  82. package/packages/datadog-instrumentations/src/router.js +12 -12
  83. package/packages/datadog-instrumentations/src/stripe.js +12 -12
  84. package/packages/datadog-instrumentations/src/vitest.js +107 -26
  85. package/packages/datadog-instrumentations/src/winston.js +4 -4
  86. package/packages/datadog-instrumentations/src/ws.js +7 -7
  87. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  88. package/packages/datadog-plugin-aws-sdk/src/base.js +70 -28
  89. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  90. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +20 -13
  91. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +46 -36
  92. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +34 -23
  93. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  94. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  95. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +14 -15
  96. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +74 -55
  97. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +20 -18
  98. package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
  99. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
  100. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  101. package/packages/datadog-plugin-cucumber/src/index.js +5 -0
  102. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +215 -26
  103. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  104. package/packages/datadog-plugin-electron/src/index.js +17 -0
  105. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  106. package/packages/datadog-plugin-electron/src/net.js +82 -0
  107. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -5
  108. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  109. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
  110. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  111. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  112. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  113. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  114. package/packages/datadog-plugin-graphql/src/utils.js +29 -0
  115. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  116. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  117. package/packages/datadog-plugin-http/src/client.js +3 -7
  118. package/packages/datadog-plugin-jest/src/index.js +92 -50
  119. package/packages/datadog-plugin-jest/src/util.js +1 -2
  120. package/packages/datadog-plugin-mocha/src/index.js +5 -0
  121. package/packages/datadog-plugin-mongodb-core/src/index.js +36 -69
  122. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  123. package/packages/datadog-plugin-openai/src/services.js +2 -1
  124. package/packages/datadog-plugin-openai/src/tracing.js +12 -23
  125. package/packages/datadog-plugin-pg/src/index.js +3 -3
  126. package/packages/datadog-plugin-playwright/src/index.js +5 -1
  127. package/packages/datadog-plugin-redis/src/index.js +18 -23
  128. package/packages/datadog-plugin-vitest/src/index.js +8 -1
  129. package/packages/datadog-shimmer/src/shimmer.js +7 -1
  130. package/packages/dd-trace/src/aiguard/index.js +3 -1
  131. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  132. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  133. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
  134. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
  135. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  136. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
  137. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +4 -4
  138. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
  139. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
  140. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
  141. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
  142. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  143. package/packages/dd-trace/src/appsec/index.js +21 -24
  144. package/packages/dd-trace/src/appsec/reporter.js +3 -1
  145. package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
  146. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
  147. package/packages/dd-trace/src/azure_metadata.js +17 -6
  148. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  149. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  150. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  151. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  152. package/packages/dd-trace/src/config/defaults.js +3 -14
  153. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
  154. package/packages/dd-trace/src/config/git_properties.js +2 -2
  155. package/packages/dd-trace/src/config/helper.js +4 -0
  156. package/packages/dd-trace/src/config/index.js +2 -2
  157. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  158. package/packages/dd-trace/src/config/parsers.js +7 -1
  159. package/packages/dd-trace/src/config/supported-configurations.json +51 -38
  160. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  161. package/packages/dd-trace/src/datastreams/index.js +2 -1
  162. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  163. package/packages/dd-trace/src/datastreams/processor.js +3 -4
  164. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  165. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
  166. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  167. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  168. package/packages/dd-trace/src/debugger/index.js +7 -7
  169. package/packages/dd-trace/src/dogstatsd.js +2 -2
  170. package/packages/dd-trace/src/encode/0.4.js +748 -232
  171. package/packages/dd-trace/src/encode/0.5.js +47 -10
  172. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  173. package/packages/dd-trace/src/exporter.js +2 -0
  174. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  175. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  176. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  177. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  178. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  179. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  180. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  181. package/packages/dd-trace/src/git_metadata.js +10 -8
  182. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  183. package/packages/dd-trace/src/lambda/index.js +62 -14
  184. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  185. package/packages/dd-trace/src/llmobs/index.js +13 -2
  186. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
  187. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  188. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +6 -3
  189. package/packages/dd-trace/src/llmobs/sdk.js +24 -26
  190. package/packages/dd-trace/src/llmobs/span_processor.js +25 -5
  191. package/packages/dd-trace/src/llmobs/util.js +1 -0
  192. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  193. package/packages/dd-trace/src/msgpack/chunk.js +6 -3
  194. package/packages/dd-trace/src/openfeature/noop.js +40 -36
  195. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  196. package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
  197. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
  198. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
  199. package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
  200. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  201. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
  202. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  203. package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
  204. package/packages/dd-trace/src/plugins/database.js +54 -12
  205. package/packages/dd-trace/src/plugins/index.js +1 -0
  206. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  207. package/packages/dd-trace/src/plugins/util/ci.js +9 -9
  208. package/packages/dd-trace/src/plugins/util/git-cache.js +23 -23
  209. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  210. package/packages/dd-trace/src/plugins/util/test.js +56 -12
  211. package/packages/dd-trace/src/plugins/util/url.js +1 -3
  212. package/packages/dd-trace/src/plugins/util/user-provided-git.js +18 -16
  213. package/packages/dd-trace/src/plugins/util/web.js +5 -7
  214. package/packages/dd-trace/src/priority_sampler.js +1 -1
  215. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  216. package/packages/dd-trace/src/profiling/profilers/events.js +3 -23
  217. package/packages/dd-trace/src/profiling/profilers/wall.js +5 -6
  218. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  219. package/packages/dd-trace/src/rate_limiter.js +1 -1
  220. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  221. package/packages/dd-trace/src/ritm.js +2 -1
  222. package/packages/dd-trace/src/runtime_metrics/index.js +2 -2
  223. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  224. package/packages/dd-trace/src/scope.js +3 -10
  225. package/packages/dd-trace/src/serverless.js +6 -6
  226. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +27 -1
  227. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  228. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +24 -0
  229. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  230. package/packages/dd-trace/src/span_stats.js +1 -1
  231. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  232. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  233. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  234. package/packages/dd-trace/src/tracer.js +7 -7
  235. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
@@ -44,6 +44,35 @@ function extractErrorIntoSpanEvent (config, span, exc) {
44
44
  span.addEvent('dd.graphql.query.error', attributes, Date.now())
45
45
  }
46
46
 
47
+ let tools
48
+
49
+ function getSignature (document, operationName, operationType, calculate) {
50
+ if (calculate !== false && tools !== false) {
51
+ try {
52
+ try {
53
+ tools ||= require('./tools')
54
+ } catch (e) {
55
+ tools = false
56
+ throw e
57
+ }
58
+
59
+ return tools.defaultEngineReportingSignature(document, operationName)
60
+ } catch {
61
+ // safety net
62
+ }
63
+ }
64
+
65
+ if (operationType) {
66
+ if (operationName) {
67
+ return `${operationType} ${operationName}`
68
+ }
69
+ return operationType
70
+ }
71
+
72
+ return operationName ?? ''
73
+ }
74
+
47
75
  module.exports = {
48
76
  extractErrorIntoSpanEvent,
77
+ getSignature,
49
78
  }
@@ -86,12 +86,11 @@ class GrpcClientPlugin extends ClientPlugin {
86
86
  // The only scheme we want to support here is ipv[46]:port, although
87
87
  // more are supported by the library
88
88
  // https://github.com/grpc/grpc/blob/v1.60.0/doc/naming.md
89
- const parts = peer.split(':')
90
- if (/^\d+/.test(parts.at(-1))) {
91
- const port = parts.at(-1)
92
- const ip = parts.slice(0, -1).join(':')
93
- span.setTag('network.destination.ip', ip)
94
- span.setTag('network.destination.port', port)
89
+ const colonIndex = peer.lastIndexOf(':')
90
+ const tail = colonIndex === -1 ? '' : peer.slice(colonIndex + 1)
91
+ if (tail && /^\d+$/.test(tail)) {
92
+ span.setTag('network.destination.ip', peer.slice(0, colonIndex))
93
+ span.setTag('network.destination.port', tail)
95
94
  } else {
96
95
  span.setTag('network.destination.ip', peer)
97
96
  }
@@ -121,7 +120,7 @@ function inject (tracer, span, metadata) {
121
120
 
122
121
  tracer.inject(span, TEXT_MAP, carrier)
123
122
 
124
- for (const key in carrier) {
123
+ for (const key of Object.keys(carrier)) {
125
124
  metadata.set(key, carrier[key])
126
125
  }
127
126
  }
@@ -3,46 +3,81 @@
3
3
  const pick = require('../../datadog-core/src/utils/src/pick')
4
4
  const log = require('../../dd-trace/src/log')
5
5
 
6
+ /**
7
+ * @typedef {object} ParsedMethodPath
8
+ * @property {string} name
9
+ * @property {string} service
10
+ * @property {string} package
11
+ */
12
+
13
+ // Sentinel returned by `getFilter` when the user has not configured a metadata
14
+ // filter. `addMetadataTags` short-circuits on this identity to skip the
15
+ // `metadata.getMap()` clone in the default no-filter case.
6
16
  function getEmptyObject () {
7
17
  return {}
8
18
  }
9
19
 
10
- module.exports = {
11
- getMethodMetadata (path, kind) {
12
- const tags = {
13
- path,
14
- kind,
15
- name: '',
16
- service: '',
17
- package: '',
20
+ /**
21
+ * gRPC method paths are stable per service definition (e.g.
22
+ * `/pkg.Service/Method`); a service typically only has a small finite set.
23
+ * Cache the parsed `{name, service, package}` triple by path so we skip the
24
+ * `path.split('/')` + `serviceParts.split('.')` + `serviceParts.pop()` work
25
+ * on every call.
26
+ *
27
+ * @type {Map<string, ParsedMethodPath>}
28
+ */
29
+ const methodPathCache = new Map()
30
+
31
+ /**
32
+ * @param {string} path
33
+ * @returns {ParsedMethodPath}
34
+ */
35
+ function parseMethodPath (path) {
36
+ const methodParts = path.split('/')
37
+
38
+ if (methodParts.length > 2) {
39
+ const serviceParts = methodParts[1].split('.')
40
+ return {
41
+ name: methodParts[2],
42
+ service: serviceParts.pop(),
43
+ package: serviceParts.join('.'),
18
44
  }
45
+ }
19
46
 
20
- if (typeof path !== 'string') return tags
47
+ return { name: methodParts.at(-1), service: '', package: '' }
48
+ }
21
49
 
22
- const methodParts = path.split('/')
50
+ module.exports = {
51
+ getEmptyObject,
23
52
 
24
- if (methodParts.length > 2) {
25
- const serviceParts = methodParts[1].split('.')
26
- const name = methodParts[2]
27
- const service = serviceParts.pop()
28
- const pkg = serviceParts.join('.')
53
+ getMethodMetadata (path, kind) {
54
+ if (typeof path !== 'string') {
55
+ return { path, kind, name: '', service: '', package: '' }
56
+ }
29
57
 
30
- tags.name = name
31
- tags.service = service
32
- tags.package = pkg
33
- } else {
34
- tags.name = methodParts.at(-1)
58
+ let parsed = methodPathCache.get(path)
59
+ if (parsed === undefined) {
60
+ parsed = parseMethodPath(path)
61
+ methodPathCache.set(path, parsed)
35
62
  }
36
63
 
37
- return tags
64
+ return {
65
+ path,
66
+ kind,
67
+ name: parsed.name,
68
+ service: parsed.service,
69
+ package: parsed.package,
70
+ }
38
71
  },
39
72
 
40
73
  addMetadataTags (span, metadata, filter, type) {
41
74
  if (!metadata || typeof metadata.getMap !== 'function') return
75
+ // Default no-op filter: skip the full metadata clone via `getMap()`.
76
+ if (filter === getEmptyObject) return
42
77
 
43
78
  const values = filter(metadata.getMap())
44
79
 
45
- for (const key in values) {
80
+ for (const key of Object.keys(values)) {
46
81
  span.setTag(`grpc.${type}.metadata.${key}`, values[key])
47
82
  }
48
83
  },
@@ -39,10 +39,10 @@ class HttpClientPlugin extends ClientPlugin {
39
39
  // TODO delegate to super.startspan
40
40
  const span = this.startSpan(this.operationName(), {
41
41
  childOf,
42
- integrationName: this.constructor.id,
42
+ integrationName: this.component,
43
43
  service: this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
44
44
  meta: {
45
- [COMPONENT]: this.constructor.id,
45
+ [COMPONENT]: this.component,
46
46
  'span.kind': 'client',
47
47
  'resource.name': method,
48
48
  'span.type': 'http',
@@ -79,11 +79,7 @@ class HttpClientPlugin extends ClientPlugin {
79
79
  }
80
80
 
81
81
  shouldInjectTraceHeaders (options, uri) {
82
- if (!this.config.propagationFilter(uri)) {
83
- return false
84
- }
85
-
86
- return true
82
+ return Boolean(this.config.propagationFilter(uri))
87
83
  }
88
84
 
89
85
  bindAsyncStart ({ parentStore }) {
@@ -21,6 +21,7 @@ const {
21
21
  TEST_SOURCE_START,
22
22
  TEST_ITR_UNSKIPPABLE,
23
23
  TEST_ITR_FORCED_RUN,
24
+ TEST_ITR_SKIPPING_ENABLED,
24
25
  TEST_CODE_OWNERS,
25
26
  ITR_CORRELATION_ID,
26
27
  TEST_SOURCE_FILE,
@@ -107,6 +108,7 @@ class JestPlugin extends CiPlugin {
107
108
  process.on('message', handler)
108
109
  }
109
110
  this.testSuiteSpanPerTestSuiteAbsolutePath = new Map()
111
+ this.pendingTestSuiteFinishes = new Set()
110
112
 
111
113
  this.addSub('ci:jest:session:finish', ({
112
114
  status,
@@ -123,58 +125,66 @@ class JestPlugin extends CiPlugin {
123
125
  isTestManagementTestsEnabled,
124
126
  onDone,
125
127
  }) => {
126
- this.testSessionSpan.setTag(TEST_STATUS, status)
127
- this.testModuleSpan.setTag(TEST_STATUS, status)
128
+ const finishSession = () => {
129
+ this.testSessionSpan.setTag(TEST_STATUS, status)
130
+ this.testModuleSpan.setTag(TEST_STATUS, status)
128
131
 
129
- if (error) {
130
- this.testSessionSpan.setTag('error', error)
131
- this.testModuleSpan.setTag('error', error)
132
- }
133
-
134
- addIntelligentTestRunnerSpanTags(
135
- this.testSessionSpan,
136
- this.testModuleSpan,
137
- {
138
- isSuitesSkipped,
139
- isSuitesSkippingEnabled,
140
- isCodeCoverageEnabled,
141
- testCodeCoverageLinesTotal,
142
- skippingType: 'suite',
143
- skippingCount: numSkippedSuites,
144
- hasUnskippableSuites,
145
- hasForcedToRunSuites,
132
+ if (error) {
133
+ this.testSessionSpan.setTag('error', error)
134
+ this.testModuleSpan.setTag('error', error)
146
135
  }
147
- )
148
136
 
149
- if (isEarlyFlakeDetectionEnabled) {
150
- this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
151
- }
152
- if (isEarlyFlakeDetectionFaulty) {
153
- this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
154
- }
155
- if (isTestManagementTestsEnabled) {
156
- this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
157
- }
137
+ addIntelligentTestRunnerSpanTags(
138
+ this.testSessionSpan,
139
+ this.testModuleSpan,
140
+ {
141
+ isSuitesSkipped,
142
+ isSuitesSkippingEnabled,
143
+ isCodeCoverageEnabled,
144
+ testCodeCoverageLinesTotal,
145
+ skippingType: 'suite',
146
+ skippingCount: numSkippedSuites,
147
+ hasUnskippableSuites,
148
+ hasForcedToRunSuites,
149
+ }
150
+ )
158
151
 
159
- this.testModuleSpan.finish()
160
- this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
161
- this.testSessionSpan.finish()
162
- this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
163
- hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
164
- })
165
- finishAllTraceSpans(this.testSessionSpan)
152
+ if (isEarlyFlakeDetectionEnabled) {
153
+ this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
154
+ }
155
+ if (isEarlyFlakeDetectionFaulty) {
156
+ this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
157
+ }
158
+ if (isTestManagementTestsEnabled) {
159
+ this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
160
+ }
166
161
 
167
- this.telemetry.count(TELEMETRY_TEST_SESSION, {
168
- provider: this.ciProviderName,
169
- autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
170
- })
162
+ this.testModuleSpan.finish()
163
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
164
+ this.testSessionSpan.finish()
165
+ this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
166
+ hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
167
+ })
168
+ finishAllTraceSpans(this.testSessionSpan)
169
+
170
+ this.telemetry.count(TELEMETRY_TEST_SESSION, {
171
+ provider: this.ciProviderName,
172
+ autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
173
+ })
174
+
175
+ appClosingTelemetry()
176
+ this.tracer._exporter.flush(() => {
177
+ if (onDone) {
178
+ onDone()
179
+ }
180
+ })
181
+ }
171
182
 
172
- appClosingTelemetry()
173
- this.tracer._exporter.flush(() => {
174
- if (onDone) {
175
- onDone()
176
- }
177
- })
183
+ if (this.pendingTestSuiteFinishes.size > 0) {
184
+ Promise.all(this.pendingTestSuiteFinishes).then(finishSession)
185
+ } else {
186
+ finishSession()
187
+ }
178
188
  })
179
189
 
180
190
  // Test suites can be run in a different process from jest's main one.
@@ -197,6 +207,7 @@ class JestPlugin extends CiPlugin {
197
207
  config._ddIsDiEnabled = this.libraryConfig?.isDiEnabled ?? false
198
208
  config._ddIsKnownTestsEnabled = this.libraryConfig?.isKnownTestsEnabled ?? false
199
209
  config._ddIsImpactedTestsEnabled = this.libraryConfig?.isImpactedTestsEnabled ?? false
210
+ config._ddItrSkippingEnabledTags = this.getSessionItrSkippingEnabledTags()
200
211
  }
201
212
  })
202
213
 
@@ -217,6 +228,7 @@ class JestPlugin extends CiPlugin {
217
228
  _ddForcedToRun,
218
229
  _ddUnskippable,
219
230
  _ddTestCodeCoverageEnabled,
231
+ _ddItrSkippingEnabledTags: itrSkippingEnabledTags,
220
232
  } = testEnvironmentOptions
221
233
 
222
234
  const testSessionSpanContext = this.tracer.extract('text_map', {
@@ -228,6 +240,7 @@ class JestPlugin extends CiPlugin {
228
240
  ...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, 'jest'),
229
241
  // requestErrorTags from test env options may be undefined
230
242
  ...(requestErrorTags !== undefined && requestErrorTags !== null ? requestErrorTags : {}),
243
+ ...(itrSkippingEnabledTags !== undefined && itrSkippingEnabledTags !== null ? itrSkippingEnabledTags : {}),
231
244
  }
232
245
 
233
246
  if (_ddUnskippable) {
@@ -305,10 +318,18 @@ class JestPlugin extends CiPlugin {
305
318
  }
306
319
  })
307
320
 
308
- this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error, testSuiteAbsolutePath }) => {
321
+ this.addSub('ci:jest:test-suite:finish', ({
322
+ status,
323
+ errorMessage,
324
+ error,
325
+ testSuiteAbsolutePath,
326
+ waitForFinish,
327
+ onDone,
328
+ }) => {
309
329
  const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath)
310
330
  if (!testSuiteSpan) {
311
331
  log.warn('"ci:jest:test-suite:finish": no span found for test suite absolute path %s', testSuiteAbsolutePath)
332
+ onDone?.()
312
333
  return
313
334
  }
314
335
  testSuiteSpan.setTag(TEST_STATUS, status)
@@ -319,8 +340,13 @@ class JestPlugin extends CiPlugin {
319
340
  testSuiteSpan.setTag('error', new Error(errorMessage))
320
341
  testSuiteSpan.setTag(TEST_STATUS, 'fail')
321
342
  }
322
- // We need to give the potential error in 'ci:jest:test-suite:error' time to be published
323
- process.nextTick(() => {
343
+ let resolvePendingFinish
344
+ const pendingFinish = new Promise(resolve => {
345
+ resolvePendingFinish = resolve
346
+ })
347
+ this.pendingTestSuiteFinishes.add(pendingFinish)
348
+
349
+ const finish = () => {
324
350
  testSuiteSpan.finish()
325
351
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
326
352
  // Suites potentially run in a different process than the session,
@@ -334,7 +360,19 @@ class JestPlugin extends CiPlugin {
334
360
  }
335
361
  this.removeAllDiProbes()
336
362
  this.testSuiteSpanPerTestSuiteAbsolutePath.delete(testSuiteAbsolutePath)
337
- })
363
+ this.pendingTestSuiteFinishes.delete(pendingFinish)
364
+ resolvePendingFinish()
365
+ if (onDone) {
366
+ onDone()
367
+ }
368
+ }
369
+
370
+ if (waitForFinish) {
371
+ // Give late async work time to run before Jest restarts a worker because of workerIdleMemoryLimit.
372
+ realSetTimeout(finish)
373
+ } else {
374
+ process.nextTick(finish)
375
+ }
338
376
  })
339
377
 
340
378
  this.addSub('ci:jest:test-suite:error', ({ error, errorMessage, testSuiteAbsolutePath }) => {
@@ -558,6 +596,10 @@ class JestPlugin extends CiPlugin {
558
596
  extraTags[TEST_HAS_DYNAMIC_NAME] = 'true'
559
597
  }
560
598
  const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath) || this.testSuiteSpan
599
+ const skippingEnabled = testSuiteSpan?.context()._tags?.[TEST_ITR_SKIPPING_ENABLED]
600
+ if (skippingEnabled !== undefined) {
601
+ extraTags[TEST_ITR_SKIPPING_ENABLED] = skippingEnabled
602
+ }
561
603
 
562
604
  return super.startTestSpan(name, suite, testSuiteSpan, extraTags)
563
605
  }
@@ -3,7 +3,7 @@
3
3
  const { readFileSync } = require('fs')
4
4
  const { parse } = require('../../../vendor/dist/jest-docblock')
5
5
 
6
- const { getTestSuitePath, getEfdRetryCount } = require('../../dd-trace/src/plugins/util/test')
6
+ const { getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
7
7
  const log = require('../../dd-trace/src/log')
8
8
 
9
9
  /**
@@ -172,5 +172,4 @@ module.exports = {
172
172
  getJestTestName,
173
173
  getJestSuitesToRun,
174
174
  isMarkedAsUnskippable,
175
- getEfdRetryCount,
176
175
  }
@@ -105,6 +105,7 @@ class MochaPlugin extends CiPlugin {
105
105
  'mocha'
106
106
  ),
107
107
  ...this.getSessionRequestErrorTags(),
108
+ ...this.getSessionItrSkippingEnabledTags(),
108
109
  }
109
110
  if (isUnskippable) {
110
111
  testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
@@ -218,12 +219,16 @@ class MochaPlugin extends CiPlugin {
218
219
  isAttemptToFixRetry,
219
220
  isAtrRetry,
220
221
  finalStatus,
222
+ earlyFlakeAbortReason,
221
223
  }) => {
222
224
  if (span) {
223
225
  span.setTag(TEST_STATUS, status)
224
226
  if (finalStatus) {
225
227
  span.setTag(TEST_FINAL_STATUS, finalStatus)
226
228
  }
229
+ if (earlyFlakeAbortReason) {
230
+ span.setTag(TEST_EARLY_FLAKE_ABORT_REASON, earlyFlakeAbortReason)
231
+ }
227
232
  if (hasBeenRetried) {
228
233
  span.setTag(TEST_IS_RETRY, 'true')
229
234
  if (isAtrRetry) {
@@ -90,9 +90,8 @@ class MongodbCorePlugin extends DatabasePlugin {
90
90
  }
91
91
  }
92
92
 
93
- function sanitizeBigInt (data) {
94
- return JSON.stringify(data, (_key, value) => typeof value === 'bigint' ? value.toString() : value)
95
- }
93
+ const MAX_DEPTH = 10
94
+ const MAX_QUERY_LENGTH = 10_000
96
95
 
97
96
  function extractQuery (statements) {
98
97
  if (statements.length === 1 && statements[0].q) return statements[0].q
@@ -100,7 +99,7 @@ function extractQuery (statements) {
100
99
  const extractedQueries = []
101
100
  for (let i = 0; i < statements.length; i++) {
102
101
  if (statements[i].q) {
103
- extractedQueries.push(limitDepth(statements[i].q))
102
+ extractedQueries.push(statements[i].q)
104
103
  }
105
104
  }
106
105
 
@@ -110,12 +109,12 @@ function extractQuery (statements) {
110
109
  function getQuery (cmd) {
111
110
  if (!cmd || (typeof cmd !== 'object' && !Array.isArray(cmd))) return
112
111
 
113
- if (Array.isArray(cmd)) return sanitizeBigInt(extractQuery(cmd))
114
- if (cmd.query) return sanitizeBigInt(limitDepth(cmd.query))
115
- if (cmd.filter) return sanitizeBigInt(limitDepth(cmd.filter))
116
- if (cmd.pipeline) return sanitizeBigInt(limitDepth(cmd.pipeline))
117
- if (cmd.deletes) return sanitizeBigInt(extractQuery(cmd.deletes))
118
- if (cmd.updates) return sanitizeBigInt(extractQuery(cmd.updates))
112
+ if (Array.isArray(cmd)) return sanitiseAndStringify(extractQuery(cmd))
113
+ if (cmd.query) return sanitiseAndStringify(cmd.query)
114
+ if (cmd.filter) return sanitiseAndStringify(cmd.filter)
115
+ if (cmd.pipeline) return sanitiseAndStringify(cmd.pipeline)
116
+ if (cmd.deletes) return sanitiseAndStringify(extractQuery(cmd.deletes))
117
+ if (cmd.updates) return sanitiseAndStringify(extractQuery(cmd.updates))
119
118
  }
120
119
 
121
120
  function getResource (plugin, ns, query, operationName) {
@@ -129,72 +128,40 @@ function getResource (plugin, ns, query, operationName) {
129
128
  }
130
129
 
131
130
  function truncate (input) {
132
- return input.slice(0, Math.min(input.length, 10_000))
133
- }
134
-
135
- function shouldSimplify (input) {
136
- return !isObject(input) || typeof input.toJSON === 'function'
131
+ return input.length > MAX_QUERY_LENGTH ? input.slice(0, MAX_QUERY_LENGTH) : input
137
132
  }
138
133
 
139
- function shouldHide (input) {
140
- return Buffer.isBuffer(input) || typeof input === 'function' || isBinary(input)
141
- }
142
-
143
- function limitDepth (input) {
144
- if (isBSON(input)) {
145
- input = input.toJSON()
146
- }
147
-
148
- if (shouldHide(input)) return '?'
149
- if (shouldSimplify(input)) return input
150
-
151
- const output = {}
152
- const queue = [{
153
- input,
154
- output,
155
- depth: 0,
156
- }]
157
-
158
- while (queue.length) {
159
- const {
160
- input, output, depth,
161
- } = queue.pop()
162
- const nextDepth = depth + 1
163
- for (const key of Object.keys(input)) {
164
- let child = input[key]
165
- if (typeof child === 'function') continue
166
-
167
- if (isBSON(child)) {
168
- child = typeof child.toJSON === 'function' ? child.toJSON() : '?'
169
- }
170
-
171
- if (depth >= 10 || shouldHide(child)) {
172
- output[key] = '?'
173
- } else if (shouldSimplify(child)) {
174
- output[key] = child
175
- } else {
176
- queue.push({
177
- input: child,
178
- output: output[key] = {},
179
- depth: nextDepth,
180
- })
134
+ // Single-pass sanitisation. The replacer:
135
+ // - skips functions and coerces bigint to its decimal string,
136
+ // - returns '?' for Buffer / BSON Binary on the *original* value (JSON.stringify already invoked
137
+ // toJSON before calling us; Buffer / Binary do have toJSON outputs we want to suppress),
138
+ // - lets JSON.stringify call toJSON on other BSON types (ObjectId, Long, Decimal128, Date, Timestamp, ...)
139
+ // so the result lands here as a primitive or plain object,
140
+ // - returns '?' for BSON types without toJSON (MinKey, MaxKey) where `value === original`,
141
+ // - tracks depth via an ancestor stack so cycles and depth >= MAX_DEPTH collapse to '?'.
142
+ function sanitiseAndStringify (input) {
143
+ const ancestors = []
144
+ return JSON.stringify(input, function (key, value) {
145
+ if (typeof value === 'function') return
146
+ if (typeof value === 'bigint') return value.toString()
147
+
148
+ const original = key === '' ? value : this[key]
149
+ if (typeof original === 'object' && original !== null) {
150
+ if (Buffer.isBuffer(original)) return '?'
151
+ const bsontype = original._bsontype
152
+ if (bsontype !== undefined && (bsontype === 'Binary' || value === original)) {
153
+ return '?'
181
154
  }
182
155
  }
183
- }
184
156
 
185
- return output
186
- }
157
+ if (value === null || typeof value !== 'object') return value
187
158
 
188
- function isObject (val) {
189
- return val !== null && typeof val === 'object' && !Array.isArray(val)
190
- }
191
-
192
- function isBSON (val) {
193
- return val && val._bsontype && !isBinary(val)
194
- }
159
+ while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop()
160
+ if (ancestors.length >= MAX_DEPTH || ancestors.includes(value)) return '?'
161
+ ancestors.push(value)
195
162
 
196
- function isBinary (val) {
197
- return val && val._bsontype === 'Binary'
163
+ return value
164
+ })
198
165
  }
199
166
 
200
167
  function isHeartbeat (ops, config) {
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { storage } = require('../../datadog-core')
4
- const CLIENT_PORT_KEY = require('../../dd-trace/src/constants')
4
+ const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
5
5
  const DatabasePlugin = require('../../dd-trace/src/plugins/database')
6
6
 
7
7
  class MySQLPlugin extends DatabasePlugin {
@@ -36,7 +36,8 @@ module.exports.init = function (tracerConfig) {
36
36
 
37
37
  interval = setInterval(() => {
38
38
  metrics.flush()
39
- }, FLUSH_INTERVAL).unref()
39
+ }, FLUSH_INTERVAL)
40
+ interval.unref?.()
40
41
 
41
42
  return { metrics, logger }
42
43
  }