dd-trace 5.99.1 → 5.101.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 (101) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/index.d.ts +14 -0
  3. package/package.json +8 -8
  4. package/packages/datadog-instrumentations/src/cucumber.js +69 -5
  5. package/packages/datadog-instrumentations/src/cypress.js +5 -3
  6. package/packages/datadog-instrumentations/src/express.js +3 -2
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/hono.js +15 -4
  9. package/packages/datadog-instrumentations/src/http/client.js +20 -3
  10. package/packages/datadog-instrumentations/src/jest.js +146 -90
  11. package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
  12. package/packages/datadog-instrumentations/src/mocha/main.js +43 -26
  13. package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
  14. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -4
  15. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
  16. package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
  17. package/packages/datadog-instrumentations/src/playwright.js +108 -18
  18. package/packages/datadog-instrumentations/src/router.js +53 -33
  19. package/packages/datadog-instrumentations/src/vitest.js +76 -30
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
  21. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  22. package/packages/datadog-plugin-bullmq/src/consumer.js +5 -4
  23. package/packages/datadog-plugin-bullmq/src/producer.js +37 -29
  24. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +49 -9
  25. package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
  26. package/packages/datadog-plugin-cypress/src/support.js +22 -21
  27. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  28. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  29. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
  30. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
  31. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
  32. package/packages/datadog-plugin-playwright/src/index.js +6 -0
  33. package/packages/datadog-plugin-router/src/index.js +13 -0
  34. package/packages/dd-trace/index.js +4 -3
  35. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  36. package/packages/dd-trace/src/appsec/reporter.js +4 -1
  37. package/packages/dd-trace/src/baggage.js +10 -0
  38. package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
  39. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
  40. package/packages/dd-trace/src/config/config-types.d.ts +0 -2
  41. package/packages/dd-trace/src/config/generated-config-types.d.ts +17 -41
  42. package/packages/dd-trace/src/config/index.js +7 -60
  43. package/packages/dd-trace/src/config/normalize-service.js +31 -0
  44. package/packages/dd-trace/src/config/supported-configurations.json +15 -32
  45. package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
  46. package/packages/dd-trace/src/datastreams/encoding.js +39 -28
  47. package/packages/dd-trace/src/datastreams/pathway.js +29 -26
  48. package/packages/dd-trace/src/datastreams/processor.js +17 -15
  49. package/packages/dd-trace/src/datastreams/size.js +6 -2
  50. package/packages/dd-trace/src/debugger/config.js +6 -3
  51. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
  52. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  53. package/packages/dd-trace/src/dogstatsd.js +10 -7
  54. package/packages/dd-trace/src/encode/0.4.js +3 -3
  55. package/packages/dd-trace/src/encode/0.5.js +2 -2
  56. package/packages/dd-trace/src/encode/agentless-json.js +2 -2
  57. package/packages/dd-trace/src/encode/tags-processors.js +2 -27
  58. package/packages/dd-trace/src/exporters/common/request.js +22 -11
  59. package/packages/dd-trace/src/exporters/common/retry.js +104 -0
  60. package/packages/dd-trace/src/git_metadata.js +66 -0
  61. package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
  62. package/packages/dd-trace/src/heap_snapshots.js +4 -4
  63. package/packages/dd-trace/src/id.js +15 -26
  64. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  65. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
  66. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
  67. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
  68. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
  69. package/packages/dd-trace/src/llmobs/sdk.js +5 -1
  70. package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
  71. package/packages/dd-trace/src/llmobs/tagger.js +42 -0
  72. package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
  73. package/packages/dd-trace/src/llmobs/util.js +80 -5
  74. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
  75. package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
  76. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
  77. package/packages/dd-trace/src/opentelemetry/context_manager.js +22 -10
  78. package/packages/dd-trace/src/opentelemetry/span-helpers.js +308 -0
  79. package/packages/dd-trace/src/opentelemetry/span.js +42 -108
  80. package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
  81. package/packages/dd-trace/src/opentracing/propagation/text_map.js +95 -36
  82. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +98 -32
  83. package/packages/dd-trace/src/opentracing/span.js +58 -49
  84. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  85. package/packages/dd-trace/src/plugins/util/ci.js +119 -32
  86. package/packages/dd-trace/src/plugins/util/test.js +293 -27
  87. package/packages/dd-trace/src/priority_sampler.js +6 -4
  88. package/packages/dd-trace/src/profiling/config.js +5 -4
  89. package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
  90. package/packages/dd-trace/src/propagation-hash/index.js +1 -1
  91. package/packages/dd-trace/src/proxy.js +3 -3
  92. package/packages/dd-trace/src/remote_config/index.js +5 -3
  93. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  94. package/packages/dd-trace/src/span_format.js +52 -5
  95. package/packages/dd-trace/src/span_processor.js +1 -5
  96. package/packages/dd-trace/src/spanleak.js +0 -1
  97. package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
  98. package/packages/dd-trace/src/tracer_metadata.js +1 -1
  99. package/packages/dd-trace/src/util.js +17 -0
  100. package/vendor/dist/path-to-regexp/LICENSE +0 -21
  101. package/vendor/dist/path-to-regexp/index.js +0 -1
