dd-trace 4.47.1 → 4.49.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 (156) hide show
  1. package/LICENSE-3rdparty.csv +1 -1
  2. package/ext/types.d.ts +1 -0
  3. package/ext/types.js +1 -0
  4. package/index.d.ts +361 -0
  5. package/package.json +18 -13
  6. package/packages/datadog-code-origin/index.js +38 -0
  7. package/packages/datadog-core/index.js +2 -2
  8. package/packages/datadog-core/src/utils/src/parse-tags.js +33 -0
  9. package/packages/datadog-esbuild/index.js +4 -2
  10. package/packages/datadog-instrumentations/src/amqplib.js +65 -5
  11. package/packages/datadog-instrumentations/src/avsc.js +37 -0
  12. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  13. package/packages/datadog-instrumentations/src/child_process.js +144 -27
  14. package/packages/datadog-instrumentations/src/express.js +37 -4
  15. package/packages/datadog-instrumentations/src/fastify.js +12 -1
  16. package/packages/datadog-instrumentations/src/fs.js +27 -7
  17. package/packages/datadog-instrumentations/src/helpers/hooks.js +6 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +9 -0
  19. package/packages/datadog-instrumentations/src/jest.js +2 -1
  20. package/packages/datadog-instrumentations/src/kafkajs.js +123 -63
  21. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  22. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -2
  23. package/packages/datadog-instrumentations/src/multer.js +37 -0
  24. package/packages/datadog-instrumentations/src/mysql2.js +220 -1
  25. package/packages/datadog-instrumentations/src/openai.js +2 -2
  26. package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
  27. package/packages/datadog-instrumentations/src/url.js +84 -0
  28. package/packages/datadog-instrumentations/src/utils/src/extract-package-and-module-path.js +7 -4
  29. package/packages/datadog-instrumentations/src/winston.js +22 -0
  30. package/packages/datadog-plugin-amqplib/src/consumer.js +4 -4
  31. package/packages/datadog-plugin-avsc/src/index.js +9 -0
  32. package/packages/datadog-plugin-avsc/src/schema_iterator.js +169 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  34. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  35. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
  36. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  37. package/packages/datadog-plugin-azure-functions/src/index.js +77 -0
  38. package/packages/datadog-plugin-fastify/src/code_origin.js +31 -0
  39. package/packages/datadog-plugin-fastify/src/index.js +10 -12
  40. package/packages/datadog-plugin-fastify/src/tracing.js +19 -0
  41. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +8 -1
  42. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +8 -0
  43. package/packages/datadog-plugin-grpc/src/client.js +3 -0
  44. package/packages/datadog-plugin-grpc/src/server.js +3 -0
  45. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +6 -3
  46. package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -4
  47. package/packages/datadog-plugin-kafkajs/src/producer.js +10 -4
  48. package/packages/datadog-plugin-mocha/src/index.js +4 -1
  49. package/packages/datadog-plugin-openai/src/index.js +9 -1015
  50. package/packages/datadog-plugin-openai/src/tracing.js +1023 -0
  51. package/packages/datadog-plugin-protobufjs/src/index.js +14 -0
  52. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +180 -0
  53. package/packages/dd-trace/src/appsec/addresses.js +8 -1
  54. package/packages/dd-trace/src/appsec/channels.js +7 -1
  55. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +13 -1
  56. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +8 -1
  57. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  58. package/packages/dd-trace/src/appsec/iast/index.js +3 -0
  59. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  60. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +55 -7
  61. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
  62. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -2
  63. package/packages/dd-trace/src/appsec/index.js +61 -43
  64. package/packages/dd-trace/src/appsec/rasp/command_injection.js +49 -0
  65. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
  66. package/packages/dd-trace/src/appsec/rasp/index.js +27 -10
  67. package/packages/dd-trace/src/appsec/rasp/lfi.js +112 -0
  68. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +24 -4
  69. package/packages/dd-trace/src/appsec/rasp/ssrf.js +4 -3
  70. package/packages/dd-trace/src/appsec/rasp/utils.js +4 -2
  71. package/packages/dd-trace/src/appsec/recommended.json +3 -7
  72. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  73. package/packages/dd-trace/src/appsec/remote_config/index.js +10 -0
  74. package/packages/dd-trace/src/appsec/reporter.js +17 -9
  75. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -3
  76. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  77. package/packages/dd-trace/src/appsec/waf/waf_manager.js +4 -0
  78. package/packages/dd-trace/src/azure_metadata.js +120 -0
  79. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +97 -0
  80. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +90 -0
  81. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
  82. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +19 -1
  83. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +53 -0
  84. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +8 -1
  85. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +43 -0
  86. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
  87. package/packages/dd-trace/src/config.js +86 -6
  88. package/packages/dd-trace/src/constants.js +3 -1
  89. package/packages/dd-trace/src/datastreams/pathway.js +1 -0
  90. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +25 -17
  91. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  92. package/packages/dd-trace/src/debugger/devtools_client/index.js +52 -5
  93. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
  94. package/packages/dd-trace/src/debugger/devtools_client/send.js +29 -2
  95. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +187 -0
  96. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +40 -0
  97. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +252 -0
  98. package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +6 -0
  99. package/packages/dd-trace/src/debugger/devtools_client/state.js +19 -4
  100. package/packages/dd-trace/src/debugger/index.js +10 -3
  101. package/packages/dd-trace/src/exporters/common/request.js +8 -34
  102. package/packages/dd-trace/src/exporters/common/url-to-http-options-polyfill.js +31 -0
  103. package/packages/dd-trace/src/llmobs/constants/tags.js +34 -0
  104. package/packages/dd-trace/src/llmobs/constants/text.js +6 -0
  105. package/packages/dd-trace/src/llmobs/constants/writers.js +13 -0
  106. package/packages/dd-trace/src/llmobs/index.js +103 -0
  107. package/packages/dd-trace/src/llmobs/noop.js +82 -0
  108. package/packages/dd-trace/src/llmobs/plugins/base.js +65 -0
  109. package/packages/dd-trace/src/llmobs/plugins/openai.js +205 -0
  110. package/packages/dd-trace/src/llmobs/sdk.js +377 -0
  111. package/packages/dd-trace/src/llmobs/span_processor.js +195 -0
  112. package/packages/dd-trace/src/llmobs/storage.js +7 -0
  113. package/packages/dd-trace/src/llmobs/tagger.js +322 -0
  114. package/packages/dd-trace/src/llmobs/util.js +176 -0
  115. package/packages/dd-trace/src/llmobs/writers/base.js +111 -0
  116. package/packages/dd-trace/src/llmobs/writers/evaluations.js +29 -0
  117. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +23 -0
  118. package/packages/dd-trace/src/llmobs/writers/spans/agentless.js +17 -0
  119. package/packages/dd-trace/src/llmobs/writers/spans/base.js +49 -0
  120. package/packages/dd-trace/src/noop/proxy.js +3 -0
  121. package/packages/dd-trace/src/noop/span.js +3 -0
  122. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  123. package/packages/dd-trace/src/opentelemetry/tracer.js +1 -0
  124. package/packages/dd-trace/src/opentracing/propagation/text_map.js +73 -12
  125. package/packages/dd-trace/src/opentracing/span.js +12 -0
  126. package/packages/dd-trace/src/opentracing/tracer.js +8 -1
  127. package/packages/dd-trace/src/payload-tagging/config/aws.json +71 -3
  128. package/packages/dd-trace/src/payload-tagging/index.js +1 -1
  129. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +2094 -0
  130. package/packages/dd-trace/src/plugin_manager.js +4 -2
  131. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
  132. package/packages/dd-trace/src/plugins/index.js +3 -0
  133. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  134. package/packages/dd-trace/src/plugins/outbound.js +9 -0
  135. package/packages/dd-trace/src/plugins/schema.js +35 -0
  136. package/packages/dd-trace/src/plugins/util/ci.js +23 -1
  137. package/packages/dd-trace/src/plugins/util/serverless.js +7 -0
  138. package/packages/dd-trace/src/plugins/util/stacktrace.js +94 -0
  139. package/packages/dd-trace/src/plugins/util/tags.js +7 -0
  140. package/packages/dd-trace/src/plugins/util/test.js +20 -22
  141. package/packages/dd-trace/src/plugins/util/web.js +6 -4
  142. package/packages/dd-trace/src/priority_sampler.js +16 -0
  143. package/packages/dd-trace/src/profiling/config.js +3 -1
  144. package/packages/dd-trace/src/profiling/exporters/agent.js +7 -5
  145. package/packages/dd-trace/src/profiling/profiler.js +24 -14
  146. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +95 -66
  148. package/packages/dd-trace/src/proxy.js +20 -1
  149. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  150. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  151. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  152. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  153. package/packages/dd-trace/src/span_processor.js +5 -0
  154. package/packages/dd-trace/src/telemetry/index.js +11 -1
  155. package/packages/datadog-core/src/storage/async_resource.js +0 -108
  156. package/packages/datadog-core/src/storage/index.js +0 -5
