dd-trace 5.102.0 → 5.104.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 (201) hide show
  1. package/ext/exporters.js +1 -0
  2. package/index.d.ts +25 -3
  3. package/package.json +15 -13
  4. package/packages/datadog-esbuild/src/utils.js +2 -2
  5. package/packages/datadog-instrumentations/src/ai.js +1 -1
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  8. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
  9. package/packages/datadog-instrumentations/src/couchbase.js +69 -220
  10. package/packages/datadog-instrumentations/src/cucumber.js +104 -31
  11. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  12. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  13. package/packages/datadog-instrumentations/src/electron.js +240 -0
  14. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  15. package/packages/datadog-instrumentations/src/graphql.js +13 -17
  16. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  17. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +2 -2
  18. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  21. package/packages/datadog-instrumentations/src/helpers/kafka.js +58 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
  25. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  26. package/packages/datadog-instrumentations/src/ioredis.js +18 -14
  27. package/packages/datadog-instrumentations/src/jest.js +382 -84
  28. package/packages/datadog-instrumentations/src/kafkajs.js +184 -174
  29. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  30. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  31. package/packages/datadog-instrumentations/src/mocha/main.js +309 -56
  32. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  33. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -9
  34. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  35. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  36. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  37. package/packages/datadog-instrumentations/src/pg.js +25 -11
  38. package/packages/datadog-instrumentations/src/playwright.js +449 -60
  39. package/packages/datadog-instrumentations/src/redis.js +19 -10
  40. package/packages/datadog-instrumentations/src/router.js +4 -2
  41. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  42. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
  44. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  45. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  46. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  47. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  48. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  49. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  50. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  51. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  52. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  53. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  54. package/packages/datadog-plugin-cucumber/src/index.js +1 -0
  55. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +239 -40
  56. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  57. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  58. package/packages/datadog-plugin-electron/src/index.js +17 -0
  59. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  60. package/packages/datadog-plugin-electron/src/net.js +82 -0
  61. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  62. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  63. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  64. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  65. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  66. package/packages/datadog-plugin-graphql/src/utils.js +33 -1
  67. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  68. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  69. package/packages/datadog-plugin-http/src/client.js +2 -2
  70. package/packages/datadog-plugin-jest/src/index.js +92 -50
  71. package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
  72. package/packages/datadog-plugin-mocha/src/index.js +1 -0
  73. package/packages/datadog-plugin-mongodb-core/src/index.js +70 -69
  74. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  75. package/packages/datadog-plugin-openai/src/services.js +2 -1
  76. package/packages/datadog-plugin-pg/src/index.js +3 -3
  77. package/packages/datadog-plugin-playwright/src/index.js +4 -0
  78. package/packages/datadog-plugin-redis/src/index.js +54 -24
  79. package/packages/datadog-plugin-undici/src/index.js +19 -0
  80. package/packages/datadog-plugin-vitest/src/index.js +19 -7
  81. package/packages/datadog-shimmer/src/shimmer.js +35 -0
  82. package/packages/dd-trace/src/aiguard/index.js +3 -1
  83. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  84. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  85. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  86. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  87. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  88. package/packages/dd-trace/src/appsec/index.js +10 -3
  89. package/packages/dd-trace/src/appsec/reporter.js +19 -5
  90. package/packages/dd-trace/src/azure_metadata.js +17 -6
  91. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  92. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  94. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  95. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  96. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  97. package/packages/dd-trace/src/config/defaults.js +3 -14
  98. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
  99. package/packages/dd-trace/src/config/helper.js +4 -0
  100. package/packages/dd-trace/src/config/index.js +2 -2
  101. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  102. package/packages/dd-trace/src/config/parsers.js +7 -1
  103. package/packages/dd-trace/src/config/supported-configurations.json +60 -38
  104. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  105. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  106. package/packages/dd-trace/src/datastreams/context.js +4 -2
  107. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  108. package/packages/dd-trace/src/datastreams/processor.js +2 -2
  109. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  110. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  111. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  112. package/packages/dd-trace/src/debugger/index.js +7 -7
  113. package/packages/dd-trace/src/dogstatsd.js +2 -2
  114. package/packages/dd-trace/src/encode/0.4.js +45 -54
  115. package/packages/dd-trace/src/encode/0.5.js +34 -3
  116. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
  117. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  118. package/packages/dd-trace/src/exporter.js +2 -0
  119. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  120. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  122. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  123. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  124. package/packages/dd-trace/src/exporters/common/request.js +4 -2
  125. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  126. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  127. package/packages/dd-trace/src/git_metadata.js +10 -8
  128. package/packages/dd-trace/src/id.js +17 -4
  129. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  130. package/packages/dd-trace/src/lambda/handler.js +2 -4
  131. package/packages/dd-trace/src/lambda/index.js +62 -14
  132. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  133. package/packages/dd-trace/src/llmobs/index.js +13 -2
  134. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  135. package/packages/dd-trace/src/llmobs/sdk.js +10 -0
  136. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  137. package/packages/dd-trace/src/log/writer.js +3 -1
  138. package/packages/dd-trace/src/noop/span.js +3 -1
  139. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  140. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  141. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  142. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  143. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  144. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  145. package/packages/dd-trace/src/plugins/ci_plugin.js +52 -17
  146. package/packages/dd-trace/src/plugins/database.js +54 -12
  147. package/packages/dd-trace/src/plugins/index.js +1 -0
  148. package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
  149. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  150. package/packages/dd-trace/src/plugins/tracing.js +5 -3
  151. package/packages/dd-trace/src/plugins/util/ci.js +8 -8
  152. package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
  153. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  154. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  155. package/packages/dd-trace/src/plugins/util/test.js +119 -5
  156. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
  157. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  158. package/packages/dd-trace/src/priority_sampler.js +1 -1
  159. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  160. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  161. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  162. package/packages/dd-trace/src/rate_limiter.js +1 -1
  163. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  164. package/packages/dd-trace/src/ritm.js +2 -1
  165. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  166. package/packages/dd-trace/src/scope.js +7 -5
  167. package/packages/dd-trace/src/serverless.js +5 -2
  168. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  169. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
  170. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  171. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
  172. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  173. package/packages/dd-trace/src/span_stats.js +1 -1
  174. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  175. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  176. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  177. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
  178. package/vendor/dist/opentracing/LICENSE +0 -201
  179. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  180. package/vendor/dist/opentracing/constants.d.ts +0 -61
  181. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  182. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  183. package/vendor/dist/opentracing/functions.d.ts +0 -20
  184. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  185. package/vendor/dist/opentracing/index.d.ts +0 -12
  186. package/vendor/dist/opentracing/index.js +0 -1
  187. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  188. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  189. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  190. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  191. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  192. package/vendor/dist/opentracing/noop.d.ts +0 -8
  193. package/vendor/dist/opentracing/reference.d.ts +0 -33
  194. package/vendor/dist/opentracing/span.d.ts +0 -147
  195. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  196. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  197. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  198. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  199. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  200. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  201. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -40,14 +40,14 @@ const KEY_TYPE = buildKey('type')