@@ -217,6 +217,8 @@ const TEST_MANAGEMENT_IS_QUARANTINED = 'test.test_management.is_quarantined'
217
217
  const TEST_MANAGEMENT_ENABLED = 'test.test_management.enabled'
218
218
  const TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED = 'test.test_management.attempt_to_fix_passed'
219
219
 
220
+ const MAX_TEST_OPTIMIZATION_SUMMARY_ITEMS = 10
221
+
220
222
  // Impacted tests
221
223
  const POSSIBLE_BASE_BRANCHES = ['main', 'master', 'preprod', 'prod', 'dev', 'development', 'trunk']
222
224
  const BASE_LIKE_BRANCH_FILTER = /^(main|master|preprod|prod|dev|development|trunk|release\/.*|hotfix\/.*)$/
@@ -358,7 +360,14 @@ module.exports = {
358
360
  GIT_REPOSITORY_URL,
359
361
  DYNAMIC_NAME_RE,
360
362
  collectDynamicNamesFromTraces,
363
+ collectTestOptimizationSummariesFromTraces,
361
364
  logDynamicNamesWarning,
365
+ recordAttemptToFixExecution,
366
+ collectAttemptToFixExecutionsFromTraces,
367
+ formatAttemptToFixSummary,
368
+ logAttemptToFixTestExecution,
369
+ formatDynamicNamesSummary,
370
+ logTestOptimizationSummary,
362
371
  }
363
372
 
364
373
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -1327,23 +1336,149 @@ function getModifiedFilesFromDiff (diff) {
1327
1336
  }
1328
1337
 