@@ -52,45 +52,59 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
52
52
  const send = producer.send
53
53
  const bootstrapServers = this._brokers
54
54
 
55
- producer.send = function () {
56
- const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
55
+ const kafkaClusterIdPromise = getKafkaClusterId(this)
57
56
 
58
- return innerAsyncResource.runInAsyncScope(() => {
59
- if (!producerStartCh.hasSubscribers) {
60
- return send.apply(this, arguments)
61
- }
57
+ producer.send = function () {
58
+ const wrappedSend = (clusterId) => {
59
+ const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
62
60
 
63
- try {
64
- const { topic, messages = [] } = arguments[0]
65
- for (const message of messages) {
66
- if (message !== null && typeof message === 'object') {
67
- message.headers = message.headers || {}
68
- }
61
+ return innerAsyncResource.runInAsyncScope(() => {
62
+ if (!producerStartCh.hasSubscribers) {
63
+ return send.apply(this, arguments)
69
64
  }
70
- producerStartCh.publish({ topic, messages, bootstrapServers })
71
-
72
- const result = send.apply(this, arguments)
73
-
74
- result.then(
75
- innerAsyncResource.bind(res => {
76
- producerFinishCh.publish(undefined)
77
- producerCommitCh.publish(res)
78
- }),
79
- innerAsyncResource.bind(err => {
80
- if (err) {
81
- producerErrorCh.publish(err)
65
+
66
+ try {
67
+ const { topic, messages = [] } = arguments[0]
68
+ for (const message of messages) {
69
+ if (message !== null && typeof message === 'object') {
70
+ message.headers = message.headers || {}
82
71
  }
83
- producerFinishCh.publish(undefined)
84
- })
85
- )
72
+ }
73
+ producerStartCh.publish({ topic, messages, bootstrapServers, clusterId })
86
74
 
87
- return result
88
- } catch (e) {
89
- producerErrorCh.publish(e)
90
- producerFinishCh.publish(undefined)
91
- throw e
92
- }
93
- })
75
+ const result = send.apply(this, arguments)
76
+
77
+ result.then(
78
+ innerAsyncResource.bind(res => {
79
+ producerFinishCh.publish(undefined)
80
+ producerCommitCh.publish(res)
81
+ }),
82
+ innerAsyncResource.bind(err => {
83
+ if (err) {
84
+ producerErrorCh.publish(err)
85
+ }
86
+ producerFinishCh.publish(undefined)
87
+ })
88
+ )
89
+
90
+ return result
91
+ } catch (e) {
92
+ producerErrorCh.publish(e)
93
+ producerFinishCh.publish(undefined)
94
+ throw e
95
+ }
96
+ })
97
+ }
98
+
99
+ if (!isPromise(kafkaClusterIdPromise)) {
100
+ // promise is already resolved
101
+ return wrappedSend(kafkaClusterIdPromise)
102
+ } else {
103
+ // promise is not resolved
104
+ return kafkaClusterIdPromise.then((clusterId) => {
105
+ return wrappedSend(clusterId)
106
+ })
107
+ }
94
108
  }
95
109
  return producer
96
110
  })
@@ -100,15 +114,17 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
100
114
  return createConsumer.apply(this, arguments)
101
115
  }