40
40
  const KEY_NAME = buildKey('name')
41
41
  const KEY_RESOURCE = buildKey('resource')
42
42
  const KEY_SERVICE = buildKey('service')
43
+ const KEY_ERROR = buildKey('error')
44
+ const KEY_START = buildKey('start')
45
+ const KEY_DURATION = buildKey('duration')
43
46
  const KEY_SPAN_EVENTS = buildKey('span_events')
44
47
  const KEY_META_STRUCT = buildKey('meta_struct')
45
48
  const KEY_TRACE_ID_PREFIX = buildKeyWithPrefix('trace_id', 0xCF)
46
49
  const KEY_SPAN_ID_PREFIX = buildKeyWithPrefix('span_id', 0xCF)
47
50
  const KEY_PARENT_ID_PREFIX = buildKeyWithPrefix('parent_id', 0xCF)
48
- const KEY_ERROR_PREFIX = buildKeyWithPrefix('error', 0xCE)
49
- const KEY_START_PREFIX = buildKeyWithPrefix('start', 0xCF)
50
- const KEY_DURATION_PREFIX = buildKeyWithPrefix('duration', 0xCF)
51
51
  const KEY_META_PREFIX = buildKeyWithPrefix('meta', 0xDF)
52
52
  const KEY_METRICS_PREFIX = buildKeyWithPrefix('metrics', 0xDF)
53
53
 
