dd-trace 5.62.0 → 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 (33) 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/couchbase.js +102 -65
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  6. package/packages/datadog-instrumentations/src/helpers/register.js +2 -22
  7. package/packages/datadog-instrumentations/src/hono.js +11 -8
  8. package/packages/datadog-instrumentations/src/knex.js +15 -17
  9. package/packages/datadog-instrumentations/src/mongodb-core.js +4 -6
  10. package/packages/datadog-instrumentations/src/next.js +4 -8
  11. package/packages/datadog-instrumentations/src/pg.js +38 -48
  12. package/packages/datadog-plugin-aerospike/src/index.js +6 -2
  13. package/packages/datadog-plugin-ai/src/index.js +17 -0
  14. package/packages/datadog-plugin-ai/src/tracing.js +33 -0
  15. package/packages/datadog-plugin-ai/src/utils.js +28 -0
  16. package/packages/datadog-plugin-couchbase/src/index.js +37 -17
  17. package/packages/datadog-plugin-pg/src/index.js +5 -2
  18. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +14 -7
  19. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  20. package/packages/dd-trace/src/appsec/recommended.json +271 -2
  21. package/packages/dd-trace/src/guardrails/telemetry.js +18 -2
  22. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +351 -0
  23. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +179 -0
  24. package/packages/dd-trace/src/llmobs/writers/base.js +3 -2
  25. package/packages/dd-trace/src/opentracing/span_context.js +4 -0
  26. package/packages/dd-trace/src/plugin_manager.js +8 -4
  27. package/packages/dd-trace/src/plugins/index.js +1 -0
  28. package/packages/dd-trace/src/plugins/util/ip_extractor.js +44 -3
  29. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +24 -23
  30. package/packages/dd-trace/src/profiling/profilers/events.js +3 -2
  31. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -2
  32. package/packages/dd-trace/src/supported-configurations.json +2 -0
  33. package/packages/dd-trace/src/tracer_metadata.js +1 -1
package/README.md CHANGED
@@ -22,11 +22,6 @@ Most of the documentation for `dd-trace` is available on these webpages:
22
22
 
23
23
  ## Version Release Lines and Maintenance
24
24
 
25
- > **Node.js v24 Notice**: We're currently adding compatibility for Node.js v24. To use the tracer with your application either continue to use Node.js v22 (LTS), or do both of the following as a workaround:
26
- > * Install v5.52.0 (or newer) of the tracer
27
- > * Set `--no-async-context-frame` either using a CLI argument or via `NODE_OPTIONS`
28
- > Once support for Node.js v24 is complete this flag will no longer be needed.
29
-
30
25
  | Release Line | Latest Version | Node.js | [SSI](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/single-step-apm/?tab=linuxhostorvm) | [K8s Injection](https://docs.datadoghq.com/tracing/trace_collection/library_injection_local/?tab=kubernetes) |Status |Initial Release | End of Life |
31
26
  | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
32
27
  | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | NO | NO | **EOL** | 2021-07-13 | 2022-02-25 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.62.0",
3
+ "version": "5.63.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -114,7 +114,7 @@
114
114
  ],