102
116
 
103
- const eachMessageExtractor = (args) => {
117
+ const kafkaClusterIdPromise = getKafkaClusterId(this)
118
+
119
+ const eachMessageExtractor = (args, clusterId) => {
104
120
  const { topic, partition, message } = args[0]
105
- return { topic, partition, message, groupId }
121
+ return { topic, partition, message, groupId, clusterId }
106
122
  }
107
123
 
108
- const eachBatchExtractor = (args) => {
124
+ const eachBatchExtractor = (args, clusterId) => {
109
125
  const { batch } = args[0]
110
126
  const { topic, partition, messages } = batch
111
- return { topic, partition, messages, groupId }
127
+ return { topic, partition, messages, groupId, clusterId }
112
128
  }
113
129
 
114
130
  const consumer = createConsumer.apply(this, arguments)
@@ -116,43 +132,53 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
116
132
  consumer.on(consumer.events.COMMIT_OFFSETS, commitsFromEvent)
117
133
 
118
134
  const run = consumer.run
119
-
120
135
  const groupId = arguments[0].groupId
136
+
121
137
  consumer.run = function ({ eachMessage, eachBatch, ...runArgs }) {
122
- eachMessage = wrapFunction(
123
- eachMessage,
124
- consumerStartCh,
125
- consumerFinishCh,
126
- consumerErrorCh,
127
- eachMessageExtractor
128
- )
129
-
130
- eachBatch = wrapFunction(
131
- eachBatch,
132
- batchConsumerStartCh,
133
- batchConsumerFinishCh,
134
- batchConsumerErrorCh,
135
- eachBatchExtractor
136
- )
137
-
138
- return run({
139
- eachMessage,
140
- eachBatch,
141
- ...runArgs
142
- })
138
+ const wrapConsume = (clusterId) => {
139
+ return run({
140
+ eachMessage: wrappedCallback(
141
+ eachMessage,
142
+ consumerStartCh,
143
+ consumerFinishCh,
144
+ consumerErrorCh,
145
+ eachMessageExtractor,
146
+ clusterId
147
+ ),
148
+ eachBatch: wrappedCallback(
149
+ eachBatch,
150
+ batchConsumerStartCh,
151
+ batchConsumerFinishCh,
152
+ batchConsumerErrorCh,
153
+ eachBatchExtractor,
154
+ clusterId
155
+ ),
156
+ ...runArgs
157
+ })
158
+ }
159
+
160
+ if (!isPromise(kafkaClusterIdPromise)) {
161
+ // promise is already resolved
162
+ return wrapConsume(kafkaClusterIdPromise)
163
+ } else {
164
+ // promise is not resolved
165
+ return kafkaClusterIdPromise.then((clusterId) => {
166
+ return wrapConsume(clusterId)
167
+ })
168
+ }
143
169
  }
144
-
145
170
  return consumer
146
171
  })