@@ -344,9 +344,12 @@ class AgentEncoder {
344
344
  cursor += KEY_SERVICE.length
345
345
  target.set(serviceEntry, cursor)
346
346
 
347
- this.#writeIntegerField(bytes, KEY_ERROR_PREFIX, span.error)
348
- this.#writeLongField(bytes, KEY_START_PREFIX, span.start)
349
- this.#writeLongField(bytes, KEY_DURATION_PREFIX, span.duration)
347
+ bytes.set(KEY_ERROR)
348
+ this._encodeIntOrFloat(bytes, span.error)
349
+ bytes.set(KEY_START)
350
+ this._encodeIntOrFloat(bytes, span.start)
351
+ bytes.set(KEY_DURATION)
352
+ this._encodeIntOrFloat(bytes, span.duration)
350
353
 
351
354
  this.#encodeMetaEntries(bytes, KEY_META_PREFIX, span.meta)
352
355
  this.#encodeMetaEntries(bytes, KEY_METRICS_PREFIX, span.metrics)
@@ -448,6 +451,8 @@ class AgentEncoder {
448
451
  }
449
452
 
450
453
  // Single pass: reserve the count slot, encode entries while counting, patch the count.
454
+ // Subclasses (0.5, CI visibility encoders) inherit this; the wire stays on float64
455
+ // for numeric values to keep their established trace / events intake unchanged.
451
456
  _encodeMap (bytes, value) {
452
457
  const offset = bytes.length
453
458
  bytes.reserve(5)
@@ -542,12 +547,9 @@ class AgentEncoder {
542
547
  target.set(keyEntry, writeOffset)
543
548
  target.set(valueEntry, writeOffset + keyEntryLen)
544
549
  } else {
545
- bytes.reserve(keyEntryLen + 9)
546
- const target = bytes.buffer
547
- target.set(keyEntry, writeOffset)
548
- const valueOffset = writeOffset + keyEntryLen
549
- target[valueOffset] = 0xCB
550
- bytes.view.setFloat64(valueOffset + 1, entryValue)
550
+ bytes.reserve(keyEntryLen)
551
+ bytes.buffer.set(keyEntry, writeOffset)
552
+ this._encodeIntOrFloat(bytes, entryValue)
551
553
  }
552
554
  count++
553
555
  }
@@ -588,49 +590,38 @@ class AgentEncoder {
588
590
  }
589
591
 
590
592
  /**
593
+ * Emit `value` as the smallest valid msgpack number encoding: compact
594
+ * unsigned/signed int when integer, float64 otherwise. Unlike
595
+ * `MsgpackEncoder#encodeNumber`, NaN keeps its float64 bits instead of
596
+ * coercing to fixint 0.
597
+ *
598
+ * Underscore-protected so the 0.5 subclass can call it from its own
599
+ * `_encode` / `_encodeMap` overrides.
600
+ *
591
601
  * @param {MsgpackChunk} bytes
592
- * @param {Buffer} keyPrefix Precomputed `[key, 0xCE]`.
593
602
  * @param {number} value
594
603
  */
595
- #writeIntegerField (bytes, keyPrefix, value) {
596
- const keyPrefixLen = keyPrefix.length
597
- const offset = bytes.length
598
- bytes.reserve(keyPrefixLen + 4)
599
-
600
- const target = bytes.buffer
601
- target.set(keyPrefix, offset)
602
-
603
- const valueOffset = offset + keyPrefixLen
604
- target[valueOffset] = value >> 24
605
- target[valueOffset + 1] = value >> 16
606
- target[valueOffset + 2] = value >> 8
607
- target[valueOffset + 3] = value
608
- }
609
-
610
- /**
611
- * @param {MsgpackChunk} bytes
612
- * @param {Buffer} keyPrefix Precomputed `[key, 0xCF]`.
613
- * @param {number} value Up to a 53-bit safe integer.
614
- */
615
- #writeLongField (bytes, keyPrefix, value) {
616
- const high = (value / 2 ** 32) >> 0
617
- const low = value >>> 0
618
- const keyPrefixLen = keyPrefix.length
619
- const offset = bytes.length
620
- bytes.reserve(keyPrefixLen + 8)
621
-
622
- const target = bytes.buffer
623
- target.set(keyPrefix, offset)
624
-
625
- const valueOffset = offset + keyPrefixLen
626
- target[valueOffset] = high >> 24
627
- target[valueOffset + 1] = high >> 16
628
- target[valueOffset + 2] = high >> 8
629
- target[valueOffset + 3] = high
630
- target[valueOffset + 4] = low >> 24
631
- target[valueOffset + 5] = low >> 16
632
- target[valueOffset + 6] = low >> 8
633
- target[valueOffset + 7] = low
604
+ _encodeIntOrFloat (bytes, value) {
605
+ // Fast path: positive fixint (0..127). `value === (value & 0x7F)` is true
606
+ // iff `value` is an exact integer in that range — covers `error: 0/1`,
607
+ // priority flags, attribute counts, HTTP status codes mapped to numbers,
608
+ // and most small metrics. NaN, ±Infinity, negatives, and any non-integer
609
+ // float fall through.
610
+ if (value === (value & 0x7F)) {
611
+ const offset = bytes.length
612
+ bytes.reserve(1)
613
+ bytes.buffer[offset] = value
614
+ return
615
+ }
616
+ if (Number.isInteger(value)) {
617
+ if (value >= 0) {
618
+ this.#msgpack.encodeUnsigned(bytes, value)
619
+ } else {
620
+ this.#msgpack.encodeSigned(bytes, value)
621
+ }
622
+ } else {
623
+ this.#encodeFloat(bytes, value)
624
+ }
634
625
  }
