dd-trace 5.96.0 → 5.98.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 (173) hide show
  1. package/index.d.ts +60 -2
  2. package/package.json +9 -7
  3. package/packages/datadog-esbuild/index.js +20 -9
  4. package/packages/datadog-instrumentations/src/child_process.js +7 -17
  5. package/packages/datadog-instrumentations/src/crypto.js +1 -2
  6. package/packages/datadog-instrumentations/src/cucumber.js +69 -4
  7. package/packages/datadog-instrumentations/src/cypress-config.js +318 -0
  8. package/packages/datadog-instrumentations/src/cypress.js +86 -4
  9. package/packages/datadog-instrumentations/src/dns.js +1 -2
  10. package/packages/datadog-instrumentations/src/express.js +4 -4
  11. package/packages/datadog-instrumentations/src/fs.js +27 -29
  12. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +41 -13
  14. package/packages/datadog-instrumentations/src/helpers/hook.js +31 -6
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -19
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -13
  17. package/packages/datadog-instrumentations/src/helpers/register.js +103 -142
  18. package/packages/datadog-instrumentations/src/http/client.js +2 -3
  19. package/packages/datadog-instrumentations/src/http/server.js +2 -5
  20. package/packages/datadog-instrumentations/src/http2/client.js +1 -3
  21. package/packages/datadog-instrumentations/src/http2/server.js +1 -3
  22. package/packages/datadog-instrumentations/src/jest.js +117 -16
  23. package/packages/datadog-instrumentations/src/limitd-client.js +1 -1
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +12 -1
  25. package/packages/datadog-instrumentations/src/net.js +2 -8
  26. package/packages/datadog-instrumentations/src/pino.js +1 -1
  27. package/packages/datadog-instrumentations/src/playwright.js +4 -1
  28. package/packages/datadog-instrumentations/src/prisma.js +1 -2
  29. package/packages/datadog-instrumentations/src/redis.js +12 -6
  30. package/packages/datadog-instrumentations/src/selenium.js +4 -1
  31. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  32. package/packages/datadog-instrumentations/src/url.js +1 -3
  33. package/packages/datadog-instrumentations/src/vitest.js +5 -1
  34. package/packages/datadog-instrumentations/src/vm.js +1 -3
  35. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -4
  36. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  37. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  38. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  39. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  40. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  41. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  42. package/packages/datadog-plugin-cucumber/src/index.js +13 -3
  43. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +166 -6
  44. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  47. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  48. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  49. package/packages/datadog-plugin-http/src/client.js +1 -1
  50. package/packages/datadog-plugin-http/src/server.js +10 -2
  51. package/packages/datadog-plugin-http2/src/client.js +1 -1
  52. package/packages/datadog-plugin-http2/src/server.js +10 -2
  53. package/packages/datadog-plugin-jest/src/index.js +4 -2
  54. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +31 -4
  55. package/packages/datadog-plugin-mocha/src/index.js +5 -2
  56. package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
  57. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  58. package/packages/datadog-plugin-next/src/index.js +10 -16
  59. package/packages/datadog-plugin-openai/src/services.js +1 -0
  60. package/packages/datadog-plugin-pg/src/index.js +1 -1
  61. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  62. package/packages/datadog-plugin-ws/src/close.js +1 -1
  63. package/packages/datadog-plugin-ws/src/receiver.js +1 -1
  64. package/packages/datadog-webpack/index.js +3 -3
  65. package/packages/dd-trace/index.js +12 -10
  66. package/packages/dd-trace/src/agent/url.js +2 -2
  67. package/packages/dd-trace/src/aiguard/sdk.js +26 -22
  68. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  69. package/packages/dd-trace/src/appsec/blocking.js +64 -33
  70. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  71. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +1 -1
  72. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  73. package/packages/dd-trace/src/appsec/remote_config.js +1 -0
  74. package/packages/dd-trace/src/appsec/sdk/index.js +4 -0
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +6 -1
  80. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  81. package/packages/dd-trace/src/config/defaults.js +315 -146
  82. package/packages/dd-trace/src/config/generated-config-types.d.ts +9 -1
  83. package/packages/dd-trace/src/config/helper.js +59 -10
  84. package/packages/dd-trace/src/config/index.js +587 -1496
  85. package/packages/dd-trace/src/config/parsers.js +256 -0
  86. package/packages/dd-trace/src/config/remote_config.js +59 -2
  87. package/packages/dd-trace/src/config/supported-configurations.json +406 -432
  88. package/packages/dd-trace/src/constants.js +1 -0
  89. package/packages/dd-trace/src/crashtracking/crashtracker.js +7 -1
  90. package/packages/dd-trace/src/crashtracking/index.js +1 -7
  91. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  92. package/packages/dd-trace/src/debugger/index.js +1 -1
  93. package/packages/dd-trace/src/dogstatsd.js +12 -9
  94. package/packages/dd-trace/src/encode/0.4.js +8 -7
  95. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  96. package/packages/dd-trace/src/exporters/agent/writer.js +7 -1
  97. package/packages/dd-trace/src/exporters/common/request.js +9 -0
  98. package/packages/dd-trace/src/exporters/common/writer.js +12 -2
  99. package/packages/dd-trace/src/heap_snapshots.js +3 -0
  100. package/packages/dd-trace/src/index.js +5 -2
  101. package/packages/dd-trace/src/lambda/runtime/ritm.js +6 -6
  102. package/packages/dd-trace/src/llmobs/index.js +4 -1
  103. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -1
  104. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +60 -12
  105. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +4 -2
  106. package/packages/dd-trace/src/llmobs/sdk.js +12 -8
  107. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  108. package/packages/dd-trace/src/llmobs/tagger.js +9 -6
  109. package/packages/dd-trace/src/llmobs/writers/base.js +2 -0
  110. package/packages/dd-trace/src/llmobs/writers/util.js +3 -0
  111. package/packages/dd-trace/src/log/index.js +20 -59
  112. package/packages/dd-trace/src/log/writer.js +7 -19
  113. package/packages/dd-trace/src/noop/proxy.js +8 -0
  114. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  115. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  116. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  117. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  118. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
  119. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  120. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  121. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  122. package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
  123. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
  124. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
  125. package/packages/dd-trace/src/opentracing/propagation/text_map.js +9 -4
  126. package/packages/dd-trace/src/opentracing/tracer.js +9 -4
  127. package/packages/dd-trace/src/payload-tagging/config/index.js +6 -5
  128. package/packages/dd-trace/src/plugin_manager.js +8 -6
  129. package/packages/dd-trace/src/plugins/ci_plugin.js +4 -0
  130. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  131. package/packages/dd-trace/src/plugins/plugin.js +11 -13
  132. package/packages/dd-trace/src/plugins/storage.js +2 -2
  133. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  134. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  135. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  136. package/packages/dd-trace/src/process-tags/index.js +3 -0
  137. package/packages/dd-trace/src/profiler.js +27 -2
  138. package/packages/dd-trace/src/profiling/config.js +73 -241
  139. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -4
  140. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +6 -2
  141. package/packages/dd-trace/src/profiling/profiler.js +78 -109
  142. package/packages/dd-trace/src/profiling/profilers/events.js +2 -3
  143. package/packages/dd-trace/src/profiling/profilers/wall.js +89 -6
  144. package/packages/dd-trace/src/profiling/ssi-heuristics.js +4 -1
  145. package/packages/dd-trace/src/propagation-hash/index.js +2 -1
  146. package/packages/dd-trace/src/proxy.js +40 -6
  147. package/packages/dd-trace/src/remote_config/index.js +3 -0
  148. package/packages/dd-trace/src/require-package-json.js +8 -4
  149. package/packages/dd-trace/src/ritm.js +58 -26
  150. package/packages/dd-trace/src/runtime_metrics/index.js +3 -0
  151. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +18 -11
  152. package/packages/dd-trace/src/sampler.js +1 -1
  153. package/packages/dd-trace/src/service-naming/index.js +1 -1
  154. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  155. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  156. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  157. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  158. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
  159. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
  162. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  163. package/packages/dd-trace/src/span_stats.js +5 -1
  164. package/packages/dd-trace/src/standalone/index.js +3 -0
  165. package/packages/dd-trace/src/telemetry/index.js +2 -3
  166. package/packages/dd-trace/src/telemetry/send-data.js +5 -19
  167. package/packages/dd-trace/src/telemetry/session-propagation.js +19 -44
  168. package/packages/dd-trace/src/telemetry/telemetry.js +28 -171
  169. package/packages/dd-trace/src/tracer.js +2 -2
  170. package/packages/dd-trace/src/util.js +0 -9
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
@@ -1,5 +1,9 @@
1
1
  'use strict'