147
172
  return Kafka
148
173
  })
149
174
 
150
- const wrapFunction = (fn, startCh, finishCh, errorCh, extractArgs) => {
175
+ const wrappedCallback = (fn, startCh, finishCh, errorCh, extractArgs, clusterId) => {
151
176
  return typeof fn === 'function'
152
177
  ? function (...args) {
153
178
  const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
154
179
  return innerAsyncResource.runInAsyncScope(() => {
155
- const extractedArgs = extractArgs(args)
180
+ const extractedArgs = extractArgs(args, clusterId)
181
+
156
182
  startCh.publish(extractedArgs)
157
183
  try {
158
184
  const result = fn.apply(this, args)
@@ -179,3 +205,37 @@ const wrapFunction = (fn, startCh, finishCh, errorCh, extractArgs) => {
179
205
  }
180
206
  : fn
181
207
  }
208
+
209
+ const getKafkaClusterId = (kafka) => {
210
+ if (kafka._ddKafkaClusterId) {
211
+ return kafka._ddKafkaClusterId
212
+ }
213
+
214
+ if (!kafka.admin) {
215
+ return null
216
+ }
217
+
218
+ const admin = kafka.admin()
219
+
220
+ if (!admin.describeCluster) {
221
+ return null
222
+ }
223
+
224
+ return admin.connect()
225
+ .then(() => {
226
+ return admin.describeCluster()
227
+ })
228
+ .then((clusterInfo) => {
229
+ const clusterId = clusterInfo?.clusterId
230
+ kafka._ddKafkaClusterId = clusterId
231
+ admin.disconnect()
232
+ return clusterId
233
+ })
234
+ .catch((error) => {
235
+ throw error
236
+ })
237
+ }
238
+
239
+ function isPromise (obj) {
240
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
241
+ }
@@ -1,6 +1,6 @@
1
1
  const { addHook, channel } = require('../helpers/instrument')
2
2
  const shimmer = require('../../../datadog-shimmer')
3
- const { getCallSites } = require('../../../dd-trace/src/plugins/util/test')
3
+ const { getCallSites } = require('../../../dd-trace/src/plugins/util/stacktrace')
4
4
  const { testToStartLine } = require('./utils')
5
5
 
6
6
  const parameterizedTestCh = channel('ci:mocha:test:parameterize')
@@ -280,12 +280,12 @@ function getOnFailHandler (isMain) {
280
280
  }
281
281
 
282
282
  function getOnTestRetryHandler () {
283
- return function (test) {
283
+ return function (test, err) {
284
284
  const asyncResource = getTestAsyncResource(test)
285
285
  if (asyncResource) {
286
286
  const isFirstAttempt = test._currentRetry === 0
287
287
  asyncResource.runInAsyncScope(() => {
288
- testRetryCh.publish(isFirstAttempt)
288
+ testRetryCh.publish({ isFirstAttempt, err })
289
289
  })
290
290
  }
291
291
  const key = getTestToArKey(test)
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
5
+
6
+ const multerReadCh = channel('datadog:multer:read:finish')
7
+
8
+ function publishRequestBodyAndNext (req, res, next) {
9
+ return shimmer.wrapFunction(next, next => function () {
10
+ if (multerReadCh.hasSubscribers && req) {
11
+ const abortController = new AbortController()
12
+ const body = req.body
13
+
14
+ multerReadCh.publish({ req, res, body, abortController })
15
+
16
+ if (abortController.signal.aborted) return
17
+ }
18
+
19
+ return next.apply(this, arguments)
20
+ })
21
+ }
22
+
23
+ addHook({
24
+ name: 'multer',
25
+ file: 'lib/make-middleware.js',
26
+ versions: ['^1.4.4-lts.1']
27
+ }, makeMiddleware => {
28
+ return shimmer.wrapFunction(makeMiddleware, makeMiddleware => function () {
29
+ const middleware = makeMiddleware.apply(this, arguments)
30
+
31
+ return shimmer.wrapFunction(middleware, middleware => function wrapMulterMiddleware (req, res, next) {
32
+ const nextResource = new AsyncResource('bound-anonymous-fn')
33
+ arguments[2] = nextResource.bind(publishRequestBodyAndNext(req, res, next))
34
+ return middleware.apply(this, arguments)
35
+ })
36
+ })
37
+ })
@@ -6,11 +6,14 @@ const {
6
6
  AsyncResource
7
7
  } = require('./helpers/instrument')
8
8
  const shimmer = require('../../datadog-shimmer')
9
+ const semver = require('semver')
9
10
 
10
- addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connection => {
11
+ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, (Connection, version) => {
11
12
  const startCh = channel('apm:mysql2:query:start')
12
13
  const finishCh = channel('apm:mysql2:query:finish')
13
14
  const errorCh = channel('apm:mysql2:query:error')
15
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
16
+ const shouldEmitEndAfterQueryAbort = semver.intersects(version, '>=1.3.3')
14
17
 
15
18
  shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) {
16
19
  if (!startCh.hasSubscribers) return addCommand.apply(this, arguments)
@@ -28,6 +31,76 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connec
28
31
  return asyncResource.bind(addCommand, this).apply(this, arguments)
29
32
  })
30
33
 
34
+ shimmer.wrap(Connection.prototype, 'query', query => function (sql, values, cb) {
35
+ if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
36
+
37
+ if (typeof sql === 'object') sql = sql?.sql
38
+
39
+ if (!sql) return query.apply(this, arguments)
40
+
41
+ const abortController = new AbortController()
42
+ startOuterQueryCh.publish({ sql, abortController })
43
+
44
+ if (abortController.signal.aborted) {
45
+ const addCommand = this.addCommand
46
+ this.addCommand = function (cmd) { return cmd }
47
+
48
+ let queryCommand
49
+ try {
50
+ queryCommand = query.apply(this, arguments)
51
+ } finally {
52
+ this.addCommand = addCommand
53
+ }
54
+
55
+ cb = queryCommand.onResult
56
+
57
+ process.nextTick(() => {
58
+ if (cb) {
59
+ cb(abortController.signal.reason)
60
+ } else {
61
+ queryCommand.emit('error', abortController.signal.reason)
62
+ }
63
+
64
+ if (shouldEmitEndAfterQueryAbort) {
65
+ queryCommand.emit('end')
66
+ }
67
+ })
68
+
69
+ return queryCommand
70
+ }
71
+
72
+ return query.apply(this, arguments)
73
+ })
74
+
75
+ shimmer.wrap(Connection.prototype, 'execute', execute => function (sql, values, cb) {
76
+ if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
77
+
78
+ if (typeof sql === 'object') sql = sql?.sql
79
+
80
+ if (!sql) return execute.apply(this, arguments)
81
+
82
+ const abortController = new AbortController()
83
+ startOuterQueryCh.publish({ sql, abortController })
84
+
85
+ if (abortController.signal.aborted) {
86
+ const addCommand = this.addCommand
87
+ this.addCommand = function (cmd) { return cmd }
88
+
89
+ let result
90
+ try {
91
+ result = execute.apply(this, arguments)
92
+ } finally {
93
+ this.addCommand = addCommand
94
+ }
95
+
96
+ result?.onResult(abortController.signal.reason)
97
+
98
+ return result
99
+ }
100
+
101
+ return execute.apply(this, arguments)
102
+ })
103
+
31
104
  return Connection
32
105
 
33
106
  function bindExecute (cmd, execute, asyncResource) {
@@ -79,3 +152,149 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connec
79
152
  }, cmd))
80
153
  }
81
154
  })
155
+
156
+ addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['>=1'] }, (Pool, version) => {
157
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
158
+ const shouldEmitEndAfterQueryAbort = semver.intersects(version, '>=1.3.3')
159
+
160
+ shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
161
+ if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
162
+
163
+ if (typeof sql === 'object') sql = sql?.sql
164
+
165
+ if (!sql) return query.apply(this, arguments)
166
+
167
+ const abortController = new AbortController()
168
+ startOuterQueryCh.publish({ sql, abortController })
169
+
170
+ if (abortController.signal.aborted) {
171
+ const getConnection = this.getConnection
172
+ this.getConnection = function () {}
173
+
174
+ let queryCommand
175
+ try {
176
+ queryCommand = query.apply(this, arguments)
177
+ } finally {
178
+ this.getConnection = getConnection
179
+ }
180
+
181
+ process.nextTick(() => {
182
+ if (queryCommand.onResult) {
183
+ queryCommand.onResult(abortController.signal.reason)
184
+ } else {
185
+ queryCommand.emit('error', abortController.signal.reason)
186
+ }
187
+
188
+ if (shouldEmitEndAfterQueryAbort) {
189
+ queryCommand.emit('end')
190
+ }
191
+ })
192
+
193
+ return queryCommand
194
+ }
195
+
196
+ return query.apply(this, arguments)
197
+ })
198
+
199
+ shimmer.wrap(Pool.prototype, 'execute', execute => function (sql, values, cb) {
200
+ if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
201
+
202
+ if (typeof sql === 'object') sql = sql?.sql
203
+
204
+ if (!sql) return execute.apply(this, arguments)
205
+
206
+ const abortController = new AbortController()
207
+ startOuterQueryCh.publish({ sql, abortController })
208
+
209
+ if (abortController.signal.aborted) {
210
+ if (typeof values === 'function') {
211
+ cb = values
212
+ }
213
+
214
+ process.nextTick(() => {
215
+ cb(abortController.signal.reason)
216
+ })
217
+ return
218
+ }
219
+
220
+ return execute.apply(this, arguments)
221
+ })
222
+
223
+ return Pool
224
+ })
225
+
226
+ // PoolNamespace.prototype.query does not exist in mysql2<2.3.0
227
+ addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=2.3.0'] }, PoolCluster => {
228
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
229
+ const wrappedPoolNamespaces = new WeakSet()
230
+
231
+ shimmer.wrap(PoolCluster.prototype, 'of', of => function () {
232
+ const poolNamespace = of.apply(this, arguments)
233
+
234
+ if (startOuterQueryCh.hasSubscribers && !wrappedPoolNamespaces.has(poolNamespace)) {
235
+ shimmer.wrap(poolNamespace, 'query', query => function (sql, values, cb) {
236
+ if (typeof sql === 'object') sql = sql?.sql
237
+
238
+ if (!sql) return query.apply(this, arguments)
239
+
240
+ const abortController = new AbortController()
241
+ startOuterQueryCh.publish({ sql, abortController })
242
+
243
+ if (abortController.signal.aborted) {
244
+ const getConnection = this.getConnection
245
+ this.getConnection = function () {}
246
+
247
+ let queryCommand
248
+ try {
249
+ queryCommand = query.apply(this, arguments)
250
+ } finally {
251
+ this.getConnection = getConnection
252
+ }
253
+
254
+ process.nextTick(() => {
255
+ if (queryCommand.onResult) {
256
+ queryCommand.onResult(abortController.signal.reason)
257
+ } else {
258
+ queryCommand.emit('error', abortController.signal.reason)
259
+ }
260
+
261
+ queryCommand.emit('end')
262
+ })
263
+
264
+ return queryCommand
265
+ }
266
+
267
+ return query.apply(this, arguments)
268
+ })
269
+
270
+ shimmer.wrap(poolNamespace, 'execute', execute => function (sql, values, cb) {
271
+ if (typeof sql === 'object') sql = sql?.sql
272
+
273
+ if (!sql) return execute.apply(this, arguments)
274
+
275
+ const abortController = new AbortController()
276
+ startOuterQueryCh.publish({ sql, abortController })
277
+
278
+ if (abortController.signal.aborted) {
279
+ if (typeof values === 'function') {
280
+ cb = values
281
+ }
282
+
283
+ process.nextTick(() => {
284
+ cb(abortController.signal.reason)
285
+ })
286
+
287
+ return
288
+ }
289
+
290
+ return execute.apply(this, arguments)
291
+ })
292
+
293
+ wrappedPoolNamespaces.add(poolNamespace)
294
+ }
295
+
296
+ return poolNamespace
297
+ })
298
+
299
+ return PoolCluster
300
+ })
@@ -3,8 +3,8 @@
3
3
  const { addHook } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
