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
@@ -31,11 +31,13 @@ const {
31
31
  TEST_SKIPPED_BY_ITR,
32
32
  TEST_ITR_UNSKIPPABLE,
33
33
  TEST_ITR_FORCED_RUN,
34
+ TEST_ITR_SKIPPING_ENABLED,
34
35
  ITR_CORRELATION_ID,
35
36
  TEST_SOURCE_FILE,
36
37
  TEST_IS_NEW,
37
38
  TEST_IS_RETRY,
38
39
  TEST_EARLY_FLAKE_ENABLED,
40
+ TEST_EARLY_FLAKE_ABORT_REASON,
39
41
  getTestSessionName,
40
42
  TEST_SESSION_NAME,
41
43
  TEST_LEVEL_EVENT_TYPES,
@@ -52,19 +54,26 @@ const {
52
54
  getPullRequestDiff,
53
55
  getModifiedFilesFromDiff,
54
56
  getSessionRequestErrorTags,
55
- DD_CI_LIBRARY_CONFIGURATION_ERROR,
57
+ DD_CI_LIBRARY_CONFIGURATION_ERROR_SETTINGS,
58
+ DD_CI_LIBRARY_CONFIGURATION_ERROR_KNOWN_TESTS,
59
+ DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS,
60
+ DD_CI_LIBRARY_CONFIGURATION_ERROR_TEST_MANAGEMENT_TESTS,
61
+ getSessionItrSkippingEnabledTags,
56
62
  TEST_IS_MODIFIED,
57
63
  TEST_HAS_DYNAMIC_NAME,
64
+ getIsFaultyEarlyFlakeDetection,
58
65
  DYNAMIC_NAME_RE,
59
66
  recordAttemptToFixExecution,
60
67
  logAttemptToFixTestExecution,
61
68
  logTestOptimizationSummary,
69
+ getEfdRetryCount,
70
+ getMaxEfdRetryCount,
62
71
  getPullRequestBaseBranch,
63
72
  TEST_FINAL_STATUS,
64
73
  } = require('../../dd-trace/src/plugins/util/test')
65
74
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
66
75
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
67
- const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
76
+ const getConfig = require('../../dd-trace/src/config')
68
77
  const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
69
78
  const log = require('../../dd-trace/src/log')
70
79
 
@@ -263,6 +272,36 @@ function getSuiteStatus (suiteStats) {
263
272
  return 'pass'
264
273
  }
265
274
 
275
+ function getMatchingCypressTest (cypressTests, testName, attemptIndex, testStatus, preferIndexedMatch = false) {
276
+ let matchingTestByIndex
277
+ let matchingTestByStatus
278
+ let matchingTestIndex = 0
279
+
280
+ for (const cypressTest of cypressTests) {
281
+ if (cypressTest.title.join(' ') !== testName) {
282
+ continue
283
+ }
284
+
285
+ if (matchingTestIndex === attemptIndex) {
286
+ matchingTestByIndex = cypressTest
287
+ }
288
+ matchingTestIndex++
289
+
290
+ if (!matchingTestByStatus && CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] === testStatus) {
291
+ matchingTestByStatus = cypressTest
292
+ }
293
+ }
294
+
295
+ return preferIndexedMatch
296
+ ? matchingTestByIndex || matchingTestByStatus
297
+ : matchingTestByStatus || matchingTestByIndex
298
+ }
299
+
300
+ function isCypressHookFailure (cypressTest) {
301
+ return CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] === 'fail' &&
302
+ /\bhook\b/.test(String(cypressTest.displayError || ''))
303
+ }
304
+
266
305
  const FINAL_STATUS_RETRY_KIND = {
267
306
  none: 'none',
268
307
  atr: 'atr',
@@ -320,14 +359,20 @@ class CypressPlugin {
320
359
 
321
360
  finishedTestsByFile = {}
322
361
  testStatuses = {}
362
+ hasLibraryConfiguration = false
323
363
  isTestsSkipped = false
324
364
  isSuitesSkippingEnabled = false
325
365
  isCodeCoverageEnabled = false
326
366
  isFlakyTestRetriesEnabled = false
327
367
  flakyTestRetriesCount = 0
328
368
  isEarlyFlakeDetectionEnabled = false
369
+ isEarlyFlakeDetectionFaulty = false
329
370
  isKnownTestsEnabled = false
330
371
  earlyFlakeDetectionNumRetries = 0
372
+ earlyFlakeDetectionSlowTestRetries = {}
373
+ efdRetryCountByTest = {}
374
+ efdSlowAbortedTests = {}
375
+ earlyFlakeDetectionFaultyThreshold = 0
331
376
  testsToSkip = []
332
377
  skippedTests = []
333
378
  hasForcedToRunSuites = false
@@ -393,14 +438,20 @@ class CypressPlugin {
393
438
  this._isInit = false
394
439
  this.finishedTestsByFile = {}
395
440
  this.testStatuses = {}
441
+ this.hasLibraryConfiguration = false
396
442
  this.isTestsSkipped = false
397
443
  this.isSuitesSkippingEnabled = false
398
444
  this.isCodeCoverageEnabled = false
399
445
  this.isFlakyTestRetriesEnabled = false
400
446
  this.flakyTestRetriesCount = 0
401
447
  this.isEarlyFlakeDetectionEnabled = false
448
+ this.isEarlyFlakeDetectionFaulty = false
402
449
  this.isKnownTestsEnabled = false
403
450
  this.earlyFlakeDetectionNumRetries = 0
451
+ this.earlyFlakeDetectionSlowTestRetries = {}
452
+ this.efdRetryCountByTest = {}
453
+ this.efdSlowAbortedTests = {}
454
+ this.earlyFlakeDetectionFaultyThreshold = 0
404
455
  this.testsToSkip = []
405
456
  this.skippedTests = []
406
457
  this.hasForcedToRunSuites = false
@@ -454,8 +505,7 @@ class CypressPlugin {
454
505
 
455
506
  this.isTestIsolationEnabled = getIsTestIsolationEnabled(cypressConfig)
456
507
 
457
- const envFlushWait = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS'))
458
- this.rumFlushWaitMillis = Number.isFinite(envFlushWait) ? envFlushWait : undefined
508
+ this.rumFlushWaitMillis = getConfig().DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS
459
509
 
460
510
  if (!this.isTestIsolationEnabled) {
461
511
  log.warn('Test isolation is disabled, retries will not be enabled')
@@ -471,16 +521,19 @@ class CypressPlugin {
471
521
  if (libraryConfigurationResponse.err) {
472
522
  log.error('Cypress plugin library config response error', libraryConfigurationResponse.err)
473
523
  this._pendingRequestErrorTags.push({
474
- tag: DD_CI_LIBRARY_CONFIGURATION_ERROR,
524
+ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_SETTINGS,
475
525
  value: 'true',
476
526
  })
477
527
  } else {
528
+ this.hasLibraryConfiguration = true
478
529
  const {
479
530
  libraryConfig: {
480
531
  isSuitesSkippingEnabled,
481
532
  isCodeCoverageEnabled,
482
533
  isEarlyFlakeDetectionEnabled,
483
534
  earlyFlakeDetectionNumRetries,
535
+ earlyFlakeDetectionSlowTestRetries,
536
+ earlyFlakeDetectionFaultyThreshold,
484
537
  isFlakyTestRetriesEnabled,
485
538
  flakyTestRetriesCount,
486
539
  isKnownTestsEnabled,
@@ -493,6 +546,8 @@ class CypressPlugin {
493
546
  this.isCodeCoverageEnabled = isCodeCoverageEnabled
494
547
  this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
495
548
  this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
549
+ this.earlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries ?? {}
550
+ this.earlyFlakeDetectionFaultyThreshold = earlyFlakeDetectionFaultyThreshold
496
551
  this.isKnownTestsEnabled = isKnownTestsEnabled
497
552
  if (isFlakyTestRetriesEnabled && this.isTestIsolationEnabled) {
498
553
  this.isFlakyTestRetriesEnabled = true
@@ -533,9 +588,76 @@ class CypressPlugin {
533
588
  return { isAttemptToFix, isDisabled, isQuarantined }
534
589
  }
535
590
 
591
+ /**
592
+ * Returns how many EFD retries must be scheduled before the first duration is known.
593
+ *
594
+ * @returns {number}
595
+ */
596
+ getConfiguredEfdRetryCount () {
597
+ const { earlyFlakeDetectionSlowTestRetries } = this
598
+ if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
599
+ return this.earlyFlakeDetectionNumRetries
600
+ }
601
+ return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
602
+ }
603
+
604
+ /**
605
+ * Returns the selected EFD retry count for a test, or the scheduling count if it has not run yet.
606
+ *
607
+ * @param {string} testSuite
608
+ * @param {string} testName
609
+ * @returns {number}
610
+ */
611
+ getEfdRetryCountForTest (testSuite, testName) {
612
+ const testSuiteRetries = this.efdRetryCountByTest[testSuite]
613
+ if (!testSuiteRetries || testSuiteRetries[testName] === undefined) {
614
+ return this.getConfiguredEfdRetryCount()
615
+ }
616
+ return testSuiteRetries[testName]
617
+ }
618
+
619
+ /**
620
+ * Stores the selected EFD retry count for a test after its first execution duration is known.
621
+ *
622
+ * @param {string} testSuite
623
+ * @param {string} testName
624
+ * @param {number | undefined} duration
625
+ * @returns {number}
626
+ */
627
+ setEfdRetryCountForTest (testSuite, testName, duration) {
628
+ if (!this.efdRetryCountByTest[testSuite]) {
629
+ this.efdRetryCountByTest[testSuite] = {}
630
+ }
631
+ const retryCount = getEfdRetryCount(duration ?? 0, this.earlyFlakeDetectionSlowTestRetries)
632
+ this.efdRetryCountByTest[testSuite][testName] = retryCount
633
+ if (retryCount === 0) {
634
+ if (!this.efdSlowAbortedTests[testSuite]) {
635
+ this.efdSlowAbortedTests[testSuite] = {}
636
+ }
637
+ this.efdSlowAbortedTests[testSuite][testName] = true
638
+ }
639
+ return retryCount
640
+ }
641
+
642
+ /**
643
+ * Returns whether an EFD retry clone is beyond the selected retry count and should be discarded.
644
+ *
645
+ * @param {string} testSuite
646
+ * @param {string} testName
647
+ * @param {number} efdRetryIndex
648
+ * @returns {boolean}
649
+ */
650
+ shouldSkipEfdRetry (testSuite, testName, efdRetryIndex) {
651
+ const testSuiteRetries = this.efdRetryCountByTest[testSuite]
652
+ return testSuiteRetries?.[testName] !== undefined && efdRetryIndex > testSuiteRetries[testName]
653
+ }
654
+
536
655
  getTestSuiteSpan ({ testSuite, testSuiteAbsolutePath }) {
537
- const testSuiteSpanMetadata =
538
- getTestSuiteCommonTags(this.command, this.frameworkVersion, testSuite, TEST_FRAMEWORK_NAME)
656
+ const testSuiteSpanMetadata = {
657
+ ...getTestSuiteCommonTags(this.command, this.frameworkVersion, testSuite, TEST_FRAMEWORK_NAME),
658
+ ...this.getSessionRequestErrorTags(),
659
+ ...this.getSessionItrSkippingEnabledTags(),
660
+ }
539
661
 
540
662
  this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
541
663
 
@@ -588,6 +710,7 @@ class CypressPlugin {
588
710
  if (testSourceFile) {
589
711
  testSpanMetadata[TEST_SOURCE_FILE] = testSourceFile
590
712
  }
713
+ Object.assign(testSpanMetadata, this.getSessionItrSkippingEnabledTags())
591
714
 
592
715
  const codeOwners = this.getTestCodeOwners({ testSuite, testSourceFile })
593
716
  if (codeOwners) {
@@ -637,6 +760,15 @@ class CypressPlugin {
637
760
  return getSessionRequestErrorTags(this.testSessionSpan)
638
761
  }
639
762
 
763
+ /**
764
+ * Returns ITR skipping-enabled tags from the test session span for propagation to child events.
765
+ *
766
+ * @returns {Record<string, string>}
767
+ */
768
+ getSessionItrSkippingEnabledTags () {
769
+ return getSessionItrSkippingEnabledTags(this.testSessionSpan)
770
+ }
771
+
640
772
  ciVisEvent (name, testLevel, tags = {}) {
641
773
  incrementCountMetric(name, {
642
774
  testLevel,
@@ -662,10 +794,11 @@ class CypressPlugin {
662
794
  )
663
795
  if (knownTestsResponse.err) {
664
796
  log.error('Cypress known tests response error', knownTestsResponse.err)
797
+ this._pendingRequestErrorTags.push({ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_KNOWN_TESTS, value: 'true' })
665
798
  this.isEarlyFlakeDetectionEnabled = false
666
799
  this.isKnownTestsEnabled = false
667
800
  } else {
668
- if (knownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]) {
801
+ if (knownTestsResponse.knownTests?.[TEST_FRAMEWORK_NAME]) {
669
802
  this.knownTestsByTestSuite = knownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]
670
803
  } else {
671
804
  this.isEarlyFlakeDetectionEnabled = false
@@ -674,6 +807,21 @@ class CypressPlugin {
674
807
  }
675
808
  }
676
809
 
810
+ if (this.isKnownTestsEnabled && details.specs) {
811
+ const testSuites = details.specs.map(({ relative }) => relative)
812
+ const isFaulty = getIsFaultyEarlyFlakeDetection(
813
+ testSuites,
814
+ this.knownTestsByTestSuite,
815
+ this.earlyFlakeDetectionFaultyThreshold
816
+ )
817
+ if (isFaulty) {
818
+ log.warn('New test detection is disabled because the number of new test files is too high.')
819
+ this.isEarlyFlakeDetectionEnabled = false
820
+ this.isEarlyFlakeDetectionFaulty = true
821
+ this.isKnownTestsEnabled = false
822
+ }
823
+ }
824
+
677
825
  if (this.isSuitesSkippingEnabled) {
678
826
  const skippableTestsResponse = await getSkippableTests(
679
827
  this.tracer,
@@ -681,6 +829,7 @@ class CypressPlugin {
681
829
  )
682
830
  if (skippableTestsResponse.err) {
683
831
  log.error('Cypress skippable tests response error', skippableTestsResponse.err)
832
+ this._pendingRequestErrorTags.push({ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, value: 'true' })
684
833
  } else {
685
834
  const { skippableTests, correlationId } = skippableTestsResponse
686
835
  this.testsToSkip = skippableTests || []
@@ -696,6 +845,10 @@ class CypressPlugin {
696
845
  )
697
846
  if (testManagementTestsResponse.err) {
698
847
  log.error('Cypress test management tests response error', testManagementTestsResponse.err)
848
+ this._pendingRequestErrorTags.push({
849
+ tag: DD_CI_LIBRARY_CONFIGURATION_ERROR_TEST_MANAGEMENT_TESTS,
850
+ value: 'true',
851
+ })
699
852
  this.isTestManagementTestsEnabled = false
700
853
  } else {
701
854
  this.testManagementTests = testManagementTestsResponse.testManagementTests
@@ -731,6 +884,9 @@ class CypressPlugin {
731
884
  if (this.isEarlyFlakeDetectionEnabled) {
732
885
  testSessionSpanMetadata[TEST_EARLY_FLAKE_ENABLED] = 'true'
733
886
  }
887
+ if (this.isEarlyFlakeDetectionFaulty) {
888
+ testSessionSpanMetadata[TEST_EARLY_FLAKE_ABORT_REASON] = 'faulty'
889
+ }
734
890
 
735
891
  const trimmedCommand = DD_MAJOR < 6 ? this.command : 'cypress run'
736
892
 
@@ -789,6 +945,11 @@ class CypressPlugin {
789
945
  },
790
946
  integrationName: TEST_FRAMEWORK_NAME,
791
947
  })
948
+ if (this.hasLibraryConfiguration) {
949
+ const skippingEnabled = this.isSuitesSkippingEnabled ? 'true' : 'false'
950
+ this.testSessionSpan.setTag(TEST_ITR_SKIPPING_ENABLED, skippingEnabled)
951
+ this.testModuleSpan.setTag(TEST_ITR_SKIPPING_ENABLED, skippingEnabled)
952
+ }
792
953
  this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
793
954
 
794
955
  return details
@@ -833,7 +994,7 @@ class CypressPlugin {
833
994
  this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
834
995
  incrementCountMetric(TELEMETRY_TEST_SESSION, {
835
996
  provider: this.ciProviderName,
836
- autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER'),
997
+ autoInjected: !!getConfig().DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
837
998
  })
838
999
 
839
1000
  finishAllTraceSpans(this.testSessionSpan)
@@ -934,17 +1095,25 @@ class CypressPlugin {
934
1095
 
935
1096
  for (const [testName, finishedTestAttempts] of Object.entries(finishedTestsByTestName)) {
936
1097
  for (const [attemptIndex, finishedTest] of finishedTestAttempts.entries()) {
937
- // TODO: there could be multiple if there have been retries!
938
- // potentially we need to match the test status!
939
- const cypressTest = cypressTests.find(test => test.title.join(' ') === testName)
1098
+ // We can check if this is the last attempt regardless of the retry mechanism
1099
+ const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
1100
+ const isDatadogManagedAttempt = finishedTest.isEfdManagedTest || finishedTest.isAttemptToFix
1101
+ const cypressTest = isDatadogManagedAttempt
1102
+ ? getMatchingCypressTest(cypressTests, testName, attemptIndex, finishedTest.testStatus, isLastAttempt) ||
1103
+ cypressTests.find(test => test.title.join(' ') === testName)
1104
+ : cypressTests.find(test => test.title.join(' ') === testName)
940
1105
  if (!cypressTest) {
941
1106
  continue
942
1107
  }
943
1108
  // finishedTests can include multiple tests with the same name if they have been retried
944
1109
  // by early flake detection. Cypress is unaware of this so .attempts does not necessarily have
945
1110
  // the same length as `finishedTestAttempts`
946
- let cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
947
- if (cypressTest.attempts && cypressTest.attempts[attemptIndex]) {
1111
+ const shouldUseCapturedStatus = isDatadogManagedAttempt && !(isLastAttempt && isCypressHookFailure(cypressTest))
1112
+ let cypressTestStatus = shouldUseCapturedStatus
1113
+ ? finishedTest.testStatus
1114
+ : CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
1115
+ if (!finishedTest.isEfdManagedTest && !finishedTest.isAttemptToFix &&
1116
+ cypressTest.attempts && cypressTest.attempts[attemptIndex]) {
948
1117
  cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.attempts[attemptIndex].state]
949
1118
  const isAtrRetry = attemptIndex > 0 &&
950
1119
  this.isFlakyTestRetriesEnabled &&
@@ -961,6 +1130,9 @@ class CypressPlugin {
961
1130
  }
962
1131
  }
963
1132
  }
1133
+ if (finishedTest.isEfdManagedTest && finishedTest.testStatus !== 'skip' && cypressTestStatus === 'skip') {
1134
+ cypressTestStatus = finishedTest.testStatus
1135
+ }
964
1136
  if (cypressTest.displayError) {
965
1137
  latestError = new Error(cypressTest.displayError)
966
1138
  }
@@ -970,6 +1142,9 @@ class CypressPlugin {
970
1142
  if (cypressTestStatus !== finishedTest.testStatus && (!isQuarantinedTest || finishedTest.isAttemptToFix)) {
971
1143
  finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
972
1144
  finishedTest.testSpan.setTag('error', latestError)
1145
+ if (finishedTest.isAttemptToFix && cypressTestStatus === 'fail') {
1146
+ finishedTest.testSpan.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'false')
1147
+ }
973
1148
  }
974
1149
  if (this.itrCorrelationId) {
975
1150
  finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
@@ -989,8 +1164,6 @@ class CypressPlugin {
989
1164
  finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
990
1165
  }
991
1166
 
992
- // We can check if this is the last attempt regardless of the retry mechanism
993
- const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
994
1167
  if (isLastAttempt) {
995
1168
  const testSpanTags = finishedTest.testSpan.context()._tags
996
1169
  const retryKind = getFinalStatusRetryKind({
@@ -1042,7 +1215,8 @@ class CypressPlugin {
1042
1215
  const suitePayload = {
1043
1216
  isEarlyFlakeDetectionEnabled: this.isEarlyFlakeDetectionEnabled,
1044
1217
  knownTestsForSuite: this.knownTestsByTestSuite?.[testSuite] || [],
1045
- earlyFlakeDetectionNumRetries: this.earlyFlakeDetectionNumRetries,
1218
+ earlyFlakeDetectionNumRetries: this.getConfiguredEfdRetryCount(),
1219
+ earlyFlakeDetectionSlowTestRetries: this.earlyFlakeDetectionSlowTestRetries,
1046
1220
  isKnownTestsEnabled: this.isKnownTestsEnabled,
1047
1221
  isTestManagementEnabled: this.isTestManagementTestsEnabled,
1048
1222
  testManagementAttemptToFixRetries: this.testManagementAttemptToFixRetries,
@@ -1054,14 +1228,14 @@ class CypressPlugin {
1054
1228
  rumFlushWaitMillis: this.rumFlushWaitMillis,
1055
1229
  }
1056
1230
 
1057
- if (this.testSuiteSpan) {
1058
- return suitePayload
1059
- }
1060
- this.testSuiteSpan = this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
1231
+ this.testSuiteSpan ||= this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
1061
1232
  return suitePayload
1062
1233
  },
1063
1234
  'dd:beforeEach': (test) => {
1064
- const { testName, testSuite } = test
1235
+ const { testName, testSuite, isEfdRetry, efdRetryIndex } = test
1236
+ if (isEfdRetry && this.shouldSkipEfdRetry(testSuite, testName, efdRetryIndex)) {
1237
+ return { shouldSkip: true, shouldDiscard: true }
1238
+ }
1065
1239
  const shouldSkip = this.testsToSkip.some(test => {
1066
1240
  return testName === test.name && testSuite === test.suite
1067
1241
  })
@@ -1118,6 +1292,7 @@ class CypressPlugin {
1118
1292
  isEfdRetry,
1119
1293
  isAttemptToFix,
1120
1294
  isModified,
1295
+ duration,
1121
1296
  isQuarantined: isQuarantinedFromSupport,
1122
1297
  isDisabled: isDisabledFromSupport,
1123
1298
  } = test
@@ -1139,7 +1314,19 @@ class CypressPlugin {
1139
1314
  }
1140
1315
  this.tracer._tracer._exporter.exportCoverage(formattedCoverage)
1141
1316
  }
1142
- const testStatus = CYPRESS_STATUS_TO_TEST_STATUS[state]
1317
+ const isEfdManagedTest = (isNew || isModified) && this.isEarlyFlakeDetectionEnabled && !isAttemptToFix
1318
+ let testStatus = CYPRESS_STATUS_TO_TEST_STATUS[state]
1319
+ let didAbortSlowEfdRetries = false
1320
+ if (isEfdManagedTest && !isEfdRetry && this.efdRetryCountByTest[testSuite]?.[testName] === undefined) {
1321
+ const retryCount = this.setEfdRetryCountForTest(testSuite, testName, duration)
1322
+ if (retryCount === 0) {
1323
+ didAbortSlowEfdRetries = true
1324
+ this.activeTestSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'slow')
1325
+ }
1326
+ }
1327
+ if (didAbortSlowEfdRetries && testStatus === 'skip' && !error && duration > 0) {
1328
+ testStatus = 'pass'
1329
+ }
1143
1330
  this.activeTestSpan.setTag(TEST_STATUS, testStatus)
1144
1331
 
1145
1332
  // Save the test status to know if it has passed all retries
@@ -1202,9 +1389,10 @@ class CypressPlugin {
1202
1389
  }
1203
1390
  }
1204
1391
  // Check if all EFD retries failed
1205
- if ((isNew || isModified) && this.earlyFlakeDetectionNumRetries > 0) {
1206
- const isLastEfdAttempt = testStatuses.length === this.earlyFlakeDetectionNumRetries + 1
1207
- if (isLastEfdAttempt && testStatuses.every(status => status === 'fail')) {
1392
+ if (isEfdManagedTest) {
1393
+ const efdRetryCount = this.getEfdRetryCountForTest(testSuite, testName)
1394
+ const isLastEfdAttempt = testStatuses.length === efdRetryCount + 1
1395
+ if (efdRetryCount > 0 && isLastEfdAttempt && testStatuses.every(status => status === 'fail')) {
1208
1396
  this.activeTestSpan.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
1209
1397
  }
1210
1398
  }
@@ -1255,6 +1443,7 @@ class CypressPlugin {
1255
1443
  finishTime: this._now(),
1256
1444
  testSpan: this.activeTestSpan,
1257
1445
  isEfdRetry,
1446
+ isEfdManagedTest,
1258
1447
  isAttemptToFix,
1259
1448
  }
1260
1449
  if (this.finishedTestsByFile[testSuite]) {
@@ -79,6 +79,9 @@ function getRetriedTests (test, numRetries, tags) {
79
79
  // TODO: signal in framework logs that this is a retry.
80
80
  // TODO: Change it so these tests are allowed to fail.
81
81
  const clonedTest = test.clone()
82
+ if (tags.includes('_ddIsEfdRetry')) {
83
+ clonedTest._ddEfdRetryIndex = retryIndex + 1
84
+ }
82
85
  for (const tag of tags) {
83
86
  if (tag) {
84
87
  clonedTest[tag] = true
@@ -173,7 +176,12 @@ beforeEach(function () {
173
176
  cy.task('dd:beforeEach', {
174
177
  testName,
175
178
  testSuite: Cypress.mocha.getRootSuite().file,
176
- }).then(({ traceId, shouldSkip }) => {
179
+ isEfdRetry: Cypress.mocha.getRunner().suite.ctx.currentTest._ddIsEfdRetry,
180
+ efdRetryIndex: Cypress.mocha.getRunner().suite.ctx.currentTest._ddEfdRetryIndex,
181
+ }).then(({ traceId, shouldSkip, shouldDiscard }) => {
182
+ if (shouldDiscard) {
183
+ this.currentTest._ddShouldDiscard = true
184
+ }
177
185
  if (traceId) {
178
186
  cy.setCookie(DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME, traceId).then(() => {
179
187
  // When testIsolation:false, the page is not reset between tests, so the RUM session
@@ -242,6 +250,9 @@ after(() => {
242
250
 
243
251
  afterEach(function () {
244
252
  const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
253
+ if (currentTest._ddShouldDiscard) {
254
+ return
255
+ }
245
256
  const testName = currentTest.fullTitle()
246
257
 
247
258
  // Check if this was a test management test that we suppressed the failure for.
@@ -267,6 +278,7 @@ afterEach(function () {
267
278
  isEfdRetry: currentTest._ddIsEfdRetry,
268
279
  isAttemptToFix: currentTest._ddIsAttemptToFix,
269
280
  isModified: currentTest._ddIsModified,
281
+ duration: currentTest.duration,
270
282
  // Mark suppressed tests so the plugin can tag them with the correct test management reason.
271
283
  isQuarantined: isTestManagementTestThatFailed && suppressedTestFailure.isQuarantined,
272
284
  isDisabled: isTestManagementTestThatFailed && suppressedTestFailure.isDisabled,
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ const CompositePlugin = require('../../dd-trace/src/plugins/composite')
4
+ const ElectronIpcPlugin = require('./ipc')
5
+ const ElectronNetPlugin = require('./net')
6
+
7
+ class ElectronPlugin extends CompositePlugin {
8
+ static id = 'electron'
9
+ static get plugins () {
10
+ return {
11
+ net: ElectronNetPlugin,
12
+ ipc: ElectronIpcPlugin,
13
+ }
14
+ }
15
+ }
16
+
17
+ module.exports = ElectronPlugin
@@ -0,0 +1,143 @@
1
+ 'use strict'
2
+
3
+ const CompositePlugin = require('../../dd-trace/src/plugins/composite')
4
+ const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
5
+ const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
6
+
7
+ class ElectronIpcPlugin extends CompositePlugin {
8
+ static id = 'electron:ipc'
9
+ static get plugins () {
10
+ return {
11
+ main: ElectronMainPlugin,
12
+ renderer: ElectronRendererPlugin,
13
+ }
14
+ }
15
+ }
16
+
17
+ class ElectronMainPlugin extends CompositePlugin {
18
+ static id = 'electron:ipc:main'
19
+ static get plugins () {
20
+ return {
21
+ receive: ElectronMainReceivePlugin,
22
+ handle: ElectronMainHandlePlugin,
23
+ send: ElectronMainSendPlugin,
24
+ }
25
+ }
26
+ }
27
+
28
+ class ElectronRendererPlugin extends CompositePlugin {
29
+ static id = 'electron:ipc:renderer'
30
+ static get plugins () {
31
+ return {
32
+ receive: ElectronRendererReceivePlugin,
33
+ send: ElectronRendererSendPlugin,
34
+ }
35
+ }
36
+ }
37
+
38
+ class ElectronRendererReceivePlugin extends ConsumerPlugin {
39
+ static id = 'electron:ipc:renderer:receive'
40
+ static component = 'electron'
41
+ static operation = 'receive'
42
+ static prefix = 'tracing:apm:electron:ipc:renderer:receive'
43
+
44
+ bindStart (ctx) {
45
+ const { args, channel } = ctx
46
+
47
+ if (channel?.startsWith('datadog:')) return
48
+
49
+ const childOf = this._tracer.extract('text_map', args[args.length - 1])
50
+
51
+ if (childOf) {
52
+ args.pop()
53
+ }
54
+
55
+ this.startSpan({
56
+ childOf,
57
+ resource: channel,
58
+ type: 'worker',
59
+ meta: {},
60
+ }, ctx)
61
+
62
+ return ctx.currentStore
63
+ }
64
+
65
+ asyncEnd (ctx) {
66
+ this.finish(ctx)
67
+ }
68
+ }
69
+
70
+ class ElectronRendererSendPlugin extends ProducerPlugin {
71
+ static id = 'electron:ipc:renderer:send'
72
+ static component = 'electron'
73
+ static operation = 'send'
74
+ static prefix = 'tracing:apm:electron:ipc:renderer:send'
75
+
76
+ bindStart (ctx) {
77
+ const { args, channel } = ctx
78
+
79
+ if (channel?.startsWith('datadog:')) return
80
+
81
+ const span = this.startSpan({
82
+ resource: channel,
83
+ meta: {},
84
+ }, ctx)
85
+
86
+ if (this._shouldInject(ctx)) {
87
+ const carrier = {}
88
+
89
+ this._tracer.inject(span, 'text_map', carrier)
90
+
91
+ args.push(carrier)
92
+ }
93
+
94
+ return ctx.currentStore
95
+ }
96
+
97
+ end (ctx) {
98
+ if (ctx.hasOwnProperty('result')) {
99
+ this.finish(ctx)
100
+ }
101
+ }
102
+
103
+ asyncEnd (ctx) {
104
+ this.finish(ctx)
105
+ }
106
+
107
+ // Renderer can always inject since main is guaranteed to be patched.
108
+ _shouldInject () {
109
+ return true
110
+ }
111
+ }
112
+
113
+ class ElectronMainReceivePlugin extends ElectronRendererReceivePlugin {
114
+ static id = 'electron:ipc:main:receive'
115
+ static prefix = 'tracing:apm:electron:ipc:main:receive'
116
+ }
117
+
118
+ class ElectronMainHandlePlugin extends ElectronMainReceivePlugin {
119
+ static id = 'electron:ipc:main:handle'
120
+ static prefix = 'tracing:apm:electron:ipc:main:handle'
121
+ }
122
+
123
+ class ElectronMainSendPlugin extends ElectronRendererSendPlugin {
124
+ static id = 'electron:ipc:main:send'
125
+ static prefix = 'tracing:apm:electron:ipc:main:send'
126
+
127
+ constructor (...args) {
128
+ super(...args)
129
+
130
+ this._renderers = new WeakSet()
131
+
132
+ this.addSub('apm:electron:ipc:renderer:patched', event => {
133
+ this._renderers.add(event.sender)
134
+ })
135
+ }
136
+
137
+ // Only inject when the renderer was patched.
138
+ _shouldInject ({ self }) {
139
+ return this._renderers.has(self)
140
+ }
141
+ }
142
+
143
+ module.exports = ElectronIpcPlugin