2
2
 
3
+ // Capture real timers at module load, before any test can install fake timers.
4
+ const { performance } = require('perf_hooks')
5
+ const dateNow = Date.now
6
+
3
7
  const {
4
8
  TEST_STATUS,
5
9
  TEST_IS_RUM_ACTIVE,
@@ -53,6 +57,7 @@ const {
53
57
  DYNAMIC_NAME_RE,
54
58
  logDynamicNamesWarning,
55
59
  getPullRequestBaseBranch,
60
+ TEST_FINAL_STATUS,
56
61
  } = require('../../dd-trace/src/plugins/util/test')
57
62
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
58
63
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
@@ -240,6 +245,58 @@ function getSuiteStatus (suiteStats) {
240
245
  return 'pass'
241
246
  }
242
247
 
248
+ const FINAL_STATUS_RETRY_KIND = {
249
+ none: 'none',
250
+ atr: 'atr',
251
+ efd: 'efd',
252
+ atf: 'atf',
253
+ }
254
+
255
+ function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTestRetriesCount }) {
256
+ // Infer retry kind from the executions we actually saw so ATR enabled with
257
+ // a retry count of 0 is still treated as a single final execution.
258
+ if (finishedTest.isAttemptToFix) {
259
+ return FINAL_STATUS_RETRY_KIND.atf
260
+ }
261
+
262
+ if (finishedTestAttempts.some(testAttempt => testAttempt.isEfdRetry)) {
263
+ return FINAL_STATUS_RETRY_KIND.efd
264
+ }
265
+
266
+ if (finishedTestAttempts.length > 1 && flakyTestRetriesCount > 0) {
267
+ return FINAL_STATUS_RETRY_KIND.atr
268
+ }
269
+
270
+ return FINAL_STATUS_RETRY_KIND.none
271
+ }
272
+
273
+ function getFinalStatus ({
274
+ status,
275
+ retryKind,
276
+ hasFailedAllRetries,
277
+ hasPassedAllAtfRetries,
278
+ isQuarantined,
279
+ isDisabled,
280
+ }) {
281
+ // If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
282
+ // the final status of its last execution should be reported as 'skip'.
283
+ if (isQuarantined || isDisabled || status === 'skip') {
284
+ return 'skip'
285
+ }
286
+
287
+ switch (retryKind) {
288
+ case FINAL_STATUS_RETRY_KIND.atr:
289
+ case FINAL_STATUS_RETRY_KIND.efd:
290
+ // These modes report the aggregate result across attempts.
291
+ return hasFailedAllRetries ? 'fail' : 'pass'
292
+ case FINAL_STATUS_RETRY_KIND.atf:
293
+ // Attempt-to-fix only passes if every execution passed.
294
+ return hasPassedAllAtfRetries ? 'pass' : 'fail'
295
+ default:
296
+ return status
297
+ }
298
+ }
299
+
243
300
  class CypressPlugin {
244
301
  _isInit = false
245
302
  testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
@@ -307,10 +364,68 @@ class CypressPlugin {
307
364
  }
308
365
  }