5
 
6
- const tracingChannel = require('dc-polyfill').tracingChannel
7
- const ch = tracingChannel('apm:openai:request')
6
+ const dc = require('dc-polyfill')
7
+ const ch = dc.tracingChannel('apm:openai:request')
8
8
 
9
9
  const V4_PACKAGE_SHIMS = [
10
10
  {
@@ -0,0 +1,127 @@
1
+ const shimmer = require('../../datadog-shimmer')
2
+ const { addHook } = require('./helpers/instrument')
3
+
4
+ const dc = require('dc-polyfill')
5
+ const serializeChannel = dc.channel('apm:protobufjs:serialize-start')
6
+ const deserializeChannel = dc.channel('apm:protobufjs:deserialize-end')
7
+
8
+ function wrapSerialization (messageClass) {
9
+ if (messageClass?.encode) {
10
+ shimmer.wrap(messageClass, 'encode', original => function () {
11
+ if (!serializeChannel.hasSubscribers) {
12
+ return original.apply(this, arguments)
13
+ }
14
+ serializeChannel.publish({ messageClass: this })
15
+ return original.apply(this, arguments)
16
+ })
17
+ }
18
+ }
19
+
20
+ function wrapDeserialization (messageClass) {
21
+ if (messageClass?.decode) {
22
+ shimmer.wrap(messageClass, 'decode', original => function () {
23
+ if (!deserializeChannel.hasSubscribers) {
24
+ return original.apply(this, arguments)
25
+ }
26
+ const result = original.apply(this, arguments)
27
+ deserializeChannel.publish({ messageClass: result })
28
+ return result
29
+ })
30
+ }
31
+ }
32
+
33
+ function wrapSetup (messageClass) {
34
+ if (messageClass?.setup) {
35
+ shimmer.wrap(messageClass, 'setup', original => function () {
36
+ const result = original.apply(this, arguments)
37
+
38
+ wrapSerialization(messageClass)
39
+ wrapDeserialization(messageClass)
40
+
41
+ return result
42
+ })
43
+ }
44
+ }
45
+
46
+ function wrapProtobufClasses (root) {
47
+ if (!root) {
48
+ return
49
+ }
50
+
51
+ if (root.decode) {
52
+ wrapSetup(root)
53
+ }
54
+
55
+ if (root.nestedArray) {
56
+ for (const subRoot of root.nestedArray) {
57
+ wrapProtobufClasses(subRoot)
58
+ }
59
+ }
60
+ }
61
+
62
+ function wrapReflection (protobuf) {
63
+ const reflectionMethods = [
64
+ {
65
+ target: protobuf.Root,
66
+ name: 'fromJSON'
67
+ },
68
+ {
69
+ target: protobuf.Type.prototype,
70
+ name: 'fromObject'
71
+ }
72
+ ]
73
+
74
+ reflectionMethods.forEach(method => {
75
+ shimmer.wrap(method.target, method.name, original => function () {
76
+ const result = original.apply(this, arguments)
77
+ if (result.nested) {
78
+ for (const type in result.nested) {
79
+ wrapSetup(result.nested[type])
80
+ }
81
+ }
82
+ if (result.$type) {
83
+ wrapSetup(result.$type)
84
+ }
85
+ return result
86
+ })
87
+ })
88
+ }
89
+
90
+ function isPromise (obj) {
91
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
92
+ }
93
+
94
+ addHook({
95
+ name: 'protobufjs',
96
+ versions: ['>=6.8.0']
97
+ }, protobuf => {
98
+ shimmer.wrap(protobuf.Root.prototype, 'load', original => function () {
99
+ const result = original.apply(this, arguments)
100
+ if (isPromise(result)) {
101
+ return result.then(root => {
102
+ wrapProtobufClasses(root)
103
+ return root
104
+ })
105
+ } else {
106
+ // If result is not a promise, directly wrap the protobuf classes
107
+ wrapProtobufClasses(result)
108
+ return result
109
+ }
110
+ })
111
+
112
+ shimmer.wrap(protobuf.Root.prototype, 'loadSync', original => function () {
113
+ const root = original.apply(this, arguments)
114
+ wrapProtobufClasses(root)
115
+ return root
116
+ })
117
+
118
+ shimmer.wrap(protobuf, 'Type', Original => function () {
119
+ const typeInstance = new Original(...arguments)
120
+ wrapSetup(typeInstance)
121
+ return typeInstance
122
+ })
123
+
124
+ wrapReflection(protobuf)
125
+
126
+ return protobuf
127
+ })