635
626
 
636
627
  /**
@@ -853,7 +844,7 @@ class AgentEncoder {
853
844
  if (typeof value === 'number') {
854
845
  this._encodeString(bytes, key)
855
846
  bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
856
- this.#encodeFloat(bytes, value)
847
+ this._encodeIntOrFloat(bytes, value)
857
848
  return true
858
849
  }
859
850
  if (typeof value === 'boolean') {
@@ -923,7 +914,7 @@ class AgentEncoder {
923
914
  }
924
915
  if (typeof value === 'number') {
925
916
  bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
926
- this.#encodeFloat(bytes, value)
917
+ this._encodeIntOrFloat(bytes, value)
927
918
  return true
928
919
  }
929
920
  if (typeof value === 'boolean') {
@@ -46,15 +46,46 @@ class AgentEncoder extends BaseEncoder {
46
46
  this._encodeId(bytes, span.trace_id)
47
47
  this._encodeId(bytes, span.span_id)
48
48
  this._encodeId(bytes, span.parent_id)
49
- this._encodeLong(bytes, span.start || 0)
50
- this._encodeLong(bytes, span.duration || 0)
51
- this._encodeInteger(bytes, span.error)
49
+ this._encodeIntOrFloat(bytes, span.start || 0)
50
+ this._encodeIntOrFloat(bytes, span.duration || 0)
51
+ this._encodeIntOrFloat(bytes, span.error)
52
52
  this._encodeMap(bytes, span.meta || {})
53
53
  this._encodeMap(bytes, span.metrics || {})
54
54
  this._encodeString(bytes, span.type)
55
55
  }
56
56
  }
57
57
 
58
+ // Override the inherited 0.4 `_encodeMap` so the v0.5 wire emits each numeric
59
+ // value via `_encodeIntOrFloat` (compact unsigned/signed int when integer,
60
+ // float64 otherwise) instead of always float64. The 0.4 base method stays on
61
+ // float64 because the CI-visibility encoders inherit it and target a
62
+ // different intake.
63
+ _encodeMap (bytes, value) {
64
+ const offset = bytes.length
65
+ bytes.reserve(5)
66
+ bytes.buffer[offset] = 0xDF
67
+
68
+ let count = 0
69
+ for (const key of Object.keys(value)) {
70
+ const entryValue = value[key]
71
+ if (typeof entryValue === 'string') {
72
+ this._encodeString(bytes, key)
73
+ this._encodeString(bytes, entryValue)
74
+ count++
75
+ } else if (typeof entryValue === 'number') {
76
+ this._encodeString(bytes, key)
77
+ this._encodeIntOrFloat(bytes, entryValue)
78
+ count++
79
+ }
80
+ }
81
+
82
+ const target = bytes.buffer
83
+ target[offset + 1] = count >>> 24
84
+ target[offset + 2] = count >>> 16
85
+ target[offset + 3] = count >>> 8
86
+ target[offset + 4] = count
87
+ }
88
+
58
89
  _encodeString (bytes, value = '') {
59
90
  let index = this._stringMap[value]
60
91
  if (index === undefined) {
@@ -7,6 +7,7 @@ const {
7
7
  TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
8
8
  TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT,
9
9
  } = require('../ci-visibility/telemetry')
10
+ const { MsgpackChunk } = require('../msgpack')
10
11
  const { AgentEncoder } = require('./0.4')
11
12
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
12
13
 
@@ -20,6 +21,9 @@ const TEST_AND_SPAN_KEYS_LENGTH = 11
20
21
 
21
22
  const INTAKE_SOFT_LIMIT = 2 * 1024 * 1024 // 2MB
22
23
 
24
+ // Prefix is ~1 KB in practice; `MsgpackChunk` resizes on overflow.
25
+ const PREFIX_CHUNK_INITIAL_SIZE = 2048
26
+
23
27
  function formatSpan (span) {
24
28
  let encodingVersion = ENCODING_VERSION
25
29
  if (span.type === 'test' && span.meta && span.meta.test_session_id) {
@@ -259,10 +263,6 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
259
263
  }
260
264
 
261
265
  _encode (bytes, trace) {
262
- if (this._isReset) {
263
- this._encodePayloadStart(bytes)
264
- this._isReset = false
265
- }
266
266
  const startTime = Date.now()
267
267
 
268
268
  const events = trace.map(formatSpan)
@@ -281,20 +281,28 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
281
281
 
282
282
  makePayload () {
283
283
  distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT, { endpoint: 'test_cycle' }, this._eventCount)
284
- const bytes = this._traceBytes
285
- const eventsOffset = this._eventsOffset
286
- const eventsCount = this._eventCount
287
284
 
288
- bytes.buffer[eventsOffset] = 0xDD
289
- bytes.buffer[eventsOffset + 1] = eventsCount >> 24
290
- bytes.buffer[eventsOffset + 2] = eventsCount >> 16
291
- bytes.buffer[eventsOffset + 3] = eventsCount >> 8
292
- bytes.buffer[eventsOffset + 4] = eventsCount
285
+ // Encode the payload prefix (version + metadata + events-array header) at flush time,
286
+ // not on the first `_encode`. The CI Visibility flow adds metadata across multiple
287
+ // diagnostic channels (`session:start` adds `test_session.name`, the async
288
+ // `library-configuration` callback adds capability tags). Any span finished between
289
+ // those calls would otherwise freeze the prefix with stale metadata.
290
+ const prefixBytes = new MsgpackChunk(PREFIX_CHUNK_INITIAL_SIZE)
291
+ this._encodePayloadStart(prefixBytes)
293
292
 
294
- const traceSize = bytes.length
295
- const buffer = Buffer.allocUnsafe(traceSize)
296
-
297
- bytes.buffer.copy(buffer, 0, 0, traceSize)
293
+ const eventsOffset = this._eventsOffset
294
+ const eventsCount = this._eventCount
295
+ prefixBytes.buffer[eventsOffset] = 0xDD
296
+ prefixBytes.buffer[eventsOffset + 1] = eventsCount >> 24
297
+ prefixBytes.buffer[eventsOffset + 2] = eventsCount >> 16
298
+ prefixBytes.buffer[eventsOffset + 3] = eventsCount >> 8
299
+ prefixBytes.buffer[eventsOffset + 4] = eventsCount
300
+
301
+ const eventsBytes = this._traceBytes
302
+ const totalSize = prefixBytes.length + eventsBytes.length
303
+ const buffer = Buffer.allocUnsafe(totalSize)
304
+ prefixBytes.buffer.copy(buffer, 0, 0, prefixBytes.length)
305
+ eventsBytes.buffer.copy(buffer, prefixBytes.length, 0, eventsBytes.length)
298
306
 
299
307
  this.reset()
300
308
 
@@ -302,7 +310,8 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
302
310
  }
303
311
 
304
312
  _encodePayloadStart (bytes) {
305
- // encodes the payload up to `events`. `events` will be encoded via _encode
313
+ // Encodes the payload up to (and including) the `events` array prefix. The 5 reserved
314
+ // bytes for the array length are patched in `makePayload`.
306
315
  const payload = {
307
316
  version: ENCODING_VERSION,
308
317
  metadata: {
@@ -346,7 +355,6 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
346
355
  this._encodeMap(bytes, payload.metadata.test_session_end)
347
356
  }
348
357
  this._encodeString(bytes, 'events')
349
- // Get offset of the events list to update the length of the array when calling `makePayload`
350
358
  this._eventsOffset = bytes.length
351
359
  bytes.reserve(5)
352
360
  }
@@ -354,7 +362,6 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
354
362
  reset () {
355
363
  this._reset()
356
364
  this._eventCount = 0
357
- this._isReset = true
358
365
  }
359
366
  }
360
367
 
@@ -85,7 +85,7 @@ function spanToJSON (span) {
85
85
  class AgentlessJSONEncoder {
86
86
  /**
87
87
  * @param {object} writer - Writer instance with a flush() method, called when the buffer exceeds the soft limit
88
- * @param {object} [metadata={}] - Shared metadata spread into each trace object (hostname, env, tracerVersion, etc.)
88
+ * @param {object} [metadata] - Shared metadata spread into each trace object (hostname, env, tracerVersion, etc.)
89
89
  */