309
366
 
367
+ /**
368
+ * Resets state that is scoped to a single Cypress run so the singleton plugin
369
+ * can be reused safely across multiple programmatic cypress.run() calls.
370
+ *
371
+ * @returns {void}
372
+ */
373
+ resetRunState () {
374
+ this._isInit = false
375
+ this.finishedTestsByFile = {}
376
+ this.testStatuses = {}
377
+ this.isTestsSkipped = false
378
+ this.isSuitesSkippingEnabled = false
379
+ this.isCodeCoverageEnabled = false
380
+ this.isFlakyTestRetriesEnabled = false
381
+ this.flakyTestRetriesCount = 0
382
+ this.isEarlyFlakeDetectionEnabled = false
383
+ this.isKnownTestsEnabled = false
384
+ this.earlyFlakeDetectionNumRetries = 0
385
+ this.testsToSkip = []
386
+ this.skippedTests = []
387
+ this.hasForcedToRunSuites = false
388
+ this.hasUnskippableSuites = false
389
+ this.unskippableSuites = []
390
+ this.knownTests = []
391
+ this.knownTestsByTestSuite = undefined
392
+ this.isTestManagementTestsEnabled = false
393
+ this.testManagementAttemptToFixRetries = 0
394
+ this.testManagementTests = undefined
395
+ this.isImpactedTestsEnabled = false
396
+ this.modifiedFiles = []
397
+ this.activeTestSpan = null
398
+ this.testSuiteSpan = null
399
+ this.testModuleSpan = null
400
+ this.testSessionSpan = null
401
+ this.command = undefined
402
+ this.frameworkVersion = undefined
403
+ this.rootDir = undefined
404
+ this.itrCorrelationId = undefined
405
+ this.isTestIsolationEnabled = undefined
406
+ this.rumFlushWaitMillis = undefined
407
+ this._pendingRequestErrorTags = []
408
+ this.libraryConfigurationPromise = undefined
409
+ this._timeOrigin = 0
410
+ this._perfOrigin = 0
411
+ }
412
+
413
+ /**
414
+ * Returns the current time in the same coordinate system used by span
415
+ * start/finish. Captured at session span creation so it shares the same
416
+ * epoch as the trace without reaching into span internals.
417
+ *
418
+ * @returns {number}
419
+ */
420
+ _now () {
421
+ return this._timeOrigin + performance.now() - this._perfOrigin
422
+ }
423
+
310
424
  // Init function returns a promise that resolves with the Cypress configuration