1329
1338
  /**
1330
- * Scans serialized worker trace payloads for tests tagged with TEST_HAS_DYNAMIC_NAME
1331
- * and populates the provided Set. Silently ignores parse errors.
1339
+ * @typedef {object} AttemptToFixExecutionResult
1340
+ * @property {string} name
1341
+ * @property {number} executions
1342
+ * @property {number} failedCount
1343
+ * @property {boolean} isDisabled
1344
+ * @property {boolean} isQuarantined
1345
+ */
1346
+
1347
+ /**
1348
+ * @typedef {Map<string, AttemptToFixExecutionResult>} AttemptToFixExecutions
1349
+ */
1350
+
1351
+ /**
1352
+ * Formats a test name for user-facing Test Optimization summaries.
1353
+ *
1354
+ * @param {string | undefined} testSuite
1355
+ * @param {string} testName
1356
+ * @returns {string}
1357
+ */
1358
+ function formatTestOptimizationName (testSuite, testName) {
1359
+ return testSuite ? `${testSuite} › ${testName}` : testName
1360
+ }
1361
+
1362
+ /**
1363
+ * Renders a bounded bullet list for Test Optimization summaries.
1364
+ *
1365
+ * @param {Array<{ text: string, suffix?: string }>} items
1366
+ * @returns {string}
1367
+ */
1368
+ function formatTestOptimizationList (items) {
1369
+ const shown = items.slice(0, MAX_TEST_OPTIMIZATION_SUMMARY_ITEMS)
1370
+ const more = items.length - shown.length
1371
+ const moreSuffix = more > 0 ? `\n ... and ${more} more` : ''
1372
+
1373
+ return shown.map(({ text, suffix }) => ` • ${text}${suffix ? ` (${suffix})` : ''}`).join('\n') + moreSuffix
1374
+ }
1375
+
1376
+ /**
1377
+ * Logs a compact message when an attempt-to-fix test execution starts.
1378
+ *
1379
+ * @param {string | undefined} testSuite
1380
+ * @param {string} testName
1381
+ * @param {Set<string>} [loggedAttemptToFixTests]
1382
+ */
1383
+ function logAttemptToFixTestExecution (testSuite, testName, loggedAttemptToFixTests) {
1384
+ if (!testName) return
1385
+
1386
+ const name = formatTestOptimizationName(testSuite, testName)
1387
+ if (loggedAttemptToFixTests) {
1388
+ if (loggedAttemptToFixTests.has(name)) return
1389
+
1390
+ loggedAttemptToFixTests.add(name)
1391
+ }
1392
+
1393
+ // eslint-disable-next-line no-console -- Intentional user-facing attempt-to-fix progress report
1394
+ console.warn(`Datadog Test Optimization: attempting to fix ${name}`)
1395
+ }
1396
+
1397
+ /**
1398
+ * Records a single attempt-to-fix execution for the end-of-session user summary.
1399
+ *
1400
+ * @param {AttemptToFixExecutions} attemptToFixExecutions
1401
+ * @param {{
1402
+ * testSuite?: string,
1403
+ * testName: string,
1404
+ * status: string,
1405
+ * isDisabled?: boolean,
1406
+ * isQuarantined?: boolean
1407
+ * }} execution
1408
+ */
1409
+ function recordAttemptToFixExecution (attemptToFixExecutions, execution) {
1410
+ if (!execution?.testName) return
1411
+
1412
+ const { testSuite, testName, status, isDisabled, isQuarantined } = execution
1413
+ const name = formatTestOptimizationName(testSuite, testName)
1414
+ let result = attemptToFixExecutions.get(name)
1415
+
1416
+ if (!result) {
1417
+ result = {
1418
+ name,
1419
+ executions: 0,
1420
+ failedCount: 0,
1421
+ isDisabled: false,
1422
+ isQuarantined: false,
1423
+ }
1424
+ attemptToFixExecutions.set(name, result)
1425
+ }
1426
+
1427
+ result.executions++
1428
+ result.isDisabled = result.isDisabled || !!isDisabled
1429
+ result.isQuarantined = result.isQuarantined || !!isQuarantined
1430
+
1431
+ if (status === 'fail') {
1432
+ result.failedCount++
1433
+ }
1434
+ }
1435
+
1436
+ function collectDynamicNameFromTraceSpan (span, newTestsWithDynamicNames) {
1437
+ const meta = span.meta
1438
+ if (meta?.[TEST_HAS_DYNAMIC_NAME] !== 'true') return
1439
+
1440
+ const suite = meta[TEST_SUITE]
1441
+ const name = meta[TEST_NAME]
1442
+ if (suite && name) {
1443
+ newTestsWithDynamicNames.add(`${suite} › ${name}`)
1444
+ }
1445
+ }
1446
+
1447
+ function collectAttemptToFixExecutionFromTraceSpan (span, attemptToFixExecutions) {
1448
+ const meta = span.meta
1449
+ if (meta?.[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX] !== 'true') return
1450
+
1451
+ recordAttemptToFixExecution(attemptToFixExecutions, {
1452
+ testSuite: meta[TEST_SUITE],
1453
+ testName: meta[TEST_NAME],
1454
+ status: meta[TEST_STATUS],
1455
+ isDisabled: meta[TEST_MANAGEMENT_IS_DISABLED] === 'true',
1456
+ isQuarantined: meta[TEST_MANAGEMENT_IS_QUARANTINED] === 'true',
1457
+ })
1458
+ }
1459
+
1460
+ /**
1461
+ * Scans serialized worker trace payloads and populates Test Optimization summary data.
1462
+ * Silently ignores parse errors.
1332
1463
  *
1333
1464
  * @param {string} data - JSON-serialized traces from a worker
1334
- * @param {Set<string>} newTestsWithDynamicNames - Set to populate with "suite › name" strings
1465
+ * @param {{
1466
+ * newTestsWithDynamicNames?: Set<string>,
1467
+ * attemptToFixExecutions?: AttemptToFixExecutions
1468
+ * }} summaries
1335
1469
  */
