dd-trace 5.61.1 → 5.63.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 (215) hide show
  1. package/README.md +0 -5
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/src/ai.js +140 -0
  4. package/packages/datadog-instrumentations/src/apollo-server.js +50 -8
  5. package/packages/datadog-instrumentations/src/aws-sdk.js +49 -60
  6. package/packages/datadog-instrumentations/src/couchbase.js +102 -65
  7. package/packages/datadog-instrumentations/src/fastify.js +61 -55
  8. package/packages/datadog-instrumentations/src/graphql.js +90 -122
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  10. package/packages/datadog-instrumentations/src/helpers/register.js +2 -22
  11. package/packages/datadog-instrumentations/src/hono.js +11 -8
  12. package/packages/datadog-instrumentations/src/http2/server.js +14 -20
  13. package/packages/datadog-instrumentations/src/knex.js +15 -17
  14. package/packages/datadog-instrumentations/src/microgateway-core.js +16 -15
  15. package/packages/datadog-instrumentations/src/mongodb-core.js +35 -32
  16. package/packages/datadog-instrumentations/src/mongodb.js +9 -13
  17. package/packages/datadog-instrumentations/src/mongoose.js +25 -29
  18. package/packages/datadog-instrumentations/src/next.js +4 -8
  19. package/packages/datadog-instrumentations/src/openai.js +0 -2
  20. package/packages/datadog-instrumentations/src/oracledb.js +39 -33
  21. package/packages/datadog-instrumentations/src/pg.js +38 -48
  22. package/packages/datadog-plugin-aerospike/src/index.js +11 -11
  23. package/packages/datadog-plugin-ai/src/index.js +17 -0
  24. package/packages/datadog-plugin-ai/src/tracing.js +33 -0
  25. package/packages/datadog-plugin-ai/src/utils.js +28 -0
  26. package/packages/datadog-plugin-amqp10/src/consumer.js +2 -2
  27. package/packages/datadog-plugin-amqp10/src/index.js +1 -1
  28. package/packages/datadog-plugin-amqp10/src/producer.js +3 -3
  29. package/packages/datadog-plugin-amqplib/src/client.js +3 -3
  30. package/packages/datadog-plugin-amqplib/src/consumer.js +2 -2
  31. package/packages/datadog-plugin-amqplib/src/index.js +1 -1
  32. package/packages/datadog-plugin-amqplib/src/producer.js +2 -2
  33. package/packages/datadog-plugin-apollo/src/gateway/execute.js +2 -4
  34. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +2 -4
  35. package/packages/datadog-plugin-apollo/src/gateway/index.js +1 -1
  36. package/packages/datadog-plugin-apollo/src/gateway/plan.js +2 -4
  37. package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +2 -4
  38. package/packages/datadog-plugin-apollo/src/gateway/request.js +2 -4
  39. package/packages/datadog-plugin-apollo/src/gateway/validate.js +2 -4
  40. package/packages/datadog-plugin-apollo/src/index.js +1 -1
  41. package/packages/datadog-plugin-avsc/src/index.js +2 -2
  42. package/packages/datadog-plugin-aws-sdk/src/base.js +70 -46
  43. package/packages/datadog-plugin-aws-sdk/src/index.js +1 -3
  44. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +1 -3
  45. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
  46. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  47. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +3 -3
  48. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  49. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +22 -20
  50. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  51. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  52. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +3 -3
  53. package/packages/datadog-plugin-aws-sdk/src/services/sfn.js +1 -1
  54. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +3 -3
  55. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +17 -15
  56. package/packages/datadog-plugin-aws-sdk/src/services/states.js +1 -1
  57. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  58. package/packages/datadog-plugin-azure-functions/src/index.js +5 -5
  59. package/packages/datadog-plugin-azure-service-bus/src/index.js +1 -1
  60. package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
  61. package/packages/datadog-plugin-bunyan/src/index.js +3 -5
  62. package/packages/datadog-plugin-cassandra-driver/src/index.js +3 -3
  63. package/packages/datadog-plugin-child_process/src/index.js +2 -2
  64. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +1 -3
  65. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +1 -3
  66. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +1 -1
  67. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +1 -3
  68. package/packages/datadog-plugin-connect/src/index.js +1 -3
  69. package/packages/datadog-plugin-couchbase/src/index.js +39 -19
  70. package/packages/datadog-plugin-cucumber/src/index.js +1 -3
  71. package/packages/datadog-plugin-cypress/src/index.js +1 -3
  72. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -3
  73. package/packages/datadog-plugin-dns/src/index.js +1 -1
  74. package/packages/datadog-plugin-dns/src/lookup.js +2 -2
  75. package/packages/datadog-plugin-dns/src/lookup_service.js +2 -2
  76. package/packages/datadog-plugin-dns/src/resolve.js +2 -2
  77. package/packages/datadog-plugin-dns/src/reverse.js +2 -2
  78. package/packages/datadog-plugin-elasticsearch/src/index.js +1 -1
  79. package/packages/datadog-plugin-express/src/code_origin.js +1 -3
  80. package/packages/datadog-plugin-express/src/index.js +1 -1
  81. package/packages/datadog-plugin-express/src/tracing.js +1 -3
  82. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -3
  83. package/packages/datadog-plugin-fastify/src/index.js +1 -1
  84. package/packages/datadog-plugin-fastify/src/tracing.js +18 -3
  85. package/packages/datadog-plugin-fetch/src/index.js +2 -2
  86. package/packages/datadog-plugin-find-my-way/src/index.js +1 -3
  87. package/packages/datadog-plugin-fs/src/index.js +2 -2
  88. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +3 -3
  89. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  90. package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +1 -1
  91. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +2 -2
  92. package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +1 -1
  93. package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +2 -4
  94. package/packages/datadog-plugin-graphql/src/execute.js +16 -9
  95. package/packages/datadog-plugin-graphql/src/index.js +1 -1
  96. package/packages/datadog-plugin-graphql/src/parse.js +12 -7
  97. package/packages/datadog-plugin-graphql/src/resolve.js +50 -16
  98. package/packages/datadog-plugin-graphql/src/validate.js +13 -7
  99. package/packages/datadog-plugin-grpc/src/client.js +4 -4
  100. package/packages/datadog-plugin-grpc/src/index.js +1 -1
  101. package/packages/datadog-plugin-grpc/src/server.js +3 -3
  102. package/packages/datadog-plugin-hapi/src/index.js +1 -3
  103. package/packages/datadog-plugin-hono/src/index.js +1 -3
  104. package/packages/datadog-plugin-http/src/client.js +2 -2
  105. package/packages/datadog-plugin-http/src/index.js +1 -1
  106. package/packages/datadog-plugin-http/src/server.js +3 -7
  107. package/packages/datadog-plugin-http2/src/client.js +2 -2
  108. package/packages/datadog-plugin-http2/src/index.js +1 -1
  109. package/packages/datadog-plugin-http2/src/server.js +22 -11
  110. package/packages/datadog-plugin-ioredis/src/index.js +1 -3
  111. package/packages/datadog-plugin-iovalkey/src/index.js +2 -4
  112. package/packages/datadog-plugin-jest/src/index.js +1 -3
  113. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -2
  114. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -2
  115. package/packages/datadog-plugin-kafkajs/src/index.js +1 -1
  116. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -3
  117. package/packages/datadog-plugin-koa/src/index.js +1 -3
  118. package/packages/datadog-plugin-langchain/src/index.js +2 -2
  119. package/packages/datadog-plugin-langchain/src/tracing.js +30 -48
  120. package/packages/datadog-plugin-mariadb/src/index.js +2 -2
  121. package/packages/datadog-plugin-memcached/src/index.js +1 -1
  122. package/packages/datadog-plugin-microgateway-core/src/index.js +4 -4
  123. package/packages/datadog-plugin-mocha/src/index.js +1 -3
  124. package/packages/datadog-plugin-moleculer/src/client.js +2 -2
  125. package/packages/datadog-plugin-moleculer/src/index.js +1 -1
  126. package/packages/datadog-plugin-moleculer/src/server.js +2 -2
  127. package/packages/datadog-plugin-mongodb-core/src/index.js +9 -5
  128. package/packages/datadog-plugin-mongoose/src/index.js +20 -0
  129. package/packages/datadog-plugin-mysql/src/index.js +2 -2
  130. package/packages/datadog-plugin-mysql2/src/index.js +1 -1
  131. package/packages/datadog-plugin-net/src/index.js +1 -1
  132. package/packages/datadog-plugin-net/src/ipc.js +2 -2
  133. package/packages/datadog-plugin-net/src/tcp.js +2 -2
  134. package/packages/datadog-plugin-next/src/index.js +1 -3
  135. package/packages/datadog-plugin-nyc/src/index.js +1 -3
  136. package/packages/datadog-plugin-openai/src/index.js +1 -1
  137. package/packages/datadog-plugin-openai/src/tracing.js +7 -411
  138. package/packages/datadog-plugin-opensearch/src/index.js +1 -3
  139. package/packages/datadog-plugin-oracledb/src/index.js +9 -5
  140. package/packages/datadog-plugin-pg/src/index.js +8 -5
  141. package/packages/datadog-plugin-pino/src/index.js +3 -5
  142. package/packages/datadog-plugin-playwright/src/index.js +1 -3
  143. package/packages/datadog-plugin-prisma/src/client.js +4 -6
  144. package/packages/datadog-plugin-prisma/src/engine.js +3 -3
  145. package/packages/datadog-plugin-prisma/src/index.js +1 -1
  146. package/packages/datadog-plugin-protobufjs/src/index.js +2 -6
  147. package/packages/datadog-plugin-redis/src/index.js +2 -2
  148. package/packages/datadog-plugin-restify/src/index.js +1 -3
  149. package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
  150. package/packages/datadog-plugin-rhea/src/index.js +1 -1
  151. package/packages/datadog-plugin-rhea/src/producer.js +2 -2
  152. package/packages/datadog-plugin-router/src/index.js +1 -3
  153. package/packages/datadog-plugin-selenium/src/index.js +1 -3
  154. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  155. package/packages/datadog-plugin-tedious/src/index.js +3 -3
  156. package/packages/datadog-plugin-undici/src/index.js +2 -4
  157. package/packages/datadog-plugin-vitest/src/index.js +1 -3
  158. package/packages/datadog-plugin-web/src/index.js +1 -3
  159. package/packages/datadog-plugin-winston/src/index.js +3 -5
  160. package/packages/dd-trace/src/appsec/channels.js +1 -0
  161. package/packages/dd-trace/src/appsec/graphql.js +14 -12
  162. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +14 -7
  163. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
  164. package/packages/dd-trace/src/appsec/recommended.json +271 -2
  165. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  166. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +1 -3
  167. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +1 -3
  168. package/packages/dd-trace/src/config.js +1 -1
  169. package/packages/dd-trace/src/datastreams/checkpointer.js +23 -2
  170. package/packages/dd-trace/src/datastreams/processor.js +4 -3
  171. package/packages/dd-trace/src/guardrails/telemetry.js +18 -2
  172. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +351 -0
  173. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +179 -0
  174. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +30 -50
  175. package/packages/dd-trace/src/llmobs/plugins/openai.js +3 -5
  176. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +3 -5
  177. package/packages/dd-trace/src/llmobs/writers/base.js +3 -2
  178. package/packages/dd-trace/src/opentracing/propagation/text_map.js +25 -2
  179. package/packages/dd-trace/src/opentracing/span_context.js +4 -0
  180. package/packages/dd-trace/src/plugin_manager.js +8 -4
  181. package/packages/dd-trace/src/plugins/apollo.js +3 -3
  182. package/packages/dd-trace/src/plugins/cache.js +1 -1
  183. package/packages/dd-trace/src/plugins/client.js +3 -3
  184. package/packages/dd-trace/src/plugins/consumer.js +3 -3
  185. package/packages/dd-trace/src/plugins/database.js +2 -2
  186. package/packages/dd-trace/src/plugins/index.js +2 -0
  187. package/packages/dd-trace/src/plugins/log_plugin.js +1 -5
  188. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  189. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  190. package/packages/dd-trace/src/plugins/producer.js +3 -3
  191. package/packages/dd-trace/src/plugins/server.js +3 -3
  192. package/packages/dd-trace/src/plugins/storage.js +1 -1
  193. package/packages/dd-trace/src/plugins/tracing.js +24 -6
  194. package/packages/dd-trace/src/plugins/util/ci.js +11 -7
  195. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +15 -19
  196. package/packages/dd-trace/src/plugins/util/ip_extractor.js +44 -3
  197. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  198. package/packages/dd-trace/src/plugins/util/web.js +26 -7
  199. package/packages/dd-trace/src/profiling/config.js +2 -0
  200. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +2 -21
  201. package/packages/dd-trace/src/profiling/libuv-size.js +49 -0
  202. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +2 -6
  203. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +1 -3
  204. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +1 -3
  205. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +1 -3
  206. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +1 -3
  207. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +24 -23
  208. package/packages/dd-trace/src/profiling/profilers/event_plugins/fs.js +3 -9
  209. package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +3 -9
  210. package/packages/dd-trace/src/profiling/profilers/events.js +83 -64
  211. package/packages/dd-trace/src/profiling/profilers/poisson.js +105 -0
  212. package/packages/dd-trace/src/profiling/profilers/wall.js +3 -3
  213. package/packages/dd-trace/src/remote_config/manager.js +1 -1
  214. package/packages/dd-trace/src/supported-configurations.json +2 -0
  215. package/packages/dd-trace/src/tracer_metadata.js +1 -1