311
425
  // Depending on the received configuration, the Cypress configuration can be modified:
312
426
  // for example, to enable retries for failed tests.
313
427
  init (tracer, cypressConfig) {
428
+ this.resetRunState()
314
429
  this._isInit = true
315
430
  this.tracer = tracer
316
431
  this.cypressConfig = cypressConfig
@@ -620,6 +735,13 @@ class CypressPlugin {
620
735
  this.tracer._tracer._exporter.addMetadataTags(metadataTags)
621
736
  }
622
737
 
738
+ // Capture time references that match what startSpan records internally
739
+ // (trace.startTime = Date.now(), trace.ticks = performance.now()).
740
+ // This lets _now() produce values in the same coordinate system as
741
+ // span._startTime without accessing span internals.
742
+ this._timeOrigin = dateNow()
743
+ this._perfOrigin = performance.now()
744
+
623
745
  this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
624
746
  childOf,
625
747
  tags: {
@@ -694,20 +816,27 @@ class CypressPlugin {
694
816
  }
695
817
 
696
818
  return new Promise(resolve => {
819
+ const finishAfterRun = () => {
820
+ this._isInit = false
821
+ appClosingTelemetry()
822
+ resolve(null)
823
+ }
824
+
697
825
  const exporter = this.tracer._tracer._exporter
698
826
  if (!exporter) {
699
- return resolve(null)
827
+ finishAfterRun()
828
+ return
700
829
  }
701
830
  if (exporter.flush) {
702
831
  exporter.flush(() => {
703
- appClosingTelemetry()
704
- resolve(null)
832
+ finishAfterRun()
705
833
  })
706
834
  } else if (exporter._writer) {
707
835
  exporter._writer.flush(() => {
708
- appClosingTelemetry()
709
- resolve(null)
836
+ finishAfterRun()
710
837
  })
838
+ } else {
839
+ finishAfterRun()
711
840
  }
712
841
  })
713
842
  }
@@ -745,6 +874,7 @@ class CypressPlugin {
745
874
  : spec.relative
746
875
 
747
876
  const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
877
+ skippedTestSpan.setTag(TEST_FINAL_STATUS, 'skip')
748
878
 
749
879
  skippedTestSpan.setTag(TEST_STATUS, 'skip')
750
880
  if (isSkippedByItr) {
@@ -834,6 +964,36 @@ class CypressPlugin {
834
964
  finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
835
965
  }
836
966
 
967
+ // We can check if this is the last attempt regardless of the retry mechanism
968
+ const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
969
+ if (isLastAttempt) {
970
+ const testSpanTags = finishedTest.testSpan.context()._tags
971
+ const retryKind = getFinalStatusRetryKind({
972
+ finishedTest,
973
+ finishedTestAttempts,
974
+ flakyTestRetriesCount: this.flakyTestRetriesCount,
975
+ })
976
+
977
+ const hasFailedAllRetries = testSpanTags[TEST_HAS_FAILED_ALL_RETRIES] === 'true'
978
+ const hasPassedAllAtfRetries =
979
+ testSpanTags[TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED] === 'true'
980
+ const isQuarantined = testSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true'
981
+ const isDisabled = testSpanTags[TEST_MANAGEMENT_IS_DISABLED] === 'true'
982
+
983
+ const finalStatus = getFinalStatus({
984
+ status: cypressTestStatus,
985
+ retryKind,
986
+ hasFailedAllRetries,
987
+ hasPassedAllAtfRetries,
988
+ isQuarantined,
989
+ isDisabled,
990
+ })
991
+
992
+ if (finalStatus) {
993
+ finishedTest.testSpan.setTag(TEST_FINAL_STATUS, finalStatus)
994
+ }
995
+ }
996
+
837
997
  finishedTest.testSpan.finish(finishedTest.finishTime)
838
998
  }
839
999
  }
@@ -1051,7 +1211,7 @@ class CypressPlugin {
1051
1211
  const finishedTest = {
1052
1212
  testName,
1053
1213
  testStatus,
1054
- finishTime: this.activeTestSpan._getTime(), // we store the finish time here
1214
+ finishTime: this._now(),
1055
1215
  testSpan: this.activeTestSpan,
1056
1216
  isEfdRetry,
1057
1217
  isAttemptToFix,
@@ -1,11 +1,68 @@
1
1
  'use strict'
2
2
 
3
+ const NoopTracer = require('../../dd-trace/src/noop/tracer')
3
4
  const Plugin = require('../../dd-trace/src/plugins/plugin')
4
5
 
5
- // Cypress plugin does not patch any library. This is just a placeholder to
6
- // follow the structure of the plugins
6
+ /**
7
+ * Cypress plugin handles setup-node-events from the instrumentation layer
8
+ * via a diagnostic channel, keeping the instrumentation free of tracer references.
9
+ */
7
10
  class CypressPlugin extends Plugin {
8
11
  static id = 'cypress'
12
+
13
+ constructor (...args) {
14
+ super(...args)
15
+
16
+ this.addSub('ci:cypress:setup-node-events', (payload) => {
17
+ // Bail out if the tracer failed to init (e.g. invalid DD_SITE).
18
+ // Mirrors the guard in the manual plugin entrypoint (plugin.js).
19
+ if (this._tracer._tracer instanceof NoopTracer) return
20
+
21
+ const { on, config, userAfterSpecHandlers, userAfterRunHandlers, cleanupWrapper } = payload
22
+
23
+ const registerAfterRunWithCleanup = (afterRunHandler) => {
24
+ on('after:run', (results) => {
25
+ const chain = userAfterRunHandlers.reduce(
26
+ (p, h) => p.then(() => h(results)),
27
+ Promise.resolve()
28
+ )
29
+ if (afterRunHandler) {
30
+ return chain.then(() => afterRunHandler(results)).finally(cleanupWrapper)
31
+ }
32
+ return chain.finally(cleanupWrapper)
33
+ })
34
+ }
35
+
36
+ const cypressPlugin = require('./cypress-plugin')
37
+
38
+ if (cypressPlugin._isInit) {
39
+ // Already initialized by manual plugin call — just chain user handlers
40
+ for (const h of userAfterSpecHandlers) on('after:spec', h)
41
+ registerAfterRunWithCleanup()
42
+ payload.registered = true
43
+ return
44
+ }
45
+
46
+ on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))
47
+
48
+ on('after:spec', (spec, results) => {
49
+ const chain = userAfterSpecHandlers.reduce(
50
+ (p, h) => p.then(() => h(spec, results)),
51
+ Promise.resolve()
52
+ )
53
+ return chain.then(() => cypressPlugin.afterSpec(spec, results))
54
+ })
55
+
56
+ registerAfterRunWithCleanup((results) => cypressPlugin.afterRun(results))
57
+
58
+ on('task', cypressPlugin.getTasks())
59
+
60
+ payload.registered = true
61
+ // cypressPlugin.init expects the proxy tracer (with ._tracer._exporter),
62
+ // not the unwrapped internal tracer that this.tracer returns.
63
+ payload.configPromise = Promise.resolve(cypressPlugin.init(this._tracer, config)).then(() => config)
64
+ })
65
+ }
9
66
  }
10
67
 
11
68
  module.exports = CypressPlugin
@@ -11,7 +11,7 @@ class FsPlugin extends TracingPlugin {
11
11
  }
12
12
 
13
13
  bindStart (ctx) {
14
- if (!this.activeSpan) return this.skip()
14
+ if (!this.activeSpan) return { noop: true }
15
15
 
16
16
  const { operation, ...params } = ctx
17
17
  const lowerOp = operation.toLowerCase()
@@ -123,7 +123,8 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
123
123
  const topicName = topic?.slice(topic.lastIndexOf('/') + 1) ??
124
124
  subscription.name.slice(subscription.name.lastIndexOf('/') + 1)
125
125
  const baseService = this.tracer._service || 'unknown'
126
- const serviceName = this.config.service || `${baseService}-pubsub`
126
+ const serviceName = this.config.service || { name: `${baseService}-pubsub`, source: baseService }
127
+
127
128
  const meta = {
128
129
  'gcloud.project_id': subscription.pubsub.projectId,
129
130
  'pubsub.topic': topic,
@@ -145,17 +145,12 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
145
145
  const subscriptionName = subscription?.slice(subscription.lastIndexOf('/') + 1) ?? subscription
146
146
  const publishStartTime = attrs['x-dd-publish-start-time']
147
147
  const startTime = publishStartTime ? Number.parseInt(publishStartTime, 10) : undefined
148
-
149
- // Get the base service name and construct the pubsub service override
150
- const baseService = this.tracer._service
151
- const serviceOverride = this.config.service ?? `${baseService}-pubsub`
152
-
153
148
  // Use this.startSpan() which automatically activates the span
154
149
  const span = this.startSpan('pubsub.push.receive', {
155
150
  childOf: parentContext,
156
151
  startTime,
157
152
  kind: 'consumer',
158
- service: serviceOverride,
153
+ service: this.config.service || { name: `${this.tracer._service}-pubsub`, source: this.tracer._service },
159
154
  meta: {
160
155
  component: 'google-cloud-pubsub',
161
156
  'pubsub.method': 'receive',
@@ -163,7 +158,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
163
158
  'pubsub.message_id': message.messageId,
164
159
  'pubsub.subscription_type': 'push',
165
160
  'pubsub.topic': topicName,
166
- '_dd.base_service': baseService,
161
+ '_dd.base_service': this.tracer._service,
167
162
  '_dd.serviceoverride.type': 'integration',
168
163
  'resource.name': `Push Subscription ${subscriptionName}`,
169
164
  },
@@ -28,7 +28,7 @@ class GraphQLResolvePlugin extends TracingPlugin {
28
28
  if (rootCtx.fields[computedPathString]) return
29
29
 
30
30
  if (!rootCtx[collapsedPathSym]) {
31
- rootCtx[collapsedPathSym] = {}
31
+ rootCtx[collapsedPathSym] = Object.create(null)
32
32
  } else if (rootCtx[collapsedPathSym][computedPathString]) {
33
33
  return
34
34
  }
@@ -40,10 +40,10 @@ class HttpClientPlugin extends ClientPlugin {
40
40
  const span = this.startSpan(this.operationName(), {
41
41
  childOf,
42
42
  integrationName: this.constructor.id,
43
+ service: this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
43
44
  meta: {
44
45
  [COMPONENT]: this.constructor.id,
45
46
  'span.kind': 'client',
46
- 'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
47
47
  'resource.name': method,
48
48
  'span.type': 'http',
49
49
  'http.method': method,
@@ -4,7 +4,7 @@ const ServerPlugin = require('../../dd-trace/src/plugins/server')
4
4
  const { storage } = require('../../datadog-core')
5
5
  const web = require('../../dd-trace/src/plugins/util/web')
6
6
  const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/channels')
7
- const { COMPONENT } = require('../../dd-trace/src/constants')
7
+ const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
8
8
 
9
9
  class HttpServerPlugin extends ServerPlugin {
10
10
  static id = 'http'
@@ -18,16 +18,24 @@ class HttpServerPlugin extends ServerPlugin {
18
18
 
19
19
  start ({ req, res, abortController }) {
20
20
  let store = storage('legacy').getStore()
21
+ const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
22
+ const service = this.config.service || schemaServiceName
23
+ const serviceSource = (this.config.service && service !== this.tracer._service)
24
+ ? 'opt.plugin'
25
+ : (service === this.tracer._service ? undefined : schemaServiceSource)
21
26
  const span = web.startSpan(
22
27
  this.tracer,
23
28
  {
24
29
  ...this.config,
25
- service: this.config.service || this.serviceName(),
30
+ service,
26
31
  },
27
32
  req,
28
33
  res,
29
34
  this.operationName()
30
35
  )
36
+ if (serviceSource !== undefined) {
37
+ span.setTag(SVC_SRC_KEY, serviceSource)
38
+ }
31
39
  span.setTag(COMPONENT, this.constructor.id)
32
40
  span._integrationName = this.constructor.id
33
41
 
@@ -41,10 +41,10 @@ class Http2ClientPlugin extends ClientPlugin {
41
41
  const span = this.startSpan(this.operationName(), {
42
42
  childOf,
43
43
  integrationName: this.constructor.id,
44
+ service: this.serviceName({ pluginConfig: this.config, sessionDetails }),
44
45
  meta: {
45
46
  [COMPONENT]: this.constructor.id,
46
47
  [SPAN_KIND]: CLIENT,
47
- 'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails }),
48
48
  'resource.name': method,
49
49
  'span.type': 'http',
50
50
  'http.method': method,
@@ -4,7 +4,7 @@
4
4
 
5
5
  const ServerPlugin = require('../../dd-trace/src/plugins/server')
6
6
  const web = require('../../dd-trace/src/plugins/util/web')
7
- const { COMPONENT } = require('../../dd-trace/src/constants')
7
+ const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
8
8
 
9
9
  class Http2ServerPlugin extends ServerPlugin {
10
10
  constructor (tracer, config) {
@@ -19,17 +19,25 @@ class Http2ServerPlugin extends ServerPlugin {
19
19
  bindStart (ctx) {
20
20
  const { req, res } = ctx
21
21
 
22
+ const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
23
+ const service = this.config.service || schemaServiceName
24
+ const serviceSource = (this.config.service && service !== this.tracer._service)
25
+ ? 'opt.plugin'
26
+ : (service === this.tracer._service ? undefined : schemaServiceSource)
22
27
  const span = web.startSpan(
23
28
  this.tracer,
24
29
  {
25
30
  ...this.config,
26
- service: this.config.service || this.serviceName(),
31
+ service,
27
32
  },
28
33
  req,
29
34
  res,
30
35
  this.operationName(),
31
36
  ctx
32
37
  )
38
+ if (serviceSource !== undefined) {
39
+ span.setTag(SVC_SRC_KEY, serviceSource)
40
+ }
33
41
 
34
42
  span.setTag(COMPONENT, this.constructor.id)
35
43
  span._integrationName = this.constructor.id
@@ -1,5 +1,8 @@
1
1
  'use strict'
2
2
 
3
+ // Capture real timers at module load time, before any test can install fake timers.
4
+ const realSetTimeout = setTimeout
5
+
3
6
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
4
7
  const { storage } = require('../../datadog-core')
5
8
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
@@ -60,8 +63,7 @@ const CHILD_MESSAGE_END = 2
60
63
 
61
64
  function withTimeout (promise, timeoutMs) {
62
65
  return new Promise(resolve => {
63
- // Set a timeout to resolve after 1s
64
- setTimeout(resolve, timeoutMs)
66
+ realSetTimeout(resolve, timeoutMs)
65
67
 
66
68
  // Also resolve if the original promise resolves
67
69
  promise.then(resolve)
@@ -8,20 +8,47 @@ class KafkajsBatchConsumerPlugin extends ConsumerPlugin {
8
8
  static id = 'kafkajs'
9
9
  static operation = 'consume-batch'
10
10
 
11
- start (ctx) {
12
- const { topic, messages, groupId, clusterId } = ctx.extractedArgs || ctx
11
+ bindStart (ctx) {
12
+ const { topic, partition, messages, groupId, clusterId } = ctx.extractedArgs || ctx
13
+
14
+ const span = this.startSpan({
15
+ resource: topic,
16
+ type: 'worker',
17
+ meta: {
18
+ component: this.constructor.id,
19
+ 'kafka.topic': topic,
20
+ 'kafka.cluster_id': clusterId,
21
+ 'messaging.destination.name': topic,
22
+ 'messaging.system': 'kafka',
23
+ },
24
+ metrics: {
25
+ 'kafka.partition': partition,
26
+ 'messaging.batch.message_count': messages.length,
27
+ },
28
+ }, ctx)
13
29
 
14
- if (!this.config.dsmEnabled) return
15
30
  for (const message of messages) {
16
31
  if (!message || !message.headers) continue
32
+
33
+ const headers = convertToTextMap(message.headers)
34
+ if (headers) {
35
+ const childOf = this.tracer.extract('text_map', headers)
36
+ if (childOf) {
37
+ span.addLink(childOf)
38
+ }
39
+ }
40
+
41
+ if (!this.config.dsmEnabled) continue
17
42
  const payloadSize = getMessageSize(message)
18
- this.tracer.decodeDataStreamsContext(convertToTextMap(message.headers))
43
+ this.tracer.decodeDataStreamsContext(headers)
19
44
  const edgeTags = ['direction:in', `group:${groupId}`, `topic:${topic}`, 'type:kafka']
20
45
  if (clusterId) {
21
46
  edgeTags.push(`kafka_cluster_id:${clusterId}`)
22
47
  }
23
48
  this.tracer.setCheckpoint(edgeTags, null, payloadSize)
24
49
  }
50
+
51
+ return ctx.currentStore
25
52
  }
26
53
  }
27
54
 
@@ -1,5 +1,8 @@
1
1
  'use strict'
2
2
 
3
+ // Capture real Date.now at module load time, before any test can install fake timers.
4
+ const realDateNow = Date.now.bind(Date)
5
+
3
6
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
4
7
  const { storage } = require('../../datadog-core')
5
8
  const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
@@ -323,8 +326,8 @@ class MochaPlugin extends CiPlugin {
323
326
  this.runningTestProbe = { file, line }
324
327
  this.testErrorStackIndex = stackIndex
325
328
  test._ddShouldWaitForHitProbe = true
326
- const waitUntil = Date.now() + BREAKPOINT_SET_GRACE_PERIOD_MS
327
- while (Date.now() < waitUntil) {
329
+ const waitUntil = realDateNow() + BREAKPOINT_SET_GRACE_PERIOD_MS
330
+ while (realDateNow() < waitUntil) {
328
331
  // TODO: To avoid a race condition, we should wait until `probeInformation.setProbePromise` has resolved.
329
332
  // However, Mocha doesn't have a mechanism for waiting asyncrounously here, so for now, we'll have to
330
333
  // fall back to a fixed syncronous delay.
@@ -35,9 +35,9 @@ class MongodbCorePlugin extends DatabasePlugin {
35
35
  }
36
36
  const query = getQuery(ops)
37
37
  const resource = truncate(getResource(this, ns, query, name))
38
- const service = this.serviceName({ pluginConfig: this.config })
38
+ const serviceResult = this.serviceName({ pluginConfig: this.config })
39
39
  const span = this.startSpan(this.operationName(), {
40
- service,
40
+ service: serviceResult,
41
41
  resource,
42
42
  type: 'mongodb',
43
43
  kind: 'client',
@@ -49,7 +49,7 @@ class MongodbCorePlugin extends DatabasePlugin {
49
49
  'out.port': options.port,
50
50
  },
51
51
  }, ctx)
52
- const comment = this.injectDbmComment(span, ops.comment, service)
52
+ const comment = this.injectDbmComment(span, ops.comment, serviceResult.name)
53
53
  if (comment) {
54
54
  ops.comment = comment
55
55
  }
@@ -33,7 +33,7 @@ class MySQLPlugin extends DatabasePlugin {
33
33
  [CLIENT_PORT_KEY]: ctx.conf.port,
34
34
  },
35
35
  }, ctx)
36
- ctx.sql = this.injectDbmQuery(span, ctx.sql, service)
36
+ ctx.sql = this.injectDbmQuery(span, ctx.sql, service.name)
37
37
 
38
38
  return ctx.currentStore
39
39
  }
@@ -3,14 +3,13 @@
3
3
  const ServerPlugin = require('../../dd-trace/src/plugins/server')
4
4
  const { storage } = require('../../datadog-core')
5
5
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
6
- const { COMPONENT } = require('../../dd-trace/src/constants')
6
+ const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
7
7
  const web = require('../../dd-trace/src/plugins/util/web')
8
8
 
9
9
  const errorPages = new Set(['/404', '/500', '/_error', '/_not-found', '/_not-found/page'])
10
10
 
11
11
  class NextPlugin extends ServerPlugin {
12
12
  static id = 'next'
13
- #requestsBySpanId = new WeakMap()
14
13
 
15
14
  constructor (...args) {
16
15
  super(...args)
@@ -20,26 +19,28 @@ class NextPlugin extends ServerPlugin {
20
19
  bindStart ({ req, res }) {
21
20
  const store = storage('legacy').getStore()
22
21
  const childOf = store ? store.span : store
22
+ const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
23
+ const serviceName = this.config.service || schemaServiceName
24
+ let serviceSource = this.config.service ? 'opt.plugin' : schemaServiceSource
25
+ if (!serviceName || serviceName === this.tracer._service) serviceSource = undefined
26
+
23
27
  const span = this.tracer.startSpan(this.operationName(), {
24
28
  childOf,
25
29
  tags: {
26
30
  [COMPONENT]: this.constructor.id,
27
- 'service.name': this.config.service || this.serviceName(),
31
+ 'service.name': serviceName,
28
32
  'resource.name': req.method,
29
33
  'span.type': 'web',
30
34
  'span.kind': 'server',
31
35
  'http.method': req.method,
36
+ ...(serviceSource === undefined ? {} : { [SVC_SRC_KEY]: serviceSource }),
32
37
  },
33
38
  integrationName: this.constructor.id,
34
39
  })
35
40
 
36
41
  analyticsSampler.sample(span, this.config.measured, true)
37
42
 
38
- // Store request by span ID to handle cases where child spans are activated
39
- const spanId = span.context()._spanId
40
- this.#requestsBySpanId.set(spanId, req)
41
-
42
- return { ...store, span }
43
+ return { ...store, span, req }
43
44
  }
44
45
 
45
46
  error ({ span, error }) {
@@ -90,14 +91,7 @@ class NextPlugin extends ServerPlugin {
90
91
 
91
92
  if (!store) return
92
93
 
93
- const span = store.span
94
-
95
- const spanId = span.context()._spanId
96
- const parentSpanId = span.context()._parentId
97
-
98
- // Try current span first, then parent span.
99
- // This handles cases where pageLoad runs in a child span context
100
- const req = this.#requestsBySpanId.get(spanId) ?? this.#requestsBySpanId.get(parentSpanId)
94
+ const { span, req } = store
101
95
 
102
96
  // safeguard against missing req in complicated timeout scenarios
103
97
  if (!req) return
@@ -20,6 +20,7 @@ module.exports.init = function (tracerConfig) {
20
20
  `env:${tracerConfig.tags.env}`,
21
21
  `version:${tracerConfig.tags.version}`,
22
22
  ],
23
+ lookup: tracerConfig.lookup,
23
24
  })
24
25
  : new NoopDogStatsDClient()
25
26