90
90
  constructor (writer, metadata = {}) {
91
91
  this._writer = writer
@@ -7,6 +7,8 @@ const constants = require('./constants')
7
7
 
8
8
  module.exports = function getExporter (name) {
9
9
  switch (name) {
10
+ case exporters.ELECTRON:
11
+ return require('./exporters/electron')
10
12
  case exporters.LOG:
11
13
  return require('./exporters/log')
12
14
  case exporters.AGENT:
@@ -50,7 +50,8 @@ class AgentExporter {
50
50
  this.#timer = setTimeout(() => {
51
51
  this._writer.flush()
52
52
  this.#timer = undefined
53
- }, flushInterval).unref()
53
+ }, flushInterval)
54
+ this.#timer.unref?.()
54
55
  }
55
56
  }
56
57
 
@@ -18,7 +18,7 @@ class AgentlessExporter {
18
18
 
19
19
  /**
20
20
  * @param {object} config - Configuration object
21
- * @param {string} [config.site='datadoghq.com'] - The Datadog site
21
+ * @param {string} [config.site] - The Datadog site. Defaults to 'datadoghq.com'.
22
22
  * @param {string} [config.url] - Override intake URL
23
23
  * @param {number} [config.flushInterval] - Batch flush interval in ms
24
24
  * @param {string} [config.env] - Environment name
@@ -109,7 +109,8 @@ class AgentlessExporter {
109
109
  log.error('Failed to flush traces on timer: %s', err.message)
110
110
  }
111
111
  this.#timer = undefined
112
- }, flushInterval).unref()
112
+ }, flushInterval)
113
+ this.#timer.unref?.()
113
114
  }
114
115
  }
115
116
 
@@ -19,8 +19,8 @@ class AgentlessWriter extends BaseWriter {
19
19
  /**
20
20
  * @param {object} options - Writer options
21
21
  * @param {URL} [options.url] - The intake URL. If not provided, constructed from site.
22
- * @param {string} [options.site='datadoghq.com'] - The Datadog site
23
- * @param {object} [options.metadata={}] - Metadata to pass to the encoder (hostname, env, etc.)
22
+ * @param {string} [options.site] - The Datadog site
23
+ * @param {object} [options.metadata] - Metadata to pass to the encoder (hostname, env, etc.)
24
24
  */
25
25
  constructor ({ url, site = 'datadoghq.com', metadata = {} }) {
26
26
  super({ url })
@@ -4,6 +4,8 @@ const http = require('http')
4
4
  const https = require('https')
5
5
  const { storage } = require('../../../../datadog-core')
6
6
 
7
+ const legacyStorage = storage('legacy')
8
+
7
9
  const keepAlive = true
8
10
  const maxSockets = 1
9
11
 
@@ -26,7 +28,7 @@ function createAgentClass (BaseAgent) {
26
28
  }
27
29
 
28
30
  _noop (callback) {
29
- return storage('legacy').run({ noop: true }, callback)
31
+ return legacyStorage.run({ noop: true }, callback)
30
32
  }
31
33
  }
32
34
 
@@ -39,7 +39,8 @@ class BufferingExporter {
39
39
  this[timerKey] = setTimeout(() => {
40
40
  writer.flush()
41
41
  this[timerKey] = undefined
42
- }, flushInterval).unref()
42
+ }, flushInterval)
43
+ this[timerKey].unref?.()
43
44
  }