@@ -5,23 +5,7 @@
5
5
  const os = require('os')
6
6
  const perf = require('perf_hooks').performance
7
7
  const version = require('../../../../../package.json').version
8
- const { getEnvironmentVariable } = require('../../config-helper')
9
-
10
- const libuvThreadPoolSize = (() => {
11
- const ss = getEnvironmentVariable('UV_THREADPOOL_SIZE')
12
- if (ss === undefined) {
13
- // Backend will apply the default size based on Node version.
14
- return
15
- }
16
- // libuv uses atoi to parse the value, which is almost the same as parseInt, except that parseInt
17
- // will return NaN on invalid input, while atoi will return 0. This is handled at return.
18
- const s = Number.parseInt(ss)
19
- // We don't interpret the value further here in the library. Backend will interpret the number
20
- // based on Node version. In all currently known Node versions, 0 results in 1 worker thread,
21
- // negative values (because they're assigned to an unsigned int) become very high positive values,
22
- // and the value is finally capped at 1024.
23
- return Number.isNaN(s) ? 0 : s
24
- })()
8
+ const { availableParallelism, libuvThreadPoolSize } = require('../libuv-size')
25
9
 
26
10
  class EventSerializer {
27
11
  constructor ({ env, host, service, version, libraryInjected, activation } = {}) {
@@ -77,10 +61,7 @@ class EventSerializer {
77
61
  version
78
62
  },
79
63
  runtime: {
80
- // os.availableParallelism only available in node 18.14.0/19.4.0 and above
81
- available_processors: typeof os.availableParallelism === 'function'
82
- ? os.availableParallelism()
83
- : os.cpus().length,
64
+ available_processors: availableParallelism(),
84
65
  // Using `nodejs` for consistency with the existing `runtime` tag.
85
66
  // Note that the event `family` property uses `node`, as that's what's
86
67
  // proscribed by the Intake API, but that's an internal enum and is
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { getEnvironmentVariable } = require('../config-helper')
4
+ const os = require('node:os')
5
+
6
+ function getLibuvThreadPoolSize (envVar) {
7
+ if (envVar === undefined) {
8
+ return
9
+ }
10
+ // libuv uses atoi to parse the value, which is almost the same as parseInt, except that parseInt
11
+ // will return NaN on invalid input, while atoi will return 0. This is handled at return.
12
+ const s = Number.parseInt(envVar, 10)
13
+ // We don't interpret the value further here in the library. Backend will interpret the number
14
+ // based on Node version.
15
+ return Number.isNaN(s) ? 0 : s
16
+ }
17
+
18
+ const libuvThreadPoolSize = getLibuvThreadPoolSize(getEnvironmentVariable('UV_THREADPOOL_SIZE'))
19
+
20
+ function getEffectiveLibuvThreadCount (size) {
21
+ // In all currently known Node versions, 0 results in 1 worker thread, negative values (because
22
+ // they're assigned to an unsigned int) become very high positive values, and the value is finally
23
+ // capped at 1024.
24
+ if (size === undefined) {
25
+ return 4
26
+ } else if (size < 0 || size > 1024) {
27
+ return 1024
28
+ } else if (size === 0) {
29
+ return 1
30
+ }
31
+ return size
32
+ }
33
+
34
+ const effectiveLibuvThreadCount = getEffectiveLibuvThreadCount(libuvThreadPoolSize)
35
+
36
+ function availableParallelism () {
37
+ // os.availableParallelism only available in node 18.14.0/19.4.0 and above
38
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
39
+ return typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length
40
+ }
41
+
42
+ module.exports = {
43
+ availableParallelism,
44
+ effectiveLibuvThreadCount,
45
+ libuvThreadPoolSize,
46
+ // Only used for testing
47
+ getLibuvThreadPoolSize,
48
+ getEffectiveLibuvThreadCount
49
+ }
@@ -3,13 +3,9 @@
3
3
  const EventPlugin = require('./event')
4
4
 
5
5
  class DNSPlugin extends EventPlugin {
6
- static get id () {
7
- return 'dns'
8
- }
6
+ static id = 'dns'
9
7
 
10
- static get entryType () {
11
- return 'dns'
12
- }
8
+ static entryType = 'dns'
13
9
  }
14
10
 
15
11
  module.exports = DNSPlugin
@@ -3,9 +3,7 @@
3
3
  const DNSPlugin = require('./dns')
4
4
 
5
5
  class DNSLookupPlugin extends DNSPlugin {
6
- static get operation () {
7
- return 'lookup'
8
- }
6
+ static operation = 'lookup'
9
7
 
10
8
  extendEvent (event, startEvent) {
11
9
  event.name = 'lookup'
@@ -3,9 +3,7 @@
3
3
  const DNSPlugin = require('./dns')
4
4
 
5
5
  class DNSLookupServicePlugin extends DNSPlugin {
6
- static get operation () {
7
- return 'lookup_service'
8
- }
6
+ static operation = 'lookup_service'
9
7
 
10
8
  extendEvent (event, startEvent) {
11
9
  event.name = 'lookupService'
@@ -5,9 +5,7 @@ const DNSPlugin = require('./dns')
5
5
  const queryNames = new Map()
6
6
 
7
7
  class DNSResolvePlugin extends DNSPlugin {
8
- static get operation () {
9
- return 'resolve'
10
- }
8
+ static operation = 'resolve'
11
9
 
12
10
  extendEvent (event, startEvent) {
13
11
  const rrtype = startEvent.args[1]
@@ -3,9 +3,7 @@
3
3
  const DNSPlugin = require('./dns')
4
4
 
5
5
  class DNSReversePlugin extends DNSPlugin {
6
- static get operation () {
7
- return 'reverse'
8
- }
6
+ static operation = 'reverse'
9
7
 
10
8
  extendEvent (event, startEvent) {
11
9
  event.name = 'getHostByAddr'
@@ -7,54 +7,55 @@ const { performance } = require('perf_hooks')
7
7
  // start/error/finish methods to the appropriate diagnostic channels.
8
8
  // TODO: Decouple this from TracingPlugin.
9
9
  class EventPlugin extends TracingPlugin {
10
+ #eventHandler
11
+ #eventFilter
12
+ #dataSymbol
13
+ #entryType
14
+
10
15
  constructor (eventHandler, eventFilter) {
11
16
  super()
12
- this.eventHandler = eventHandler
13
- this.eventFilter = eventFilter
14
- this.contextData = new WeakMap()
15
- this.entryType = this.constructor.entryType
17
+ this.#eventHandler = eventHandler
18
+ this.#eventFilter = eventFilter
19
+ this.#entryType = this.constructor.entryType
20
+ this.#dataSymbol = Symbol(`dd-trace.profiling.event.${this.#entryType}.${this.constructor.operation}`)
16
21
  }
17
22
 
18
23
  start (ctx) {
19
- this.contextData.set(ctx, {
20
- startEvent: ctx,
21
- startTime: performance.now()
22
- })
24
+ ctx[this.#dataSymbol] = performance.now()
23
25
  }
24
26
 
25
27
  error (ctx) {
26
- const data = this.contextData.get(ctx)
27
- if (data) {
28
- data.error = true
29
- }
28
+ // We don't emit perf events for failed operations
29
+ ctx[this.#dataSymbol] = undefined
30
30
  }
31
31
 
32
32
  finish (ctx) {
33
- const data = this.contextData.get(ctx)
34
-
35
- if (!data) return
33
+ const startTime = ctx[this.#dataSymbol]
34
+ if (startTime === undefined) {
35
+ return
36
+ }
37
+ ctx[this.#dataSymbol] = undefined
36
38
 
37
- const { startEvent, startTime, error } = data
38
- if (error || this.ignoreEvent(startEvent)) {
39
- return // don't emit perf events for failed operations or ignored events
39
+ if (this.ignoreEvent(ctx)) {
40
+ return // don't emit perf events for ignored events
40
41
  }
41
42
 
42
43
  const duration = performance.now() - startTime
43
44
  const event = {
44
- entryType: this.entryType,
45
+ entryType: this.#entryType,
45
46
  startTime,
46
47
  duration
47
48
  }
48
49
 
49
- if (!this.eventFilter(event)) {
50
+ if (!this.#eventFilter(event)) {
50
51
  return
51
52
  }
52
53
 
53
54
  const context = (ctx.currentStore?.span || this.activeSpan)?.context()
54
- event._ddSpanId = context?.toSpanId()
55
- event._ddRootSpanId = context?._trace.started[0]?.context().toSpanId() || event._ddSpanId
55
+ event._ddSpanId = context?.toBigIntSpanId()
56
+ event._ddRootSpanId = context?._trace.started[0]?.context().toBigIntSpanId() || event._ddSpanId
56
57
 
57
- this.eventHandler(this.extendEvent(event, startEvent))
58
+ this.#eventHandler(this.extendEvent(event, ctx))
58
59
  }
59
60
 
60
61
  ignoreEvent () {
@@ -19,17 +19,11 @@ const allowedParams = new Set([
19
19
  ])
20
20
 
21
21
  class FilesystemPlugin extends EventPlugin {
22
- static get id () {
23
- return 'fs'
24
- }
22
+ static id = 'fs'
25
23
 
26
- static get operation () {
27
- return 'operation'
28
- }
24
+ static operation = 'operation'
29
25
 
30
- static get entryType () {
31
- return 'fs'
32
- }
26
+ static entryType = 'fs'
33
27
 
34
28
  ignoreEvent (event) {
35
29
  // Don't care about sync events, they show up in the event loop samples anyway
@@ -3,17 +3,11 @@
3
3
  const EventPlugin = require('./event')
4
4
 
5
5
  class NetPlugin extends EventPlugin {
6
- static get id () {
7
- return 'net'
8
- }
6
+ static id = 'net'
9
7
 
10
- static get operation () {
11
- return 'tcp'
12
- }
8
+ static operation = 'tcp'
13
9
 
14
- static get entryType () {
15
- return 'net'
16
- }
10
+ static entryType = 'net'
17
11
 
18
12
  extendEvent (event, { options }) {
19
13
  event.name = 'connect'
@@ -3,9 +3,12 @@
3
3
  const { performance, constants, PerformanceObserver } = require('perf_hooks')
4
4
  const { END_TIMESTAMP_LABEL, SPAN_ID_LABEL, LOCAL_ROOT_SPAN_ID_LABEL, encodeProfileAsync } = require('./shared')
5
5
  const { Function, Label, Line, Location, Profile, Sample, StringTable, ValueType } = require('pprof-format')
6
-
6
+ const PoissonProcessSamplingFilter = require('./poisson')
7
+ const { availableParallelism, effectiveLibuvThreadCount } = require('../libuv-size')
7
8
  // perf_hooks uses millis, with fractional part representing nanos. We emit nanos into the pprof file.
8
9
  const MS_TO_NS = 1_000_000
10
+ // The number of sampling intervals that need to pass before we reset the Poisson process sampling instant.
11
+ const POISSON_RESET_FACTOR = 2
9
12
 
10
13
  // While this is an "events profiler", meaning it emits a pprof file based on events observed as
11
14
  // perf_hooks events, the emitted pprof file uses the type "timeline".
@@ -38,6 +41,24 @@ function labelFromStrStr (stringTable, keyStr, valStr) {
38
41
  return labelFromStr(stringTable, stringTable.dedup(keyStr), valStr)
39
42
  }
40
43
 
44
+ function getSamplingIntervalMillis (options) {
45
+ return (options.samplingInterval || 1e3 / 99) // 99Hz
46
+ }
47
+
48
+ function getMaxSamples (options) {
49
+ const cpuSamplingInterval = getSamplingIntervalMillis(options)
50
+ const flushInterval = options.flushInterval || 65 * 1e3 // 60 seconds
51
+ const maxCpuSamples = flushInterval / cpuSamplingInterval
52
+
53
+ // The lesser of max parallelism and libuv thread pool size, plus one so we can detect
54
+ // oversubscription on libuv thread pool, plus another one for GC.
55
+ const factor = Math.max(1, Math.min(availableParallelism(), effectiveLibuvThreadCount)) + 2
56
+
57
+ // Let's not go overboard with too large limit and cap it at 100k. With current defaults, the
58
+ // value will be 65000/10.1*(4+2) = 38613.
59
+ return Math.min(100_000, Math.floor(maxCpuSamples * factor))
60
+ }
61
+
41
62
  class GCDecorator {
42
63
  constructor (stringTable) {
43
64
  this.stringTable = stringTable
@@ -181,12 +202,15 @@ const decoratorTypes = {
181
202
 
182
203
  // Translates performance entries into pprof samples.
183
204
  class EventSerializer {
184
- constructor () {
205
+ #sampleCount = 0
206
+
207
+ constructor (maxSamples) {
185
208
  this.stringTable = new StringTable()
186
209
  this.samples = []
187
210
  this.locations = []
188
211
  this.functions = []
189
212
  this.decorators = {}
213
+ this.maxSamples = maxSamples
190
214
 
191
215
  // A synthetic single-frame location to serve as the location for timeline
192
216
  // samples. We need these as the profiling backend (mimicking official pprof
@@ -204,6 +228,31 @@ class EventSerializer {
204
228
  }
205
229
 
206
230
  addEvent (item) {
231
+ if (this.samples.length < this.maxSamples) {
232
+ const sample = this.#createSample(item)
233
+ if (sample !== undefined) {
234
+ this.samples.push(sample)
235
+ this.#sampleCount++
236
+ }
237
+ } else {
238
+ this.#sampleCount++
239
+ // Reservoir sampling
240
+ const replacementIndex = Math.floor(Math.random() * this.#sampleCount)
241
+ if (replacementIndex < this.maxSamples) {
242
+ const sample = this.#createSample(item)
243
+ if (sample === undefined) {
244
+ this.#sampleCount-- // unlikely
245
+ } else {
246
+ // This will cause the samples to no longer be sorted in their array
247
+ // by their end time. This is fine as the backend has no ordering
248
+ // expectations.
249
+ this.samples[replacementIndex] = sample
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ #createSample (item) {
207
256
  const { entryType, startTime, duration, _ddSpanId, _ddRootSpanId } = item
208
257
  let decorator = this.decorators[entryType]
209
258
  if (!decorator) {
@@ -224,10 +273,11 @@ class EventSerializer {
224
273
  new Label({ key: this.timestampLabelKey, num: dateOffset + BigInt(Math.round(endTime * MS_TO_NS)) })
225
274
  ]
226
275
  if (_ddSpanId) {
227
- label.push(labelFromStr(this.stringTable, this.spanIdKey, _ddSpanId))
276
+ label.push(
277
+ new Label({ key: this.spanIdKey, num: _ddSpanId }))
228
278
  }
229
279
  if (_ddRootSpanId) {
230
- label.push(labelFromStr(this.stringTable, this.rootSpanIdKey, _ddRootSpanId))
280
+ label.push(new Label({ key: this.rootSpanIdKey, num: _ddRootSpanId }))
231
281
  }
232
282
 
233
283
  const sampleInput = {
@@ -236,7 +286,7 @@ class EventSerializer {
236
286
  label
237
287
  }
238
288
  decorator.decorateSample(sampleInput, item)
239
- this.samples.push(new Sample(sampleInput))
289
+ return new Sample(sampleInput)
240
290
  }
241
291
 
242
292
  createProfile (startDate, endDate) {
@@ -324,50 +374,13 @@ class DatadogInstrumentationEventSource {
324
374
  }
325
375
  }
326
376
 
327
- class CompositeEventSource {
328
- constructor (sources) {
329
- this.sources = sources
330
- }
331
-
332
- start () {
333
- this.sources.forEach(s => s.start())
334
- }
335
-
336
- stop () {
337
- this.sources.forEach(s => s.stop())
338
- }
339
- }
340
-
341
- function createPossionProcessSamplingFilter (samplingIntervalMillis) {
342
- let nextSamplingInstant = performance.now()
343
- let currentSamplingInstant = 0
344
- setNextSamplingInstant()
345
-
346
- return event => {
347
- const endTime = event.startTime + event.duration
348
- while (endTime >= nextSamplingInstant) {
349
- setNextSamplingInstant()
350
- }
351
- // An event is sampled if it started before, and ended on or after a sampling instant. The above
352
- // while loop will ensure that the ending invariant is always true for the current sampling
353
- // instant so we don't have to test for it below. Across calls, the invariant also holds as long
354
- // as the events arrive in endTime order. This is true for events coming from
355
- // DatadogInstrumentationEventSource; they will be ordered by endTime by virtue of this method
356
- // being invoked synchronously with the plugins' finish() handler which evaluates
357
- // performance.now(). OTOH, events coming from NodeAPIEventSource (GC in typical setup) might be
358
- // somewhat delayed as they are queued by Node, so they can arrive out of order with regard to
359
- // events coming from the non-queued source. By omitting the endTime check, we will pass through
360
- // some short events that started and ended before the current sampling instant. OTOH, if we
361
- // were to check for this.currentSamplingInstant <= endTime, we would discard some long events
362
- // that also ended before the current sampling instant. We'd rather err on the side of including
363
- // some short events than excluding some long events.
364
- return event.startTime < currentSamplingInstant
365
- }
366
-
367
- function setNextSamplingInstant () {
368
- currentSamplingInstant = nextSamplingInstant
369
- nextSamplingInstant -= Math.log(1 - Math.random()) * samplingIntervalMillis
370
- }
377
+ function createPoissonProcessSamplingFilter (samplingIntervalMillis) {
378
+ const poissonFilter = new PoissonProcessSamplingFilter({
379
+ samplingInterval: samplingIntervalMillis,
380
+ resetInterval: samplingIntervalMillis * POISSON_RESET_FACTOR,
381
+ now: performance.now.bind(performance)
382
+ })
383
+ return poissonFilter.filter.bind(poissonFilter)
371
384
  }
372
385
 
373
386
  /**
@@ -376,45 +389,51 @@ function createPossionProcessSamplingFilter (samplingIntervalMillis) {
376
389
  */
377
390
  class EventsProfiler {
378
391
  type = 'events'
379
- eventSerializer = new EventSerializer()
392
+ #maxSamples
393
+ #eventSerializer
394
+ #eventSources
380
395
 
381
396
  constructor (options = {}) {
382
- const eventHandler = event => this.eventSerializer.addEvent(event)
397
+ this.#maxSamples = getMaxSamples(options)
398
+ this.#eventSerializer = new EventSerializer(this.#maxSamples)
399
+
400
+ const eventHandler = event => this.#eventSerializer.addEvent(event)
383
401
  const eventFilter = options.timelineSamplingEnabled
384
- // options.samplingInterval comes in microseconds, we need millis
385
- ? createPossionProcessSamplingFilter((options.samplingInterval ?? 1e6 / 99) / 1000)
386
- : _ => true
402
+ ? createPoissonProcessSamplingFilter(getSamplingIntervalMillis(options))
403
+ : () => true
387
404
  const filteringEventHandler = event => {
388
405
  if (eventFilter(event)) {
389
406
  eventHandler(event)
390
407
  }
391
408
  }
392
409
 
393
- this.eventSource = options.codeHotspotsEnabled
410
+ this.#eventSources = options.codeHotspotsEnabled
394
411
  // Use Datadog instrumentation to collect events with span IDs. Still use
395
412
  // Node API for GC events.
396
- ? new CompositeEventSource([
397
- new DatadogInstrumentationEventSource(eventHandler, eventFilter),
398
- new NodeApiEventSource(filteringEventHandler, ['gc'])
399
- ])
413
+ ? [
414
+ new DatadogInstrumentationEventSource(eventHandler, eventFilter),
415
+ new NodeApiEventSource(filteringEventHandler, ['gc']),
416
+ ]
400
417
  // Use Node API instrumentation to collect events without span IDs
401
- : new NodeApiEventSource(filteringEventHandler)
418
+ : [
419
+ new NodeApiEventSource(filteringEventHandler)
420
+ ]
402
421
  }
403
422
 
404
423
  start () {
405
- this.eventSource.start()
424
+ this.#eventSources.forEach(s => s.start())
406
425
  }
407
426
 
408
427
  stop () {
409
- this.eventSource.stop()
428
+ this.#eventSources.forEach(s => s.stop())
410
429
  }
411
430
 
412
431
  profile (restart, startDate, endDate) {
413
432
  if (!restart) {
414
433
  this.stop()
415
434
  }
416
- const thatEventSerializer = this.eventSerializer
417
- this.eventSerializer = new EventSerializer()
435
+ const thatEventSerializer = this.#eventSerializer
436
+ this.#eventSerializer = new EventSerializer(this.#maxSamples)
418
437
  return () => thatEventSerializer.createProfile(startDate, endDate)
419
438
  }
420
439
 
@@ -0,0 +1,105 @@
1
+ 'use strict'
2
+
3
+ class PoissonProcessSamplingFilter {
4
+ #currentSamplingInstant = 0
5
+ #nextSamplingInstant
6
+ #samplingInterval
7
+ #resetInterval
8
+ #now
9
+ #lastNow = Number.NEGATIVE_INFINITY
10
+ #samplingInstantCount = 0
11
+
12
+ constructor ({ samplingInterval, now, resetInterval }) {
13
+ if (samplingInterval <= 0) {
14
+ throw new RangeError(`samplingInterval (${samplingInterval}) must be greater than 0`)
15
+ }
16
+ if (resetInterval < samplingInterval) {
17
+ throw new RangeError(
18
+ `resetInterval (${resetInterval}) must be greater than samplingInterval (${samplingInterval})`
19
+ )
20
+ }
21
+ if (typeof now !== 'function') {
22
+ throw new TypeError('now must be a function')
23
+ }
24
+ this.#samplingInterval = samplingInterval
25
+ this.#resetInterval = resetInterval
26
+ this.#now = now
27
+ this.#nextSamplingInstant = this.#callNow()
28
+ this.#setNextSamplingInstant()
29
+ }
30
+
31
+ get currentSamplingInstant () {
32
+ return this.#currentSamplingInstant
33
+ }
34
+
35
+ get nextSamplingInstant () {
36
+ return this.#nextSamplingInstant
37
+ }
38
+
39
+ get samplingInstantCount () {
40
+ return this.#samplingInstantCount
41
+ }
42
+
43
+ #callNow () {
44
+ const nowValue = this.#now()
45
+ if (typeof nowValue !== 'number') {
46
+ throw new TypeError('now() must return a number')
47
+ }
48
+ if (nowValue < this.#lastNow) {
49
+ throw new RangeError('now() must return a value greater than or equal to the last returned value')
50
+ }
51
+ this.#lastNow = nowValue
52
+ return nowValue
53
+ }
54
+
55
+ filter (event) {
56
+ const endTime = event.startTime + event.duration
57
+ // We're using the end times of events as an approximation of current time as events are
58
+ // expected to be reported close to where they ended. If the end time (and thus, presumably, the
59
+ // current time) is past the next sampling instant, we make it the current sampling instant and
60
+ // compute the next sampling instant in its future.
61
+ if (endTime >= this.#nextSamplingInstant) {
62
+ // All observed events are supposed to have happened in the past. For purposes of advancing
63
+ // the next sampling instant, we cap endTime to now(). This protects us from advancing it far
64
+ // into future if we receive an event with erroneously long duration, which would also take
65
+ // many iterations of the below "while" loop.
66
+ const cappedEndTime = Math.min(endTime, this.#callNow())
67
+
68
+ // If nextSamplingInstant is far in cappedEndTime's past, first advance it close to it. This
69
+ // can happen if we didn't receive any events for a while. Since a Poisson process has no
70
+ // memory, we can reset it anytime. This will ensure that the "while" loop below runs at most
71
+ // few iterations.
72
+ const earliestContinuousPast = cappedEndTime - this.#resetInterval
73
+ if (this.#nextSamplingInstant < earliestContinuousPast) {
74
+ this.#nextSamplingInstant = earliestContinuousPast
75
+ }
76
+
77
+ // Advance the next sampling instant until it is in cappedEndTime's future.
78
+ while (cappedEndTime >= this.#nextSamplingInstant) {
79
+ this.#setNextSamplingInstant()
80
+ }
81
+ }
82
+ // An event is sampled if it started before, and ended on or after a sampling instant. The above
83
+ // while loop will ensure that the ending invariant is always true for the current sampling
84
+ // instant so we don't have to test for it below. Across calls, the invariant also holds as long
85
+ // as the events arrive in endTime order. This is true for events coming from
86
+ // DatadogInstrumentationEventSource; they will be ordered by endTime by virtue of this method
87
+ // being invoked synchronously with the plugins' finish() handler which evaluates
88
+ // performance.now(). OTOH, events coming from NodeAPIEventSource (GC in typical setup) might be
89
+ // somewhat delayed as they are queued by Node, so they can arrive out of order with regard to
90
+ // events coming from the non-queued source. By omitting the endTime check, we will pass through
91
+ // some short events that started and ended before the current sampling instant. OTOH, if we
92
+ // were to check for this.currentSamplingInstant <= endTime, we would discard some long events
93
+ // that also ended before the current sampling instant. We'd rather err on the side of including
94
+ // some short events than excluding some long events.
95
+ return event.startTime < this.#currentSamplingInstant
96
+ }
97
+
98
+ #setNextSamplingInstant () {
99
+ this.#currentSamplingInstant = this.#nextSamplingInstant
100
+ this.#nextSamplingInstant -= Math.log(1 - Math.random()) * this.#samplingInterval
101
+ this.#samplingInstantCount++
102
+ }
103
+ }
104
+
105
+ module.exports = PoissonProcessSamplingFilter
@@ -75,7 +75,7 @@ class NativeWallProfiler {
75
75
  _started = false
76
76
 
77
77
  constructor (options = {}) {
78
- this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
78
+ this._samplingIntervalMicros = (options.samplingInterval || 1e3 / 99) * 1000 // 99hz
79
79
  this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
80
80
  this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
81
81
  this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
@@ -215,10 +215,10 @@ class NativeWallProfiler {
215
215
 
216
216
  _updateContext (context) {
217
217
  if (context.spanId !== null && typeof context.spanId === 'object') {
218
- context.spanId = context.spanId.toString(10)
218
+ context.spanId = context.spanId.toBigInt()
219
219
  }
220
220
  if (context.rootSpanId !== null && typeof context.rootSpanId === 'object') {
221
- context.rootSpanId = context.rootSpanId.toString(10)
221
+ context.rootSpanId = context.rootSpanId.toBigInt()
222
222
  }
223
223
  if (context.webTags !== undefined && context.endpoint === undefined) {
224
224
  // endpoint may not be determined yet, but keep it as fallback
@@ -22,7 +22,7 @@ const kSupportsAckCallback = Symbol('kSupportsAckCallback')
22
22
  // There MUST NOT exist separate instances of RC clients in a tracer making separate ClientGetConfigsRequest
23
23
  // with their own separated Client.ClientState.
24
24
  class RemoteConfigManager extends EventEmitter {
25
- static get kPreUpdate () { return kPreUpdate }
25
+ static kPreUpdate = kPreUpdate
26
26
 
27
27
  constructor (config) {
28
28
  super()
@@ -129,6 +129,7 @@
129
129
  "DD_PROFILING_HEAP_ENABLED": ["A"],
130
130
  "DD_PROFILING_PROFILERS": ["A"],
131
131
  "DD_PROFILING_SOURCE_MAP": ["A"],
132
+ "DD_PROFILING_TIMELINE_ENABLED": ["A"],
132
133
  "DD_PROFILING_UPLOAD_PERIOD": ["A"],
133
134
  "DD_PROFILING_V8_PROFILER_BUG_WORKAROUND": ["A"],
134
135
  "DD_PROFILING_WALLTIME_ENABLED": ["A"],
@@ -160,6 +161,7 @@
160
161
  "DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED": ["A"],
161
162
  "DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED": ["A"],
162
163
  "DD_TRACE_AEROSPIKE_ENABLED": ["A"],
164
+ "DD_TRACE_AI_ENABLED": ["A"],
163
165
  "DD_TRACE_AGENT_PORT": ["A"],
164
166
  "DD_TRACE_AGENT_PROTOCOL_VERSION": ["A"],
165
167
  "DD_TRACE_AGENT_URL": ["A"],