1336
- function collectDynamicNamesFromTraces (data, newTestsWithDynamicNames) {
1470
+ function collectTestOptimizationSummariesFromTraces (data, summaries) {
1471
+ const { newTestsWithDynamicNames, attemptToFixExecutions } = summaries
1472
+
1337
1473
  try {
1338
1474
  const traces = JSON.parse(data)
1339
1475
  for (const trace of traces) {
1340
1476
  for (const span of trace) {
1341
- if (span.meta?.[TEST_HAS_DYNAMIC_NAME] === 'true') {
1342
- const suite = span.meta[TEST_SUITE]
1343
- const name = span.meta[TEST_NAME]
1344
- if (suite && name) {
1345
- newTestsWithDynamicNames.add(`${suite} › ${name}`)
1346
- }
1477
+ if (newTestsWithDynamicNames) {
1478
+ collectDynamicNameFromTraceSpan(span, newTestsWithDynamicNames)
1479
+ }
1480
+ if (attemptToFixExecutions) {
1481
+ collectAttemptToFixExecutionFromTraceSpan(span, attemptToFixExecutions)
1347
1482
  }
1348
1483
  }
1349
1484
  }
@@ -1353,33 +1488,164 @@ function collectDynamicNamesFromTraces (data, newTestsWithDynamicNames) {
1353
1488
  }
1354
1489
 
1355
1490
  /**
1356
- * Logs a "Datadog Test Optimization" warning about new tests with dynamic names.
1357
- * Clears the Set after logging. No-op if the Set is empty.
1491
+ * Scans serialized worker trace payloads for tests tagged with TEST_HAS_DYNAMIC_NAME
1492
+ * and populates the provided Set. Silently ignores parse errors.
1358
1493
  *
1359
- * @param {Set<string>} newTestsWithDynamicNames
1494
+ * @param {string} data - JSON-serialized traces from a worker
1495
+ * @param {Set<string>} newTestsWithDynamicNames - Set to populate with "suite › name" strings
1360
1496
  */
1361
- function logDynamicNamesWarning (newTestsWithDynamicNames) {
1362
- if (newTestsWithDynamicNames.size === 0) return
1497
+ function collectDynamicNamesFromTraces (data, newTestsWithDynamicNames) {
1498
+ collectTestOptimizationSummariesFromTraces(data, { newTestsWithDynamicNames })
1499
+ }
1363
1500
 
1364
- const MAX_SHOWN = 10
1365
- const names = [...newTestsWithDynamicNames]
1366
- const shown = names.slice(0, MAX_SHOWN)
1367
- const more = names.length - shown.length
1368
- const moreSuffix = more > 0 ? `\n ... and ${more} more` : ''
1369
- const nameList = shown.map(n => ` • ${n}`).join('\n') + moreSuffix
1501
+ /**
1502
+ * Scans serialized worker trace payloads for attempt-to-fix test spans.
1503
+ *
1504
+ * @param {string} data - JSON-serialized traces from a worker
1505
+ * @param {AttemptToFixExecutions} attemptToFixExecutions
1506
+ */
1507
+ function collectAttemptToFixExecutionsFromTraces (data, attemptToFixExecutions) {
1508
+ collectTestOptimizationSummariesFromTraces(data, { attemptToFixExecutions })
1509
+ }
1370
1510
 
1371
- const line = '-'.repeat(50)
1372
- // eslint-disable-next-line no-console -- Intentional user-facing session summary
1373
- console.warn(
1374
- `\n${line}\nDatadog Test Optimization\n${line}\n` +
1511
+ function getAttemptToFixManagementNotes (result) {
1512
+ const notes = []
1513
+
1514
+ if (result.isDisabled) {
1515
+ notes.push('Test was marked as disabled but was run because it is attempt to fix.')
1516
+ }
1517
+ if (result.isQuarantined) {
1518
+ notes.push('Test was marked as quarantined but was not quarantined because it is attempt to fix.')
1519
+ }
1520
+
1521
+ return notes
1522
+ }
1523
+
1524
+ function hasAttemptToFixManagementNotes (result) {
1525
+ return result.isDisabled || result.isQuarantined
1526
+ }
1527
+
1528
+ function addAttemptToFixResultLine (lines, result) {
1529
+ lines.push(` • ${result.name}`)
1530
+
1531
+ for (const note of getAttemptToFixManagementNotes(result)) {
1532
+ lines.push(` ${note}`)
1533
+ }
1534
+ }
1535
+
1536
+ /**
1537
+ * Formats the attempt-to-fix end-of-session summary.
1538
+ *
1539
+ * @param {AttemptToFixExecutions} attemptToFixExecutions
1540
+ * @returns {string}
1541
+ */
1542
+ function formatAttemptToFixSummary (attemptToFixExecutions) {
1543
+ if (attemptToFixExecutions.size === 0) return ''
1544
+
1545
+ const results = [...attemptToFixExecutions.values()]
1546
+ const failedResults = results.filter(result => result.failedCount > 0)
1547
+ const totalExecutions = results.reduce((total, result) => total + result.executions, 0)
1548
+
1549
+ if (failedResults.length === 0) {
1550
+ const lines = [
1551
+ `Attempt to fix passed: all ${totalExecutions} execution(s) passed for ${results.length} test(s).`,
1552
+ ]
1553
+
1554
+ for (const result of results) {
1555
+ if (hasAttemptToFixManagementNotes(result)) {
1556
+ addAttemptToFixResultLine(lines, result)
1557
+ }
1558
+ }
1559
+
1560
+ return lines.join('\n')
1561
+ }
1562
+
1563
+ const totalFailedExecutions = failedResults.reduce(
1564
+ (total, result) => total + result.failedCount,
1565
+ 0
1566
+ )
1567
+ const lines = [
1568
+ `Attempt to fix failed: ${totalFailedExecutions} of ${totalExecutions} execution(s) failed ` +
1569
+ `across ${failedResults.length} of ${results.length} test(s).`,
1570
+ ]
1571
+
1572
+ for (const result of failedResults) {
1573
+ addAttemptToFixResultLine(lines, result)
1574
+ }
1575
+ for (const result of results) {
1576
+ if (result.failedCount === 0 && hasAttemptToFixManagementNotes(result)) {
1577
+ addAttemptToFixResultLine(lines, result)
1578
+ }
1579
+ }
1580
+
1581
+ return lines.join('\n')
1582
+ }
1583
+
1584
+ /**
1585
+ * Formats the dynamic-name warning section of the Test Optimization summary.
1586
+ *
1587
+ * @param {Set<string>} newTestsWithDynamicNames
1588
+ * @returns {string}
1589
+ */
1590
+ function formatDynamicNamesSummary (newTestsWithDynamicNames) {
1591
+ if (newTestsWithDynamicNames.size === 0) return ''
1592
+
1593
+ const items = [...newTestsWithDynamicNames].map(name => ({ text: name }))
1594
+ return (
1375
1595
  `${newTestsWithDynamicNames.size} test(s) detected as new but their names contain ` +
1376
1596
  'dynamic data (timestamps, UUIDs, etc.).\n' +
1377
1597
  'Tests with changing names are always treated as new on every run, ' +
1378
1598
  'causing unnecessary Early Flake Detection retries and preventing correct new test detection.\n' +
1379
1599
  'Consider using stable, deterministic test names.\n\n' +
1380
- `${nameList}\n`
1600
+ formatTestOptimizationList(items)
1381
1601
  )
1382
- newTestsWithDynamicNames.clear()
1602
+ }
1603
+
1604
+ /**
1605
+ * Logs a single Test Optimization session summary.
1606
+ *
1607
+ * @param {{
1608
+ * attemptToFixExecutions?: AttemptToFixExecutions,
1609
+ * newTestsWithDynamicNames?: Set<string>,
1610
+ * extraSections?: string[]
1611
+ * }} summary
1612
+ */
1613
+ function logTestOptimizationSummary (summary) {
1614
+ const { attemptToFixExecutions, newTestsWithDynamicNames, extraSections = [] } = summary
1615
+ const sections = []
1616
+ const attemptToFixSummary = attemptToFixExecutions
1617
+ ? formatAttemptToFixSummary(attemptToFixExecutions)
1618
+ : ''
1619
+ const dynamicNamesSummary = newTestsWithDynamicNames
1620
+ ? formatDynamicNamesSummary(newTestsWithDynamicNames)
1621
+ : ''
1622
+
1623
+ if (attemptToFixSummary) sections.push(attemptToFixSummary)
1624
+ sections.push(...extraSections.filter(Boolean))
1625
+ if (dynamicNamesSummary) sections.push(dynamicNamesSummary)
1626
+
1627
+ if (sections.length === 0) return
1628
+
1629
+ const line = '-'.repeat(50)
1630
+ // eslint-disable-next-line no-console -- Intentional user-facing session summary
1631
+ console.warn(`\n${line}\nDatadog Test Optimization\n${line}\n${sections.join('\n\n')}\n`)
1632
+
1633
+ if (attemptToFixExecutions) {
1634
+ attemptToFixExecutions.clear()
1635
+ }
1636
+ if (newTestsWithDynamicNames) {
1637
+ newTestsWithDynamicNames.clear()
1638
+ }
1639
+ }
1640
+
1641
+ /**
1642
+ * Logs a "Datadog Test Optimization" warning about new tests with dynamic names.
1643
+ * Clears the Set after logging. No-op if the Set is empty.
1644
+ *
1645
+ * @param {Set<string>} newTestsWithDynamicNames
1646
+ */
1647
+ function logDynamicNamesWarning (newTestsWithDynamicNames) {
1648
+ logTestOptimizationSummary({ newTestsWithDynamicNames })
1383
1649
  }
1384
1650
 
1385
1651
  function isModifiedTest (testPath, testStartLine, testEndLine, modifiedFiles, testFramework) {
@@ -148,9 +148,8 @@ class PrioritySampler {
148
148
  update (rates) {
149
149
  const samplers = {}
150
150
 
151
- for (const key in rates) {
152
- const rate = rates[key]
153
- samplers[key] = new Sampler(rate)
151
+ for (const key of Object.keys(rates)) {
152
+ samplers[key] = new Sampler(rates[key])
154
153
  }
155
154
 
156
155
  samplers[DEFAULT_KEY] = samplers[DEFAULT_KEY] || defaultSampler
@@ -334,7 +333,10 @@ class PrioritySampler {
334
333
  if (!trace.tags[DECISION_MAKER_KEY]) {
335
334
  trace.tags[DECISION_MAKER_KEY] = `-${mechanism}`
336
335
  }
337
- } else {
336
+ } else if (DECISION_MAKER_KEY in trace.tags) {
337
+ // Guard the `delete` so the common drop path doesn't pay the V8
338
+ // dictionary-mode transition unless a prior keep decision actually
339
+ // set the tag.
338
340
  delete trace.tags[DECISION_MAKER_KEY]
339
341
  }
340
342
  }
@@ -4,6 +4,7 @@ const path = require('path')
4
4
  const { pathToFileURL } = require('url')
5
5
 
6
6
  const satisfies = require('../../../../vendor/dist/semifies')
7
+ const getGitMetadata = require('../git_metadata')
7
8
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
8
9
  const { getIsAzureFunction } = require('../serverless')
9
10
  const { getAzureTagsFromMetadata, getAzureAppMetadata, getAzureFunctionMetadata } = require('../azure_metadata')
@@ -38,10 +39,10 @@ class Config {
38
39
  ...getAzureTagsFromMetadata(getIsAzureFunction() ? getAzureFunctionMetadata() : getAzureAppMetadata()),
39
40
  }
40
41
 
41
- // Add source code integration tags if available
42
- if (options.repositoryUrl && options.commitSHA) {
43
- this.tags[GIT_REPOSITORY_URL] = options.repositoryUrl
44
- this.tags[GIT_COMMIT_SHA] = options.commitSHA
42
+ const { commitSHA, repositoryUrl } = getGitMetadata(options)
43
+ if (repositoryUrl && commitSHA) {
44
+ this.tags[GIT_REPOSITORY_URL] = repositoryUrl
45
+ this.tags[GIT_COMMIT_SHA] = commitSHA
45
46
  }
46
47
 
47
48
  // Normalize from seconds to milliseconds. Default must be longer than a minute.
@@ -14,12 +14,12 @@ class SSIHeuristics {
14
14
  * @param {import('../config/config-base')} config - Tracer configuration
15
15
  */
16
16
  constructor (config) {
17
- const longLivedThreshold = config.profiling.longLivedThreshold || DEFAULT_LONG_LIVED_THRESHOLD
17
+ const longLivedThreshold = config.DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD || DEFAULT_LONG_LIVED_THRESHOLD
18
18
  if (typeof longLivedThreshold !== 'number' || longLivedThreshold <= 0) {
19
19
  this.longLivedThreshold = DEFAULT_LONG_LIVED_THRESHOLD
20
20
  log.warn(
21
21
  'Invalid SSIHeuristics.longLivedThreshold value: %s. Using default value:',
22
- config.profiling.longLivedThreshold,
22
+ config.DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD,
23
23
  DEFAULT_LONG_LIVED_THRESHOLD
24
24
  )
25
25
  } else {
@@ -33,7 +33,7 @@ class PropagationHashManager {
33
33
  * @returns {boolean}
34
34
  */
35
35
  isEnabled () {
36
- return this._config?.propagateProcessTags?.enabled === true
36
+ return this._config?.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED === true
37
37
  }
38
38
 
39
39
  /**
@@ -113,11 +113,11 @@ class Tracer extends NoopProxy {
113
113
  const propagationHash = require('./propagation-hash')
114
114
  propagationHash.configure(config)
115
115
 
116
- if (config.crashtracking.enabled) {
116
+ if (config.DD_CRASHTRACKING_ENABLED) {
117
117
  require('./crashtracking').start(config)
118
118
  }
119
119
 
120
- if (config.heapSnapshot.count > 0) {
120
+ if (config.DD_HEAP_SNAPSHOT_COUNT > 0) {
121
121
  require('./heap_snapshots').start(config)
122
122
  }
123
123
 
@@ -229,7 +229,7 @@ class Tracer extends NoopProxy {
229
229
  initializeOpenTelemetryLogs(config)
230
230
  }
231
231
 
232
- if (config.otelMetricsEnabled) {
232
+ if (config.DD_METRICS_OTEL_ENABLED) {
233
233
  const { initializeOpenTelemetryMetrics } = require('./opentelemetry/metrics')
234
234
  initializeOpenTelemetryMetrics(config)
235
235
  }
@@ -5,6 +5,7 @@ const tracerVersion = require('../../../../package.json').version
5
5
  const request = require('../exporters/common/request')
6
6
  const log = require('../log')
7
7
  const { getExtraServices } = require('../service-naming/extra-services')
8
+ const getGitMetadata = require('../git_metadata')
8
9
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
9
10
  const tagger = require('../tagger')
10
11
  const { getAgentUrl } = require('../agent/url')
@@ -37,11 +38,12 @@ class RemoteConfig {
37
38
  '_dd.rc.client_id': clientId,
38
39
  })
39
40
 
40
- const tags = config.repositoryUrl
41
+ const { commitSHA, repositoryUrl } = getGitMetadata(config)
42
+ const tags = repositoryUrl
41
43
  ? {
42
44
  ...config.tags,
43
- [GIT_REPOSITORY_URL]: config.repositoryUrl,
44
- [GIT_COMMIT_SHA]: config.commitSHA,
45
+ [GIT_REPOSITORY_URL]: repositoryUrl,
46
+ [GIT_COMMIT_SHA]: commitSHA,
45
47
  }
46
48
  : config.tags
47
49
 
@@ -42,7 +42,7 @@ module.exports = {
42
42
  this.stop()
43
43
  const clientConfig = DogStatsDClient.generateClientConfig(config)
44
44
 
45
- if (config.propagateProcessTags?.enabled) {
45
+ if (config.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED) {
46
46
  for (const tag of processTags.tagsArray) {
47
47
  clientConfig.tags.push(tag)
48
48
  }
@@ -2,6 +2,11 @@
2
2
 
3
3
  const tags = require('../../../ext/tags')
4
4
  const constants = require('./constants')
5
+ const {
6
+ MAX_META_KEY_LENGTH,
7
+ MAX_META_VALUE_LENGTH,
8
+ MAX_METRIC_KEY_LENGTH,
9
+ } = require('./encode/tags-processors')
5
10
  const id = require('./id')
6
11
  const { isError } = require('./util')
7
12
  const { registerExtraService } = require('./service-naming/extra-services')
@@ -33,6 +38,23 @@ const map = {
33
38
  'resource.name': 'resource',
34
39
  }
35
40
 
41
+ /**
42
+ * @typedef {object} FormattedSpan
43
+ * @property {import('./id').Identifier} trace_id
44
+ * @property {import('./id').Identifier} span_id
45
+ * @property {import('./id').Identifier} parent_id
46
+ * @property {string} name
47
+ * @property {string} resource
48
+ * @property {number} error
49
+ * @property {Record<string, string>} meta
50
+ * @property {Record<string, number>} metrics
51
+ * @property {Record<string, unknown> | undefined} meta_struct
52
+ * @property {number} start
53
+ * @property {number} duration
54
+ * @property {Array} links
55
+ * @property {Array<{ name: string, time_unix_nano: number, attributes?: Record<string, string> }>} [span_events]
56
+ */
57
+
36
58
  function format (span, isFirstSpanInChunk = false, tagForFirstSpanInChunk = false) {
37
59
  const formatted = formatSpan(span)
38
60
 
@@ -71,12 +93,15 @@ function setSingleSpanIngestionTags (span, options) {
71
93
  addTag({}, span.metrics, SPAN_SAMPLING_MAX_PER_SECOND, options.maxPerSecond)
72
94
  }
73
95
 
96
+ /**
97
+ * @param {FormattedSpan} formattedSpan
98
+ * @param {import('./opentracing/span')} span
99
+ */
74
100
  function extractSpanLinks (formattedSpan, span) {
75
101
  if (!span._links?.length) {
76
102
  return
77
103
  }
78
- const links = span._links.map(link => {
79
- const { context, attributes } = link
104
+ const links = span._links.map(({ context, attributes }) => {
80
105
  const formattedLink = {
81
106
  trace_id: context.toTraceId(true),
82
107
  span_id: context.toSpanId(true),
@@ -90,21 +115,28 @@ function extractSpanLinks (formattedSpan, span) {
90
115
 
91
116
  return formattedLink
92
117
  })
93
- formattedSpan.meta['_dd.span_links'] = JSON.stringify(links)
118
+ let serialized = JSON.stringify(links)
119
+ if (serialized.length > MAX_META_VALUE_LENGTH) {
120
+ serialized = `${serialized.slice(0, MAX_META_VALUE_LENGTH)}...`
121
+ }
122
+ formattedSpan.meta['_dd.span_links'] = serialized
94
123
  }
95
124
 
125
+ /**
126
+ * @param {FormattedSpan} formattedSpan
127
+ * @param {import('./opentracing/span')} span
128
+ */
96
129
  function extractSpanEvents (formattedSpan, span) {
97
130
  if (!span._events?.length) {
98
131
  return
99
132
  }
100
- const events = span._events.map(event => {
133
+ formattedSpan.span_events = span._events.map(event => {
101
134
  return {
102
135
  name: event.name,
103
136
  time_unix_nano: Math.round(event.startTime * 1e6),
104
137
  attributes: event.attributes && Object.keys(event.attributes).length > 0 ? event.attributes : undefined,
105
138
  }
106
139
  })
107
- formattedSpan.span_events = events
108
140
  }
109
141
 
110
142
  function extractTags (formattedSpan, span) {
@@ -225,13 +257,25 @@ function extractError (formattedSpan, error) {
225
257
  function addTag (meta, metrics, key, value, nested) {
226
258
  switch (typeof value) {
227
259
  case 'string':
260
+ if (key.length > MAX_META_KEY_LENGTH) {
261
+ key = `${key.slice(0, MAX_META_KEY_LENGTH)}...`
262
+ }
263
+ if (value.length > MAX_META_VALUE_LENGTH) {
264
+ value = `${value.slice(0, MAX_META_VALUE_LENGTH)}...`
265
+ }
228
266
  meta[key] = value
229
267
  break
230
268
  case 'number':
231
269
  if (Number.isNaN(value)) break
270
+ if (key.length > MAX_METRIC_KEY_LENGTH) {
271
+ key = `${key.slice(0, MAX_METRIC_KEY_LENGTH)}...`
272
+ }
232
273
  metrics[key] = value
233
274
  break
234
275
  case 'boolean':
276
+ if (key.length > MAX_METRIC_KEY_LENGTH) {
277
+ key = `${key.slice(0, MAX_METRIC_KEY_LENGTH)}...`
278
+ }
235
279
  metrics[key] = value ? 1 : 0
236
280
  break
237
281
  default:
@@ -240,6 +284,9 @@ function addTag (meta, metrics, key, value, nested) {
240
284
  // Special case for Node.js Buffer and URL
241
285
  // TODO(BridgeAR)[31.03.2025]: Figure out if all typed arrays should be treated as buffers.
242
286
  if (isNodeBuffer(value) || isUrl(value)) {
287
+ if (key.length > MAX_METRIC_KEY_LENGTH) {
288
+ key = `${key.slice(0, MAX_METRIC_KEY_LENGTH)}...`
289
+ }
243
290
  metrics[key] = value.toString()
244
291
  } else if (!Array.isArray(value) && !nested) {
245
292
  for (const [prop, val] of Object.entries(value)) {
@@ -25,7 +25,7 @@ class SpanProcessor {
25
25
  this._spanSampler = new SpanSampler(config.sampler)
26
26
  this._gitMetadataTagger = new GitMetadataTagger(config)
27
27
 
28
- this._processTags = config.propagateProcessTags?.enabled
28
+ this._processTags = config.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED
29
29
  ? processTags.serialized
30
30
  : false
31
31
  }
@@ -158,10 +158,6 @@ class SpanProcessor {
158
158
  }
159
159
  }
160
160
 
161
- for (const span of trace.finished) {
162
- span.context()._tags = {}
163
- }
164
-
165
161
  trace.started = active
166
162
  trace.finished = []
167
163
  }
@@ -85,7 +85,6 @@ module.exports.addSpan = function (span) {
85
85
  const expiration = now + LIFETIME
86
86
  const wrapped = new WeakRef(span)
87
87
  spans.add(wrapped, expiration)
88
- // registry.register(span, span._name)
89
88
  }
90
89
 
91
90
  function isEnabled () {
@@ -163,12 +163,14 @@ function getProducts (config) {
163
163
  * @param {import('../config/config-base')} config
164
164
  */
165
165
  function getInstallSignature (config) {
166
- const { installSignature: sig } = config
167
- if (sig && (sig.id || sig.time || sig.type)) {
166
+ const id = config.DD_INSTRUMENTATION_INSTALL_ID
167
+ const time = config.DD_INSTRUMENTATION_INSTALL_TIME
168
+ const type = config.DD_INSTRUMENTATION_INSTALL_TYPE
169
+ if (id || time || type) {
168
170
  return {
169
- install_id: sig.id,
170
- install_time: sig.time,
171
- install_type: sig.type,
171
+ install_id: id,
172
+ install_time: time,
173
+ install_type: type,
172
174
  }
173
175
  }
174
176
  }
@@ -14,7 +14,7 @@ function storeConfig (config) {
14
14
  const { containerId } = require('./exporters/common/docker')
15
15
  const processTags = require('./process-tags')
16
16
 
17
- const processTagsSerialized = config.propagateProcessTags?.enabled
17
+ const processTagsSerialized = config.DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED
18
18
  ? (processTags.serialized || null)
19
19
  : null
20
20