44
45
  }
45
46
 
@@ -20,6 +20,8 @@ const {
20
20
  markEndpointReached,
21
21
  } = require('./retry')
22
22
 
23
+ const legacyStorage = storage('legacy')
24
+
23
25
  const maxActiveBufferSize = 1024 * 1024 * 64
24
26
 
25
27
  let activeBufferSize = 0
@@ -161,7 +163,7 @@ function request (data, options, callback) {
161
163
 
162
164
  activeBufferSize += options.headers['Content-Length'] ?? 0
163
165
 
164
- storage('legacy').run({ noop: true }, () => {
166
+ legacyStorage.run({ noop: true }, () => {
165
167
  let finished = false
166
168
  const finalize = () => {
167
169
  if (finished) return
@@ -180,7 +182,7 @@ function request (data, options, callback) {
180
182
  // Unref so a pending retry never keeps the host process alive past
181
183
  // its natural exit point; long-running apps still retry because the
182
184
  // event loop is held open by their own work.
183
- setTimeout(attempt, getRetryDelay(options, attemptIndex), attemptIndex + 1).unref()
185
+ setTimeout(attempt, getRetryDelay(options, attemptIndex), attemptIndex + 1).unref?.()
184
186
  } else {
185
187
  callback(error)
186
188
  }
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('dc-polyfill')
4
+ const { truncateSpan, normalizeSpan } = require('../../encode/tags-processors')
5
+
6
+ const traceChannel = channel('datadog:apm:electron:export')
7
+
8
+ class ElectronExporter {
9
+ #timer
10
+ #traces = []
11
+
12
+ constructor (config) {
13
+ this._config = config
14
+
15
+ globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(this.flush.bind(this))
16
+ }
17
+
18
+ export (spans) {
19
+ this.#traces.push(spans)
20
+
21
+ const { flushInterval } = this._config
22
+
23
+ if (flushInterval === 0) {
24
+ this.flush()
25
+ } else if (this.#timer === undefined) {
26
+ this.#timer = setTimeout(() => {
27
+ this.flush()
28
+ this.#timer = undefined
29
+ }, flushInterval)
30
+ this.#timer.unref?.()
31
+ }
32
+ }
33
+
34
+ flush (done = () => {}) {
35
+ clearTimeout(this.#timer)
36
+ this.#timer = undefined
37
+
38
+ const traces = this.#traces.splice(0)
39
+
40
+ if (traces.length > 0 && traceChannel.hasSubscribers) {
41
+ const formattedTraces = traces.map(spans => spans.map(span => normalizeSpan(truncateSpan(span))))
42
+ traceChannel.publish(formattedTraces)
43
+ }
44
+
45
+ done()
46
+ }
47
+ }
48
+
49
+ module.exports = ElectronExporter
@@ -26,7 +26,8 @@ class ExternalLogger {
26
26
  }
27
27
  this.timer = setInterval(() => {
28
28
  this.flush()
29
- }, this.interval).unref()
29
+ }, this.interval)
30
+ this.timer.unref?.()
30
31
 
31
32
  tracerLogger.debug(`started log writer to https://${this.intake}${this.endpoint}`)
32
33
  }
@@ -12,19 +12,21 @@ const {
12
12
  resolveGitHeadSHA,
13
13
  } = require('./config/git_properties')
14
14
 
15
- /** @type {{ commitSHA: string | undefined, repositoryUrl: string | undefined } | undefined} */
16
- let cached
15
+ /**
16
+ * @typedef {{ commitSHA: string | undefined, repositoryUrl: string | undefined }} GitMetadata
17
+ * @type {{ enabled?: GitMetadata, disabled?: GitMetadata }}
18
+ */
19
+ const cache = {}
17
20
 
18
21
  /**
19
22
  * @param {import('./config/config-types').ConfigProperties} config
20
23
  */
21
24
  function getGitMetadata (config) {
22
- if (cached) return cached
23
-
24
25
  if (!config.DD_TRACE_GIT_METADATA_ENABLED) {
25
- cached = { commitSHA: undefined, repositoryUrl: undefined }
26
- return cached
26
+ cache.disabled ??= { commitSHA: undefined, repositoryUrl: undefined }
27
+ return cache.disabled
27
28
  }
29
+ if (cache.enabled) return cache.enabled
28
30
 
29
31
  let repositoryUrl = removeUserSensitiveInfo(config.DD_GIT_REPOSITORY_URL ?? config.tags[GIT_REPOSITORY_URL])
30
32
  let commitSHA = config.DD_GIT_COMMIT_SHA ?? config.tags[GIT_COMMIT_SHA]
@@ -59,8 +61,8 @@ function getGitMetadata (config) {
59
61
 
60
62
  commitSHA ??= resolveGitHeadSHA(gitFolderPath)
61
63
 
62
- cached = { commitSHA, repositoryUrl }
63
- return cached
64
+ cache.enabled = { commitSHA, repositoryUrl }
65
+ return cache.enabled
64
66
  }
65
67
 
66
68
  module.exports = getGitMetadata
@@ -13,6 +13,12 @@ let batch = 0
13
13
  class Identifier {
14
14
  /** @type {number[] | Uint8Array} */
15
15
  #buffer
16
+ /** @type {bigint | undefined} */
17
+ #bigInt
18
+ /** @type {string | undefined} */
19
+ #stringHex
20
+ /** @type {string | undefined} */
21
+ #stringDecimal
16
22
 
17
23
  /**
18
24
  * @param {string} value
@@ -29,16 +35,23 @@ class Identifier {
29
35
  * @returns {string}
30
36
  */
31
37
  toString (radix = 16) {
32
- return radix === 16
33
- ? Buffer.from(this.#buffer).toString('hex')
34
- : toNumberString(this.#buffer, radix)
38
+ if (radix === 16) {
39
+ this.#stringHex ??= Buffer.from(this.#buffer).toString('hex')
40
+ return this.#stringHex
41
+ }
42
+ if (radix === 10) {
43
+ this.#stringDecimal ??= toNumberString(this.#buffer, 10)
44
+ return this.#stringDecimal
45
+ }
46
+ return toNumberString(this.#buffer, radix)
35
47
  }
36
48
 
37
49
  /**
38
50
  * @returns {bigint}
39
51
  */
40
52
  toBigInt () {
41
- return Buffer.from(this.#buffer).readBigUInt64BE(0)
53
+ this.#bigInt ??= Buffer.from(this.#buffer).readBigUInt64BE(0)
54
+ return this.#bigInt
42
55
  }
43
56
 
44
57
  /**
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * Modifications copyright 2022 Datadog, Inc.
4
+ *
5
+ * Some functions are part of aws-lambda-nodejs-runtime-interface-client
6
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/v2.1.0/src/utils/UserFunction.ts
7
+ */
8
+ 'use strict'
9
+
10
+ const path = require('path')
11
+
12
+ /**
13
+ * Example: `'./api/src/index.nested.handler'` → `['./api/src/', 'index.nested.handler']`.
14
+ *
15
+ * @param {string} fullHandler
16
+ */
17
+ function extractModuleRootAndHandler (fullHandler) {
18
+ const handlerString = path.basename(fullHandler)
19
+ const moduleRoot = fullHandler.slice(0, Math.max(0, fullHandler.indexOf(handlerString)))
20
+ return [moduleRoot, handlerString]
21
+ }
22
+
23
+ /**
24
+ * Example: `'index.nested.handler'` → `['index', 'nested.handler']`.
25
+ *
26
+ * @param {string} handler
27
+ * @throws {Error} When the handler is not of the form `<module>.<path>`.
28
+ */
29
+ function extractModuleNameAndHandlerPath (handler) {
30
+ const match = handler.match(/^([^.]*)\.(.*)$/)
31
+ if (!match || match.length !== 3) {
32
+ throw new Error(`Malformed handler name: ${handler}`)
33
+ }
34
+ return [match[1], match[2]]
35
+ }
36
+
37
+ /**
38
+ * @param {string} lambdaStylePath `LAMBDA_TASK_ROOT` joined with the module root and module name.
39
+ */
40
+ function getLambdaFilePaths (lambdaStylePath) {
41
+ return [
42
+ `${lambdaStylePath}.js`,
43
+ `${lambdaStylePath}.mjs`,
44
+ `${lambdaStylePath}.cjs`,
45
+ ]
46
+ }
47
+
48
+ module.exports = {
49
+ extractModuleRootAndHandler,
50
+ extractModuleNameAndHandlerPath,
51
+ getLambdaFilePaths,
52
+ }
@@ -6,8 +6,6 @@ const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
6
6
  const { ImpendingTimeout } = require('./runtime/errors')
7
7
  const { extractContext } = require('./context')
8
8
 
9
- const globalTracer = global._ddtrace
10
- const tracer = globalTracer._tracer
11
9
  const timeoutChannel = channel('apm:aws:lambda:timeout')
12
10
  // Always crash the flushes when a message is received
13
11
  // from this channel.
@@ -25,8 +23,7 @@ let __lambdaTimeout
25
23
  */
26
24
  function checkTimeout (context) {
27
25
  const remainingTimeInMillis = context.getRemainingTimeInMillis()
28
-
29
- const apmFlushDeadline = tracer._config.DD_APM_FLUSH_DEADLINE_MILLISECONDS
26
+ const apmFlushDeadline = global._ddtrace._tracer._config.DD_APM_FLUSH_DEADLINE_MILLISECONDS
30
27
 
31
28
  __lambdaTimeout = setTimeout(() => {
32
29
  timeoutChannel.publish()
@@ -42,6 +39,7 @@ function checkTimeout (context) {
42
39
  * Once that is done, it finishes the last span.
43
40
  */
44
41
  function crashFlush () {
42
+ const tracer = global._ddtrace._tracer
45
43
  const activeSpan = tracer.scope().active()
46
44
  if (activeSpan === null) {
47
45
  log.debug('An impending timeout was reached, but no root span was found. No error will be tagged.')