115
115
  "dependencies": {
116
116
  "@datadog/libdatadog": "0.7.0",
117
- "@datadog/native-appsec": "10.0.1",
117
+ "@datadog/native-appsec": "10.1.0",
118
118
  "@datadog/native-iast-taint-tracking": "4.0.0",
119
119
  "@datadog/native-metrics": "3.1.1",
120
120
  "@datadog/pprof": "5.9.0",
@@ -0,0 +1,140 @@
1
+ 'use strict'
2
+
3
+ const { addHook } = require('./helpers/instrument')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const { channel, tracingChannel } = require('dc-polyfill')
7
+ const toolCreationChannel = channel('dd-trace:vercel-ai:tool')
8
+
9
+ const TRACED_FUNCTIONS = {
10
+ generateText: wrapWithTracer,
11
+ streamText: wrapWithTracer,
12
+ generateObject: wrapWithTracer,
13
+ streamObject: wrapWithTracer,
14
+ embed: wrapWithTracer,
15
+ embedMany: wrapWithTracer,
16
+ tool: wrapTool
17
+ }
18
+
19
+ const vercelAiTracingChannel = tracingChannel('dd-trace:vercel-ai')
20
+ const vercelAiSpanSetAttributesChannel = channel('dd-trace:vercel-ai:span:setAttributes')
21
+
22
+ const noopTracer = {
23
+ startActiveSpan () {
24
+ const fn = arguments[arguments.length - 1]
25
+
26
+ const span = {
27
+ spanContext () { return { traceId: '', spanId: '', traceFlags: 0 } },
28
+ setAttribute () { return this },
29
+ setAttributes () { return this },
30
+ addEvent () { return this },
31
+ addLink () { return this },
32
+ addLinks () { return this },
33
+ setStatus () { return this },
34
+ updateName () { return this },
35
+ end () { return this },
36
+ isRecording () { return false },
37
+ recordException () { return this }
38
+ }
39
+
40
+ return fn(span)
41
+ }
42
+ }
43
+
44
+ function wrapTracer (tracer) {
45
+ if (Object.hasOwn(tracer, Symbol.for('_dd.wrapped'))) return
46
+
47
+ shimmer.wrap(tracer, 'startActiveSpan', function (startActiveSpan) {
48
+ return function () {
49
+ const name = arguments[0]
50
+ const options = arguments.length > 2 ? (arguments[1] ?? {}) : {} // startActiveSpan(name, fn)
51
+ const cb = arguments[arguments.length - 1]
52
+
53
+ const ctx = {
54
+ name,
55
+ attributes: options.attributes ?? {}
56
+ }
57
+
58
+ arguments[arguments.length - 1] = shimmer.wrapFunction(cb, function (originalCb) {
59
+ return function (span) {
60
+ shimmer.wrap(span, 'end', function (spanEnd) {
61
+ return function () {
62
+ vercelAiTracingChannel.asyncEnd.publish(ctx)
63
+ return spanEnd.apply(this, arguments)
64
+ }
65
+ })
66
+
67
+ shimmer.wrap(span, 'setAttributes', function (setAttributes) {
68
+ return function (attributes) {
69
+ vercelAiSpanSetAttributesChannel.publish({ ctx, attributes })
70
+ return setAttributes.apply(this, arguments)
71
+ }
72
+ })
73
+
74
+ shimmer.wrap(span, 'recordException', function (recordException) {
75
+ return function (exception) {
76
+ ctx.error = exception
77
+ vercelAiTracingChannel.error.publish(ctx)
78
+ return recordException.apply(this, arguments)
79
+ }
80
+ })
81
+
82
+ return originalCb.apply(this, arguments)
83
+ }
84
+ })
85
+
86
+ return vercelAiTracingChannel.start.runStores(ctx, () => {
87
+ const result = startActiveSpan.apply(this, arguments)
88
+ vercelAiTracingChannel.end.publish(ctx)
89
+ return result
90
+ })
91
+ }
92
+ })
93
+
94
+ Object.defineProperty(tracer, Symbol.for('_dd.wrapped'), { value: true })
95
+ }
96
+
97
+ function wrapWithTracer (fn) {
98
+ return function () {
99
+ const options = arguments[0]
100
+
101
+ options.experimental_telemetry ??= { isEnabled: true, tracer: noopTracer }
102
+ wrapTracer(options.experimental_telemetry.tracer)
103
+
104
+ return fn.apply(this, arguments)
105
+ }
106
+ }
107
+
108
+ function wrapTool (tool) {
109
+ return function () {
110
+ const args = arguments[0]
111
+ toolCreationChannel.publish(args)
112
+
113
+ return tool.apply(this, arguments)
114
+ }
115
+ }
116
+
117
+ // CJS exports
118
+ addHook({
119
+ name: 'ai',
120
+ versions: ['>=4.0.0'],
121
+ }, exports => {
122
+ for (const [fnName, patchingFn] of Object.entries(TRACED_FUNCTIONS)) {
123
+ exports = shimmer.wrap(exports, fnName, patchingFn, { replaceGetter: true })
124
+ }
125
+
126
+ return exports
127
+ })
128
+
129
+ // ESM exports
130
+ addHook({
131
+ name: 'ai',
132
+ versions: ['>=4.0.0'],
133
+ file: 'dist/index.mjs'
134
+ }, exports => {
135
+ for (const [fnName, patchingFn] of Object.entries(TRACED_FUNCTIONS)) {
136
+ exports = shimmer.wrap(exports, fnName, patchingFn, { replaceGetter: true })
137
+ }
138
+
139
+ return exports
140
+ })
@@ -3,8 +3,7 @@
3
3
  const { errorMonitor } = require('events')
4
4
  const {
5
5
  channel,
6
- addHook,
7
- AsyncResource
6
+ addHook
8
7
  } = require('./helpers/instrument')
9
8
  const shimmer = require('../../datadog-shimmer')
10
9
 
@@ -24,31 +23,50 @@ function wrapAllNames (names, action) {
24
23
  names.forEach(name => action(name))
25
24
  }
26
25
 
27
- // semver >=2 <3
28
- function wrapMaybeInvoke (_maybeInvoke) {
29
- const wrapped = function (fn, args) {
30
- if (!Array.isArray(args)) return _maybeInvoke.apply(this, arguments)
31
-
32
- const callbackIndex = args.length - 1
33
- const callback = args[callbackIndex]
26
+ function wrapCallback (callback, ctx, channelPrefix) {
27
+ const callbackStartCh = channel(`${channelPrefix}:callback:start`)
28
+ const callbackFinishCh = channel(`${channelPrefix}:callback:finish`)
34
29
 
35
- if (typeof callback === 'function') {
36
- args[callbackIndex] = AsyncResource.bind(callback)
30
+ const wrapped = callbackStartCh.runStores(ctx, () => {
31
+ return function (...args) {
32
+ return callbackFinishCh.runStores(ctx, () => {
33
+ return callback.apply(this, args)
34
+ })
37
35
  }
38
-
39
- return _maybeInvoke.apply(this, arguments)
40
- }
36
+ })
37
+ Object.defineProperty(wrapped, '_dd_wrapped', { value: true })
41
38
  return wrapped
42
39
  }
43
40
 
44
41
  function wrapQuery (query) {
45
- const wrapped = function (q, params, callback) {
46
- if (typeof arguments[arguments.length - 1] === 'function') {
47
- arguments[arguments.length - 1] = AsyncResource.bind(arguments[arguments.length - 1])
42
+ return function (q, params, callback) {
43
+ const cb = arguments[arguments.length - 1]
44
+ if (typeof cb === 'function') {
45
+ const ctx = {}
46
+ arguments[arguments.length - 1] = wrapCallback(cb, ctx, 'apm:couchbase:query')
48
47
  }
49
48
 
50
49
  return query.apply(this, arguments)
51
50
  }
51
+ }
52
+
53
+ function wrapCallbackFinish (callback, thisArg, _args, errorCh, finishCh, ctx, channelPrefix) {
54
+ const callbackStartCh = channel(`${channelPrefix}:callback:start`)
55
+ const callbackFinishCh = channel(`${channelPrefix}:callback:finish`)
56
+
57
+ const wrapped = callbackStartCh.runStores(ctx, () => {
58
+ return function finish (error, result) {
59
+ return callbackFinishCh.runStores(ctx, () => {
60
+ if (error) {
61
+ ctx.error = error
62
+ errorCh.publish(ctx)
63
+ }
64
+ finishCh.publish(ctx)
65
+ return callback.apply(thisArg, [error, result])
66
+ })
67
+ }
68
+ })
69
+ Object.defineProperty(wrapped, '_dd_wrapped', { value: true })
52
70
  return wrapped
53
71
  }
54
72
 
@@ -62,31 +80,24 @@ function wrap (prefix, fn) {
62
80
  return fn.apply(this, arguments)
63
81
  }
64
82
 
65
- const callbackIndex = findCallbackIndex(arguments)
83
+ const callbackIndex = findCallbackIndex(arguments, 1)
66
84
 
67
85
  if (callbackIndex < 0) return fn.apply(this, arguments)
68
86
 
69
- const callbackResource = new AsyncResource('bound-anonymous-fn')
70
- const asyncResource = new AsyncResource('bound-anonymous-fn')
71
-
72
- return asyncResource.runInAsyncScope(() => {
73
- const cb = callbackResource.bind(arguments[callbackIndex])
87
+ const ctx = { bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts }
88
+ return startCh.runStores(ctx, () => {
89
+ const cb = arguments[callbackIndex]
74
90
 
75
- startCh.publish({ bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts })
76
-
77
- arguments[callbackIndex] = shimmer.wrapFunction(cb, cb => asyncResource.bind(function (error, result) {
78
- if (error) {
79
- errorCh.publish(error)
80
- }
81
- finishCh.publish(result)
82
- return cb.apply(this, arguments)
83
- }))
91
+ arguments[callbackIndex] = shimmer.wrapFunction(cb, (cb) => {
92
+ return wrapCallbackFinish(cb, this, arguments, errorCh, finishCh, ctx, prefix)
93
+ })
84
94
 
85
95
  try {
86
96
  return fn.apply(this, arguments)
87
97
  } catch (error) {
98
+ ctx.error = error
88
99
  error.stack // trigger getting the stack at the original throwing point
89
- errorCh.publish(error)
100
+ errorCh.publish(ctx)
90
101
 
91
102
  throw error
92
103
  }
@@ -95,6 +106,26 @@ function wrap (prefix, fn) {
95
106
  return wrapped
96
107
  }
97
108
 
109
+ // semver >=2 <3
110
+ function wrapMaybeInvoke (_maybeInvoke, channelPrefix) {
111
+ return function (fn, args) {
112
+ if (!Array.isArray(args)) return _maybeInvoke.apply(this, arguments)
113
+
114
+ const callbackIndex = findCallbackIndex(args, 0)
115
+
116
+ if (callbackIndex === -1) return _maybeInvoke.apply(this, arguments)
117
+
118
+ const callback = args[callbackIndex]
119
+
120
+ if (typeof callback === 'function' && !callback._dd_wrapped) {
121
+ const ctx = {}
122
+ args[callbackIndex] = wrapCallback(callback, ctx, channelPrefix)
123
+ }
124
+
125
+ return _maybeInvoke.apply(this, arguments)
126
+ }
127
+ }
128
+
98
129
  // semver >=3
99
130
 
100
131
  function wrapCBandPromise (fn, name, startData, thisArg, args) {
@@ -104,36 +135,36 @@ function wrapCBandPromise (fn, name, startData, thisArg, args) {
104
135
 
105
136
  if (!startCh.hasSubscribers) return fn.apply(thisArg, args)
106
137
 
107
- const asyncResource = new AsyncResource('bound-anonymous-fn')
108
- const callbackResource = new AsyncResource('bound-anonymous-fn')
109
-
110
- return asyncResource.runInAsyncScope(() => {
111
- startCh.publish(startData)
112
-
138
+ const ctx = startData
139
+ return startCh.runStores(ctx, () => {
113
140
  try {
114
141
  const cbIndex = findCallbackIndex(args, 1)
115
142
  if (cbIndex >= 0) {
116
143
  // v3 offers callback or promises event handling
117
144
  // NOTE: this does not work with v3.2.0-3.2.1 cluster.query, as there is a bug in the couchbase source code
118
- const cb = callbackResource.bind(args[cbIndex])
119
- args[cbIndex] = shimmer.wrapFunction(cb, cb => asyncResource.bind(function (error, result) {
120
- if (error) {
121
- errorCh.publish(error)
122
- }
123
- finishCh.publish({ result })
124
- return cb.apply(thisArg, arguments)
125
- }))
145
+ args[cbIndex] = shimmer.wrapFunction(args[cbIndex], (cb) => {
146
+ return wrapCallbackFinish(cb, thisArg, args, errorCh, finishCh, ctx, `apm:couchbase:${name}`)
147
+ })
126
148
  }
127
149
  const res = fn.apply(thisArg, args)
128
150
 
129
151
  // semver >=3 will always return promise by default
130
152
  res.then(
131
- asyncResource.bind((result) => finishCh.publish({ result })),
132
- asyncResource.bind((err) => errorCh.publish(err)))
153
+ (result) => {
154
+ ctx.result = result
155
+ finishCh.publish(ctx)
156
+ },
157
+ (err) => {
158
+ ctx.error = err
159
+ errorCh.publish(ctx)
160
+ finishCh.publish(ctx)
161
+ }
162
+ )
133
163
  return res
134
164
  } catch (e) {
135
165
  e.stack
136
- errorCh.publish(e)
166
+ ctx.error = e
167
+ errorCh.publish(ctx)
137
168
  throw e
138
169
  }
139
170
  })
@@ -160,11 +191,14 @@ function wrapV3Query (query) {
160
191
 
161
192
  // semver >=2 <3
162
193
  addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Bucket => {
194
+ shimmer.wrap(Bucket.prototype, '_maybeInvoke', maybeInvoke => {
195
+ return wrapMaybeInvoke(maybeInvoke, 'apm:couchbase:bucket:maybeInvoke')
196
+ })
197
+
163
198
  const startCh = channel('apm:couchbase:query:start')
164
199
  const finishCh = channel('apm:couchbase:query:finish')
165
200
  const errorCh = channel('apm:couchbase:query:error')
166
201
 
167
- shimmer.wrap(Bucket.prototype, '_maybeInvoke', maybeInvoke => wrapMaybeInvoke(maybeInvoke))
168
202
  shimmer.wrap(Bucket.prototype, 'query', query => wrapQuery(query))
169
203
 
170
204
  shimmer.wrap(Bucket.prototype, '_n1qlReq', _n1qlReq => function (host, q, adhoc, emitter) {
@@ -176,24 +210,25 @@ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Buc
176
210
 
177
211
  const n1qlQuery = getQueryResource(q)
178
212
 
179
- const asyncResource = new AsyncResource('bound-anonymous-fn')
180
- return asyncResource.runInAsyncScope(() => {
181
- startCh.publish({ resource: n1qlQuery, bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts })
182
-
183
- emitter.once('rows', asyncResource.bind(() => {
184
- finishCh.publish()
185
- }))
213
+ const ctx = { resource: n1qlQuery, bucket: { name: this.name || this._name }, seedNodes: this._dd_hosts }
214
+ return startCh.runStores(ctx, () => {
215
+ emitter.once('rows', () => {
216
+ finishCh.publish(ctx)
217
+ })
186
218
 
187
- emitter.once(errorMonitor, asyncResource.bind((error) => {
188
- errorCh.publish(error)
189
- finishCh.publish()
190
- }))
219
+ emitter.once(errorMonitor, (error) => {
220
+ if (!error) return
221
+ ctx.error = error
222
+ errorCh.publish(ctx)
223
+ finishCh.publish(ctx)
224
+ })
191
225
 
192
226
  try {
193
227
  return _n1qlReq.apply(this, arguments)
194
228
  } catch (err) {
195
229
  err.stack // trigger getting the stack at the original throwing point
196
- errorCh.publish(err)
230
+ ctx.error = err
231
+ errorCh.publish(ctx)
197
232
 
198
233
  throw err
199
234
  }
@@ -208,9 +243,11 @@ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Buc
208
243
  })
209
244
 
210
245
  addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^2.6.12'] }, Cluster => {
211
- shimmer.wrap(Cluster.prototype, '_maybeInvoke', maybeInvoke => wrapMaybeInvoke(maybeInvoke))
212
- shimmer.wrap(Cluster.prototype, 'query', query => wrapQuery(query))
246
+ shimmer.wrap(Cluster.prototype, '_maybeInvoke', maybeInvoke => {
247
+ return wrapMaybeInvoke(maybeInvoke, 'apm:couchbase:cluster:maybeInvoke')
248
+ })
213
249
 
250
+ shimmer.wrap(Cluster.prototype, 'query', query => wrapQuery(query))
214
251
  shimmer.wrap(Cluster.prototype, 'openBucket', openBucket => {
215
252
  return function () {
216
253
  const bucket = openBucket.apply(this, arguments)
@@ -30,6 +30,7 @@ module.exports = {
30
30
  '@smithy/smithy-client': () => require('../aws-sdk'),
31
31
  '@vitest/runner': { esmFirst: true, fn: () => require('../vitest') },
32
32
  aerospike: () => require('../aerospike'),
33
+ ai: () => require('../ai'),
33
34
  amqp10: () => require('../amqp10'),
34
35
  amqplib: () => require('../amqplib'),
35
36
  avsc: () => require('../avsc'),
@@ -9,7 +9,6 @@ const log = require('../../../dd-trace/src/log')
9
9
  const checkRequireCache = require('./check-require-cache')
10
10
  const telemetry = require('../../../dd-trace/src/guardrails/telemetry')
11
11
  const { isInServerlessEnvironment } = require('../../../dd-trace/src/serverless')
12
- const { isFalse, isTrue, normalizePluginEnvName } = require('../../../dd-trace/src/util')
13
12
  const { getEnvironmentVariables } = require('../../../dd-trace/src/config-helper')
14
13
 
15
14
  const envs = getEnvironmentVariables()
@@ -25,22 +24,8 @@ const names = Object.keys(hooks)
25
24
  const pathSepExpr = new RegExp(`\\${path.sep}`, 'g')
26
25
 
27
26
  const disabledInstrumentations = new Set(
28
- DD_TRACE_DISABLED_INSTRUMENTATIONS?.split(',').map(name => normalizePluginEnvName(name, true)) ?? []
27
+ DD_TRACE_DISABLED_INSTRUMENTATIONS?.split(',')
29
28
  )
30
- const reenabledInstrumentations = new Set()
31
-
32
- // Check for DD_TRACE_<INTEGRATION>_ENABLED environment variables
33
- for (const [key, value] of Object.entries(envs)) {
34
- const match = key.match(/^DD_TRACE_(.+)_ENABLED$/)
35
- if (match && value) {
36
- const integration = normalizePluginEnvName(match[1], true)
37
- if (isFalse(value)) {
38
- disabledInstrumentations.add(integration)
39
- } else if (isTrue(value)) {
40
- reenabledInstrumentations.add(integration)
41
- }
42
- }
43
- }
44
29
 
45
30
  const loadChannel = channel('dd-trace:instrumentation:load')
46
31
 
@@ -65,8 +50,7 @@ const allInstrumentations = {}
65
50
 
66
51
  // TODO: make this more efficient
67
52
  for (const packageName of names) {
68
- const normalizedPackageName = normalizePluginEnvName(packageName, true)
69
- if (disabledInstrumentations.has(normalizedPackageName)) continue
53
+ if (disabledInstrumentations.has(packageName)) continue
70
54
 
71
55
  const hookOptions = {}
72
56
 
@@ -75,10 +59,6 @@ for (const packageName of names) {
75
59
  if (hook !== null && typeof hook === 'object') {
76
60
  if (hook.serverless === false && isInServerlessEnvironment()) continue
77
61
 
78
- // some integrations are disabled by default, but can be enabled by setting
79
- // the DD_TRACE_<INTEGRATION>_ENABLED environment variable to true
80
- if (hook.disabled && !reenabledInstrumentations.has(normalizedPackageName)) continue
81
-
82
62
  hookOptions.internals = hook.esmFirst
83
63
  hook = hook.fn
84
64
  }
@@ -17,8 +17,14 @@ function wrapFetch (fetch) {
17
17
  }
18
18
  }
19
19
 
20
+ function onErrorFn (error, _context_) {
21
+ throw error
22
+ }
23
+
20
24
  function wrapCompose (compose) {
21
- return function (middleware, onError, onNotFound) {
25
+ return function (middlewares, onError, onNotFound) {
26
+ onError ??= onErrorFn
27
+
22
28
  const instrumentedOnError = (...args) => {
23
29
  const [error, context] = args
24
30
  const req = context.env.incoming
@@ -26,23 +32,20 @@ function wrapCompose (compose) {
26
32
  return onError(...args)
27
33
  }
28
34
 
29
- const instrumentedMiddlewares = middleware.map(h => {
35
+ const instrumentedMiddlewares = middlewares.map(h => {
30
36
  const [[fn, meta], params] = h
31
37
 
32
- // TODO: handle middleware instrumentation
33
38
  const instrumentedFn = (...args) => {
34
39
  const context = args[0]
35
- const req = context.env.incoming
36
- const route = meta.path
37
40
  routeChannel.publish({
38
- req,
39
- route
41
+ req: context.env.incoming,
42
+ route: meta?.path
40
43
  })
41
44
  return fn(...args)
42
45
  }
43
46
  return [[instrumentedFn, meta], params]
44
47
  })
45
- return compose.apply(this, [instrumentedMiddlewares, instrumentedOnError, onNotFound])
48
+ return compose.call(this, instrumentedMiddlewares, instrumentedOnError, onNotFound)
46
49
  }
47
50
  }
48
51
 
@@ -1,10 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const { addHook, channel, AsyncResource } = require('./helpers/instrument')
3
+ const { addHook, channel } = require('./helpers/instrument')
4
4
  const { wrapThen } = require('./helpers/promise')
5
5
  const shimmer = require('../../datadog-shimmer')
6
6
 
7
7
  const startRawQueryCh = channel('datadog:knex:raw:start')
8
+ const rawQuerySubscribes = channel('datadog:knex:raw:subscribes')
8
9
  const finishRawQueryCh = channel('datadog:knex:raw:finish')
9
10
 
10
11
  patch('lib/query/builder.js')
@@ -22,8 +23,8 @@ function patch (file) {
22
23
  })
23
24
  }
24
25
 
25
- function finish () {
26
- finishRawQueryCh.publish()
26
+ function finish (context, cb) {
27
+ finishRawQueryCh.runStores(context, cb)
27
28
  }
28
29
 
29
30
  addHook({
@@ -43,21 +44,18 @@ addHook({
43
44
  return raw.apply(this, arguments)
44
45
  }
45
46
 
46
- const asyncResource = new AsyncResource('bound-anonymous-fn')
47
-
48
- return asyncResource.runInAsyncScope(() => {
49
- startRawQueryCh.publish({ sql, dialect: this.dialect })
50
-
47
+ const context = { sql, dialect: this.dialect }
48
+ return startRawQueryCh.runStores(context, () => {
51
49
  const rawResult = raw.apply(this, arguments)
52
50
  shimmer.wrap(rawResult, 'then', originalThen => function () {
53
- return asyncResource.runInAsyncScope(() => {
54
- arguments[0] = wrapCallbackWithFinish(arguments[0], finish)
55
- if (arguments[1]) arguments[1] = wrapCallbackWithFinish(arguments[1], finish)
51
+ return rawQuerySubscribes.runStores(context, () => {
52
+ arguments[0] = wrapCallbackWithFinish(arguments[0], finish, context)
53
+ if (arguments[1]) arguments[1] = wrapCallbackWithFinish(arguments[1], finish, context)
56
54
 
57
55
  const originalThenResult = originalThen.apply(this, arguments)
58
56
 
59
57
  shimmer.wrap(originalThenResult, 'catch', originalCatch => function () {
60
- arguments[0] = wrapCallbackWithFinish(arguments[0], finish)
58
+ arguments[0] = wrapCallbackWithFinish(arguments[0], finish, context)
61
59
  return originalCatch.apply(this, arguments)
62
60
  })
63
61
 
@@ -66,8 +64,8 @@ addHook({
66
64
  })
67
65
 
68
66
  shimmer.wrap(rawResult, 'asCallback', originalAsCallback => function () {
69
- return asyncResource.runInAsyncScope(() => {
70
- arguments[0] = wrapCallbackWithFinish(arguments[0], finish)
67
+ return rawQuerySubscribes.runStores(context, () => {
68
+ arguments[0] = wrapCallbackWithFinish(arguments[0], finish, context)
71
69
  return originalAsCallback.apply(this, arguments)
72
70
  })
73
71
  })
@@ -75,14 +73,14 @@ addHook({
75
73
  return rawResult
76
74
  })
77
75
  })
76
+
78
77
  return Knex
79
78
  })
80
79
 
81
- function wrapCallbackWithFinish (callback, finish) {
80
+ function wrapCallbackWithFinish (callback, finish, context) {
82
81
  if (typeof callback !== 'function') return callback
83
82
 
84
83
  return shimmer.wrapFunction(callback, callback => function () {
85
- finish()
86
- callback.apply(this, arguments)
84
+ finish(context, () => callback.apply(this, arguments))
87
85
  })
88
86
  }
@@ -199,17 +199,15 @@ function instrumentPromise (operation, command, instance, args, server, ns, ops,
199
199
  return startCh.runStores(ctx, () => {
200
200
  const promise = command.apply(instance, args)
201
201
 
202
- return promise.then(function (res) {
202
+ promise.then(function (res) {
203
203
  ctx.result = res
204
- return finishCh.runStores(ctx, () => {
205
- return res
206
- })
204
+ finishCh.publish(ctx)
207
205
  }, function (err) {
208
206
  ctx.error = err
209
207
  errorCh.publish(ctx)
210
208
  finishCh.publish(ctx)
211
-
212
- throw err
213
209
  })
210
+
211
+ return promise
214
212
  })
215
213
  }
@@ -162,14 +162,16 @@ function instrument (req, res, handler, error) {
162
162
 
163
163
  // promise should only reject when propagateError is true:
164
164
  // https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L547
165
- return promise.then(
165
+ promise.then(
166
166
  result => finish(ctx, result),
167
167
  err => finish(ctx, null, err)
168
168
  )
169
+ return promise
169
170
  } catch (e) {
170
171
  // this will probably never happen as the handler caller is an async function:
171
172
  // https://github.com/vercel/next.js/blob/cee656238a/packages/next/server/api-utils/node.ts#L420
172
- return finish(ctx, null, e)
173
+ finish(ctx, null, e)
174
+ throw e
173
175
  }
174
176
  })
175
177
  }
@@ -198,12 +200,6 @@ function finish (ctx, result, err) {
198
200
  }
199
201
 
200
202
  finishChannel.publish(ctx)
201
-
202
- if (err) {
203
- throw err
204
- }
205
-
206
- return result
207
203
  }
208
204
 
209
205
  // also wrapped in dist/server/future/route-handlers/app-route-route-handler.js