dd-trace 5.52.0 → 5.53.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.
- package/README.md +5 -0
- package/index.d.ts +54 -6
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/amqplib.js +8 -5
- package/packages/datadog-instrumentations/src/child_process.js +2 -1
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +16 -1
- package/packages/datadog-instrumentations/src/couchbase.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +41 -46
- package/packages/datadog-instrumentations/src/express.js +2 -6
- package/packages/datadog-instrumentations/src/fs.js +6 -5
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
- package/packages/datadog-instrumentations/src/http/client.js +2 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
- package/packages/datadog-instrumentations/src/jest.js +49 -41
- package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
- package/packages/datadog-instrumentations/src/mocha/utils.js +72 -75
- package/packages/datadog-instrumentations/src/mysql2.js +3 -1
- package/packages/datadog-instrumentations/src/net.js +3 -1
- package/packages/datadog-instrumentations/src/next.js +6 -14
- package/packages/datadog-instrumentations/src/pg.js +5 -11
- package/packages/datadog-instrumentations/src/playwright.js +60 -69
- package/packages/datadog-instrumentations/src/url.js +9 -17
- package/packages/datadog-instrumentations/src/vitest.js +55 -75
- package/packages/datadog-plugin-cucumber/src/index.js +29 -18
- package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
- package/packages/datadog-plugin-jest/src/index.js +14 -8
- package/packages/datadog-plugin-kafkajs/src/producer.js +8 -5
- package/packages/datadog-plugin-mocha/src/index.js +55 -35
- package/packages/datadog-plugin-playwright/src/index.js +26 -20
- package/packages/datadog-plugin-redis/src/index.js +8 -3
- package/packages/datadog-plugin-vitest/src/index.js +53 -42
- package/packages/datadog-shimmer/src/shimmer.js +164 -33
- package/packages/dd-trace/src/appsec/graphql.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +14 -11
- package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
- package/packages/dd-trace/src/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +59 -7
- package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
- package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
- package/packages/dd-trace/src/debugger/devtools_client/state.js +21 -1
- package/packages/dd-trace/src/dogstatsd.js +2 -0
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/proxy.js +0 -4
- package/packages/dd-trace/src/serverless.js +0 -48
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
package/README.md
CHANGED
|
@@ -22,6 +22,11 @@ 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
|
+
|
|
25
30
|
| 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 |
|
|
26
31
|
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
27
32
|
| [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) |  | `>= v12` | NO | NO | **EOL** | 2021-07-13 | 2022-02-25 |
|
package/index.d.ts
CHANGED
|
@@ -189,6 +189,7 @@ interface Plugins {
|
|
|
189
189
|
"http": tracer.plugins.http;
|
|
190
190
|
"http2": tracer.plugins.http2;
|
|
191
191
|
"ioredis": tracer.plugins.ioredis;
|
|
192
|
+
"iovalkey": tracer.plugins.iovalkey;
|
|
192
193
|
"jest": tracer.plugins.jest;
|
|
193
194
|
"kafkajs": tracer.plugins.kafkajs
|
|
194
195
|
"knex": tracer.plugins.knex;
|
|
@@ -855,7 +856,7 @@ declare namespace tracer {
|
|
|
855
856
|
* @param value The amount to increment the stat by.
|
|
856
857
|
* @param tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
857
858
|
*/
|
|
858
|
-
increment(stat: string, value?: number, tags?: Record<string, string|number>): void
|
|
859
|
+
increment(stat: string, value?: number, tags?: Record<string, string|number> | string[]): void
|
|
859
860
|
|
|
860
861
|
/**
|
|
861
862
|
* Decrements a metric by the specified value, optionally specifying tags.
|
|
@@ -863,7 +864,7 @@ declare namespace tracer {
|
|
|
863
864
|
* @param value The amount to decrement the stat by.
|
|
864
865
|
* @param tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
865
866
|
*/
|
|
866
|
-
decrement(stat: string, value?: number, tags?: Record<string, string|number>): void
|
|
867
|
+
decrement(stat: string, value?: number, tags?: Record<string, string|number> | string[]): void
|
|
867
868
|
|
|
868
869
|
/**
|
|
869
870
|
* Sets a distribution value, optionally specifying tags.
|
|
@@ -871,7 +872,7 @@ declare namespace tracer {
|
|
|
871
872
|
* @param value The amount to increment the stat by.
|
|
872
873
|
* @param tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
873
874
|
*/
|
|
874
|
-
distribution(stat: string, value?: number, tags?: Record<string, string|number>): void
|
|
875
|
+
distribution(stat: string, value?: number, tags?: Record<string, string|number> | string[]): void
|
|
875
876
|
|
|
876
877
|
/**
|
|
877
878
|
* Sets a gauge value, optionally specifying tags.
|
|
@@ -879,7 +880,7 @@ declare namespace tracer {
|
|
|
879
880
|
* @param value The amount to increment the stat by.
|
|
880
881
|
* @param tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
881
882
|
*/
|
|
882
|
-
gauge(stat: string, value?: number, tags?: Record<string, string|number>): void
|
|
883
|
+
gauge(stat: string, value?: number, tags?: Record<string, string|number> | string[]): void
|
|
883
884
|
|
|
884
885
|
/**
|
|
885
886
|
* Sets a histogram value, optionally specifying tags.
|
|
@@ -887,7 +888,7 @@ declare namespace tracer {
|
|
|
887
888
|
* @param value The amount to increment the stat by.
|
|
888
889
|
* @param tags Tags to pass along, such as `{ foo: 'bar' }`. Values are combined with config.tags.
|
|
889
890
|
*/
|
|
890
|
-
histogram(stat: string, value?: number, tags?: Record<string, string|number>): void
|
|
891
|
+
histogram(stat: string, value?: number, tags?: Record<string, string|number> | string[]): void
|
|
891
892
|
|
|
892
893
|
/**
|
|
893
894
|
* Forces any unsent metrics to be sent
|
|
@@ -1361,7 +1362,7 @@ declare namespace tracer {
|
|
|
1361
1362
|
* [child_process](https://nodejs.org/api/child_process.html) module.
|
|
1362
1363
|
*/
|
|
1363
1364
|
interface child_process extends Instrumentation {}
|
|
1364
|
-
|
|
1365
|
+
|
|
1365
1366
|
/**
|
|
1366
1367
|
* This plugin automatically instruments the
|
|
1367
1368
|
* [confluentinc-kafka-javascript](https://github.com/confluentinc/confluent-kafka-js) module.
|
|
@@ -1666,6 +1667,53 @@ declare namespace tracer {
|
|
|
1666
1667
|
splitByInstance?: boolean;
|
|
1667
1668
|
}
|
|
1668
1669
|
|
|
1670
|
+
/**
|
|
1671
|
+
* This plugin automatically instruments the
|
|
1672
|
+
* [iovalkey](https://github.com/valkey-io/iovalkey) module.
|
|
1673
|
+
*/
|
|
1674
|
+
interface iovalkey extends Instrumentation {
|
|
1675
|
+
/**
|
|
1676
|
+
* List of commands that should be instrumented. Commands must be in
|
|
1677
|
+
* lowercase for example 'xread'.
|
|
1678
|
+
*
|
|
1679
|
+
* @default /^.*$/
|
|
1680
|
+
*/
|
|
1681
|
+
allowlist?: string | RegExp | ((command: string) => boolean) | (string | RegExp | ((command: string) => boolean))[];
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Deprecated in favor of `allowlist`.
|
|
1685
|
+
*
|
|
1686
|
+
* @deprecated
|
|
1687
|
+
* @hidden
|
|
1688
|
+
*/
|
|
1689
|
+
whitelist?: string | RegExp | ((command: string) => boolean) | (string | RegExp | ((command: string) => boolean))[];
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* List of commands that should not be instrumented. Takes precedence over
|
|
1693
|
+
* allowlist if a command matches an entry in both. Commands must be in
|
|
1694
|
+
* lowercase for example 'xread'.
|
|
1695
|
+
*
|
|
1696
|
+
* @default []
|
|
1697
|
+
*/
|
|
1698
|
+
blocklist?: string | RegExp | ((command: string) => boolean) | (string | RegExp | ((command: string) => boolean))[];
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* Deprecated in favor of `blocklist`.
|
|
1702
|
+
*
|
|
1703
|
+
* @deprecated
|
|
1704
|
+
* @hidden
|
|
1705
|
+
*/
|
|
1706
|
+
blacklist?: string | RegExp | ((command: string) => boolean) | (string | RegExp | ((command: string) => boolean))[];
|
|
1707
|
+
|
|
1708
|
+
/**
|
|
1709
|
+
* Whether to use a different service name for each Redis instance based
|
|
1710
|
+
* on the configured connection name of the client.
|
|
1711
|
+
*
|
|
1712
|
+
* @default false
|
|
1713
|
+
*/
|
|
1714
|
+
splitByInstance?: boolean;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1669
1717
|
/**
|
|
1670
1718
|
* This plugin automatically instruments the
|
|
1671
1719
|
* [jest](https://github.com/jestjs/jest) module.
|
package/package.json
CHANGED
|
@@ -15,13 +15,16 @@ const startCh = channel('apm:amqplib:command:start')
|
|
|
15
15
|
const finishCh = channel('apm:amqplib:command:finish')
|
|
16
16
|
const errorCh = channel('apm:amqplib:command:error')
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const methods = {}
|
|
19
19
|
|
|
20
20
|
addHook({ name: 'amqplib', file: 'lib/defs.js', versions: [MIN_VERSION] }, defs => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
for (const [key, value] of Object.entries(defs)) {
|
|
22
|
+
if (Number.isInteger(value) && isCamelCase(key)) {
|
|
23
|
+
// TODO(BridgeAR): It should replace all `-` with `.`, so it has to be replaceAll or use a regex.
|
|
24
|
+
// Example method that is not renamed properly: BasicGetEmpty = 3932232
|
|
25
|
+
methods[value] = kebabCase(key).replace('-', '.')
|
|
26
|
+
}
|
|
27
|
+
}
|
|
25
28
|
return defs
|
|
26
29
|
})
|
|
27
30
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { errorMonitor } = require('events')
|
|
3
4
|
const util = require('util')
|
|
4
5
|
|
|
5
6
|
const {
|
|
@@ -227,7 +228,7 @@ function wrapChildProcessAsyncMethod (ChildProcess, shell = false) {
|
|
|
227
228
|
if (childProcess) {
|
|
228
229
|
let errorExecuted = false
|
|
229
230
|
|
|
230
|
-
childProcess.on(
|
|
231
|
+
childProcess.on(errorMonitor, (e) => {
|
|
231
232
|
errorExecuted = true
|
|
232
233
|
childProcessChannel.error.publish(e)
|
|
233
234
|
})
|
|
@@ -7,6 +7,8 @@ const {
|
|
|
7
7
|
} = require('./helpers/instrument')
|
|
8
8
|
const shimmer = require('../../datadog-shimmer')
|
|
9
9
|
|
|
10
|
+
const log = require('../../dd-trace/src/log')
|
|
11
|
+
|
|
10
12
|
// Create channels for Confluent Kafka JavaScript
|
|
11
13
|
const channels = {
|
|
12
14
|
producerStart: channel('apm:@confluentinc/kafka-javascript:produce:start'),
|
|
@@ -25,6 +27,8 @@ const channels = {
|
|
|
25
27
|
batchConsumerCommit: channel('apm:@confluentinc/kafka-javascript:consume-batch:commit')
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
const disabledHeaderWeakSet = new WeakSet()
|
|
31
|
+
|
|
28
32
|
// we need to store the offset per partition per topic for the consumer to track offsets for DSM
|
|
29
33
|
const latestConsumerOffsets = new Map()
|
|
30
34
|
|
|
@@ -206,7 +210,8 @@ function instrumentKafkaJS (kafkaJS) {
|
|
|
206
210
|
channels.producerStart.publish({
|
|
207
211
|
topic: payload?.topic,
|
|
208
212
|
messages: payload?.messages || [],
|
|
209
|
-
bootstrapServers: kafka._ddBrokers
|
|
213
|
+
bootstrapServers: kafka._ddBrokers,
|
|
214
|
+
disableHeaderInjection: disabledHeaderWeakSet.has(producer)
|
|
210
215
|
})
|
|
211
216
|
|
|
212
217
|
const result = send.apply(this, arguments)
|
|
@@ -218,6 +223,16 @@ function instrumentKafkaJS (kafkaJS) {
|
|
|
218
223
|
}),
|
|
219
224
|
asyncResource.bind(err => {
|
|
220
225
|
if (err) {
|
|
226
|
+
// Fixes bug where we would inject message headers for kafka brokers
|
|
227
|
+
// that don't support headers (version <0.11). On the error, we disable
|
|
228
|
+
// header injection. Tnfortunately the error name / type is not more specific.
|
|
229
|
+
// This approach is implemented by other tracers as well.
|
|
230
|
+
if (err.name === 'KafkaJSError' && err.type === 'ERR_UNKNOWN') {
|
|
231
|
+
disabledHeaderWeakSet.add(producer)
|
|
232
|
+
log.error('Kafka Broker responded with UNKNOWN_SERVER_ERROR (-1). ' +
|
|
233
|
+
'Please look at broker logs for more information. ' +
|
|
234
|
+
'Tracer message header injection for Kafka is disabled.')
|
|
235
|
+
}
|
|
221
236
|
channels.producerError.publish(err)
|
|
222
237
|
}
|
|
223
238
|
channels.producerFinish.publish(undefined)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { errorMonitor } = require('events')
|
|
3
4
|
const {
|
|
4
5
|
channel,
|
|
5
6
|
addHook,
|
|
@@ -186,7 +187,7 @@ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Buc
|
|
|
186
187
|
finishCh.publish(undefined)
|
|
187
188
|
}))
|
|
188
189
|
|
|
189
|
-
emitter.once(
|
|
190
|
+
emitter.once(errorMonitor, asyncResource.bind((error) => {
|
|
190
191
|
errorCh.publish(error)
|
|
191
192
|
finishCh.publish(undefined)
|
|
192
193
|
}))
|
|
@@ -8,6 +8,7 @@ const log = require('../../dd-trace/src/log')
|
|
|
8
8
|
const testStartCh = channel('ci:cucumber:test:start')
|
|
9
9
|
const testRetryCh = channel('ci:cucumber:test:retry')
|
|
10
10
|
const testFinishCh = channel('ci:cucumber:test:finish') // used for test steps too
|
|
11
|
+
const testFnCh = channel('ci:cucumber:test:fn')
|
|
11
12
|
|
|
12
13
|
const testStepStartCh = channel('ci:cucumber:test-step:start')
|
|
13
14
|
|
|
@@ -52,7 +53,7 @@ const patched = new WeakSet()
|
|
|
52
53
|
|
|
53
54
|
const lastStatusByPickleId = new Map()
|
|
54
55
|
const numRetriesByPickleId = new Map()
|
|
55
|
-
const
|
|
56
|
+
const numAttemptToCtx = new Map()
|
|
56
57
|
const newTestsByTestFullname = new Map()
|
|
57
58
|
|
|
58
59
|
let eventDataCollector = null
|
|
@@ -227,16 +228,12 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
227
228
|
patched.add(pl)
|
|
228
229
|
|
|
229
230
|
shimmer.wrap(pl.prototype, 'run', run => function () {
|
|
230
|
-
if (!
|
|
231
|
+
if (!testFinishCh.hasSubscribers) {
|
|
231
232
|
return run.apply(this, arguments)
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
let numAttempt = 0
|
|
235
236
|
|
|
236
|
-
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
237
|
-
|
|
238
|
-
numAttemptToAsyncResource.set(numAttempt, asyncResource)
|
|
239
|
-
|
|
240
237
|
const testFileAbsolutePath = this.pickle.uri
|
|
241
238
|
|
|
242
239
|
const testSourceLine = this.gherkinDocument?.feature?.location?.line
|
|
@@ -247,9 +244,9 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
247
244
|
testSourceLine,
|
|
248
245
|
isParallel: !!process.env.CUCUMBER_WORKER_ID
|
|
249
246
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
})
|
|
247
|
+
const ctx = testStartPayload
|
|
248
|
+
numAttemptToCtx.set(numAttempt, ctx)
|
|
249
|
+
testStartCh.runStores(ctx, () => { })
|
|
253
250
|
const promises = {}
|
|
254
251
|
try {
|
|
255
252
|
this.eventBroadcaster.on('envelope', shimmer.wrapFunction(null, () => async (testCase) => {
|
|
@@ -265,7 +262,7 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
265
262
|
// ignore error
|
|
266
263
|
}
|
|
267
264
|
|
|
268
|
-
const
|
|
265
|
+
const failedAttemptCtx = numAttemptToCtx.get(numAttempt)
|
|
269
266
|
const isFirstAttempt = numAttempt++ === 0
|
|
270
267
|
const isAtrRetry = !isFirstAttempt && isFlakyTestRetriesEnabled
|
|
271
268
|
|
|
@@ -273,23 +270,19 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
273
270
|
await promises.hitBreakpointPromise
|
|
274
271
|
}
|
|
275
272
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
testRetryCh.publish({ isFirstAttempt, error, isAtrRetry })
|
|
279
|
-
})
|
|
273
|
+
// the current span will be finished and a new one will be created
|
|
274
|
+
testRetryCh.publish({ isFirstAttempt, error, isAtrRetry, ...failedAttemptCtx.currentStore })
|
|
280
275
|
|
|
281
|
-
const
|
|
282
|
-
|
|
276
|
+
const newCtx = { ...testStartPayload, promises }
|
|
277
|
+
numAttemptToCtx.set(numAttempt, newCtx)
|
|
283
278
|
|
|
284
|
-
|
|
285
|
-
testStartCh.publish({ ...testStartPayload, promises }) // a new span will be created
|
|
286
|
-
})
|
|
279
|
+
testStartCh.runStores(newCtx, () => { })
|
|
287
280
|
}
|
|
288
281
|
}
|
|
289
282
|
}))
|
|
290
283
|
let promise
|
|
291
284
|
|
|
292
|
-
|
|
285
|
+
testFnCh.runStores(ctx, () => {
|
|
293
286
|
promise = run.apply(this, arguments)
|
|
294
287
|
})
|
|
295
288
|
promise.finally(async () => {
|
|
@@ -343,39 +336,40 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
343
336
|
isEfdRetry = numRetries > 0
|
|
344
337
|
}
|
|
345
338
|
|
|
346
|
-
const
|
|
339
|
+
const attemptCtx = numAttemptToCtx.get(numAttempt)
|
|
347
340
|
|
|
348
341
|
const error = getErrorFromCucumberResult(result)
|
|
349
342
|
|
|
350
343
|
if (promises.hitBreakpointPromise) {
|
|
351
344
|
await promises.hitBreakpointPromise
|
|
352
345
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
})
|
|
346
|
+
testFinishCh.publish({
|
|
347
|
+
status,
|
|
348
|
+
skipReason,
|
|
349
|
+
error,
|
|
350
|
+
isNew,
|
|
351
|
+
isEfdRetry,
|
|
352
|
+
isFlakyRetry: numAttempt > 0,
|
|
353
|
+
isAttemptToFix,
|
|
354
|
+
isAttemptToFixRetry,
|
|
355
|
+
hasFailedAllRetries,
|
|
356
|
+
hasPassedAllRetries,
|
|
357
|
+
hasFailedAttemptToFix,
|
|
358
|
+
isDisabled,
|
|
359
|
+
isQuarantined,
|
|
360
|
+
...attemptCtx.currentStore
|
|
369
361
|
})
|
|
370
362
|
})
|
|
371
363
|
return promise
|
|
372
364
|
} catch (err) {
|
|
373
|
-
|
|
374
|
-
|
|
365
|
+
ctx.err = err
|
|
366
|
+
errorCh.runStores(ctx, () => {
|
|
367
|
+
throw err
|
|
368
|
+
})
|
|
375
369
|
}
|
|
376
370
|
})
|
|
377
371
|
shimmer.wrap(pl.prototype, 'runStep', runStep => function () {
|
|
378
|
-
if (!
|
|
372
|
+
if (!testFinishCh.hasSubscribers) {
|
|
379
373
|
return runStep.apply(this, arguments)
|
|
380
374
|
}
|
|
381
375
|
const testStep = arguments[0]
|
|
@@ -387,9 +381,8 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
387
381
|
resource = testStep.isHook ? 'hook' : testStep.pickleStep.text
|
|
388
382
|
}
|
|
389
383
|
|
|
390
|
-
const
|
|
391
|
-
return
|
|
392
|
-
testStepStartCh.publish({ resource })
|
|
384
|
+
const ctx = { resource }
|
|
385
|
+
return testStepStartCh.runStores(ctx, () => {
|
|
393
386
|
try {
|
|
394
387
|
const promise = runStep.apply(this, arguments)
|
|
395
388
|
|
|
@@ -398,12 +391,14 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
398
391
|
? getStatusFromResultLatest(result)
|
|
399
392
|
: getStatusFromResult(result)
|
|
400
393
|
|
|
401
|
-
testFinishCh.publish({ isStep: true, status, skipReason, errorMessage })
|
|
394
|
+
testFinishCh.publish({ isStep: true, status, skipReason, errorMessage, ...ctx.currentStore })
|
|
402
395
|
})
|
|
403
396
|
return promise
|
|
404
397
|
} catch (err) {
|
|
405
|
-
|
|
406
|
-
|
|
398
|
+
ctx.err = err
|
|
399
|
+
errorCh.runStores(ctx, () => {
|
|
400
|
+
throw err
|
|
401
|
+
})
|
|
407
402
|
}
|
|
408
403
|
})
|
|
409
404
|
})
|
|
@@ -149,11 +149,9 @@ addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'] }, express => {
|
|
|
149
149
|
const queryReadCh = channel('datadog:express:query:finish')
|
|
150
150
|
|
|
151
151
|
addHook({ name: 'express', file: ['lib/request.js'], versions: ['>=5.0.0'] }, request => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
shimmer.wrap(requestDescriptor, 'get', function (originalGet) {
|
|
152
|
+
shimmer.wrap(request, 'query', function (originalGet) {
|
|
155
153
|
return function wrappedGet () {
|
|
156
|
-
const query = originalGet.
|
|
154
|
+
const query = originalGet.call(this)
|
|
157
155
|
|
|
158
156
|
if (queryReadCh.hasSubscribers && query) {
|
|
159
157
|
queryReadCh.publish({ query })
|
|
@@ -163,7 +161,5 @@ addHook({ name: 'express', file: ['lib/request.js'], versions: ['>=5.0.0'] }, re
|
|
|
163
161
|
}
|
|
164
162
|
})
|
|
165
163
|
|
|
166
|
-
Object.defineProperty(request, 'query', requestDescriptor)
|
|
167
|
-
|
|
168
164
|
return request
|
|
169
165
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { errorMonitor } = require('events')
|
|
3
4
|
const { channel, addHook } = require('./helpers/instrument')
|
|
4
5
|
const shimmer = require('../../datadog-shimmer')
|
|
5
6
|
|
|
@@ -199,16 +200,16 @@ function wrapCreateStream (original) {
|
|
|
199
200
|
}
|
|
200
201
|
const onFinish = () => {
|
|
201
202
|
finishChannel.runStores(ctx, () => {})
|
|
202
|
-
stream.
|
|
203
|
-
stream.
|
|
204
|
-
stream.
|
|
205
|
-
stream.
|
|
203
|
+
stream.removeListener('close', onFinish)
|
|
204
|
+
stream.removeListener('end', onFinish)
|
|
205
|
+
stream.removeListener('finish', onFinish)
|
|
206
|
+
stream.removeListener(errorMonitor, onError)
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
stream.once('close', onFinish)
|
|
209
210
|
stream.once('end', onFinish)
|
|
210
211
|
stream.once('finish', onFinish)
|
|
211
|
-
stream.once(
|
|
212
|
+
stream.once(errorMonitor, onError)
|
|
212
213
|
|
|
213
214
|
return stream
|
|
214
215
|
} catch (error) {
|
|
@@ -61,6 +61,7 @@ module.exports = {
|
|
|
61
61
|
http2: () => require('../http2'),
|
|
62
62
|
https: () => require('../http'),
|
|
63
63
|
ioredis: () => require('../ioredis'),
|
|
64
|
+
iovalkey: () => require('../iovalkey'),
|
|
64
65
|
'jest-circus': () => require('../jest'),
|
|
65
66
|
'jest-config': () => require('../jest'),
|
|
66
67
|
'jest-environment-node': () => require('../jest'),
|
|
@@ -43,7 +43,7 @@ if (!disabledInstrumentations.has('process')) {
|
|
|
43
43
|
require('../process')
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const HOOK_SYMBOL = Symbol('
|
|
46
|
+
const HOOK_SYMBOL = Symbol('hookExportsSet')
|
|
47
47
|
|
|
48
48
|
if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') {
|
|
49
49
|
checkRequireCache.checkForRequiredModules()
|
|
@@ -89,12 +89,13 @@ for (const packageName of names) {
|
|
|
89
89
|
fullFilePattern = filename(name, fullFilePattern)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// Create a
|
|
92
|
+
// Create a WeakSet associated with the hook function so that patches on the same moduleExport only happens once
|
|
93
93
|
// for example by instrumenting both dns and node:dns double the spans would be created
|
|
94
|
-
// since they both patch the same moduleExport, this
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
// since they both patch the same moduleExport, this WeakSet is used to mitigate that
|
|
95
|
+
// TODO(BridgeAR): Instead of using a WeakSet here, why not just use aliases for the hook in register?
|
|
96
|
+
// That way it would also not be duplicated. The actual name being used has to be identified else wise.
|
|
97
|
+
// Maybe it is also not important to know what name was actually used?
|
|
98
|
+
hook[HOOK_SYMBOL] ??= new WeakSet()
|
|
98
99
|
let matchesFile = false
|
|
99
100
|
|
|
100
101
|
matchesFile = moduleName === fullFilename
|
|
@@ -114,7 +115,7 @@ for (const packageName of names) {
|
|
|
114
115
|
log.error('Error getting version for "%s": %s', name, e.message, e)
|
|
115
116
|
continue
|
|
116
117
|
}
|
|
117
|
-
if (
|
|
118
|
+
if (namesAndSuccesses[`${name}@${version}`] === undefined) {
|
|
118
119
|
// TODO If `file` is present, we might elsewhere instrument the result of the module
|
|
119
120
|
// for a version range that actually matches, so we can't assume that we're _not_
|
|
120
121
|
// going to instrument that. However, the way the data model around instrumentation
|
|
@@ -140,12 +141,16 @@ for (const packageName of names) {
|
|
|
140
141
|
loadChannel.publish({ name, version, file })
|
|
141
142
|
// Send the name and version of the module back to the callback because now addHook
|
|
142
143
|
// takes in an array of names so by passing the name the callback will know which module name is being used
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
|
|
144
|
+
// TODO(BridgeAR): This is only true in case the name is identical
|
|
145
|
+
// in all loads. If they deviate, the deviating name would not be
|
|
146
|
+
// picked up due to the unification. Check what modules actually use the name.
|
|
147
|
+
// TODO(BridgeAR): Only replace moduleExports if the hook returns a new value.
|
|
148
|
+
// This allows to reduce the instrumentation code (no return needed).
|
|
149
|
+
moduleExports = hook(moduleExports, version, name) ?? moduleExports
|
|
150
|
+
// Set the moduleExports in the hooks WeakSet
|
|
151
|
+
hook[HOOK_SYMBOL].add(moduleExports)
|
|
146
152
|
} catch (e) {
|
|
147
|
-
log.info('Error during ddtrace instrumentation of application, aborting.')
|
|
148
|
-
log.info(e)
|
|
153
|
+
log.info('Error during ddtrace instrumentation of application, aborting.', e)
|
|
149
154
|
telemetry('error', [
|
|
150
155
|
`error_type:${e.constructor.name}`,
|
|
151
156
|
`integration:${name}`,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* eslint-disable no-fallthrough */
|
|
4
4
|
|
|
5
5
|
const url = require('url')
|
|
6
|
+
const { errorMonitor } = require('events')
|
|
6
7
|
const { channel, addHook } = require('../helpers/instrument')
|
|
7
8
|
const shimmer = require('../../../datadog-shimmer')
|
|
8
9
|
|
|
@@ -88,7 +89,7 @@ function patch (http, methodName) {
|
|
|
88
89
|
const res = arg
|
|
89
90
|
ctx.res = res
|
|
90
91
|
res.on('end', finish)
|
|
91
|
-
res.on(
|
|
92
|
+
res.on(errorMonitor, finish)
|
|
92
93
|
break
|
|
93
94
|
}
|
|
94
95
|
case 'connect':
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook,
|
|
6
|
+
AsyncResource
|
|
7
|
+
} = require('./helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
const startCh = channel('apm:iovalkey:command:start')
|
|
11
|
+
const finishCh = channel('apm:iovalkey:command:finish')
|
|
12
|
+
const errorCh = channel('apm:iovalkey:command:error')
|
|
13
|
+
|
|
14
|
+
addHook({ name: 'iovalkey', versions: ['>=0.0.1'] }, Valkey => {
|
|
15
|
+
shimmer.wrap(Valkey.prototype, 'sendCommand', sendCommand => function (command, stream) {
|
|
16
|
+
if (!startCh.hasSubscribers) return sendCommand.apply(this, arguments)
|
|
17
|
+
|
|
18
|
+
if (!command?.promise) return sendCommand.apply(this, arguments)
|
|
19
|
+
|
|
20
|
+
const options = this.options || {}
|
|
21
|
+
const connectionName = options.connectionName
|
|
22
|
+
const db = options.db
|
|
23
|
+
const connectionOptions = { host: options.host, port: options.port }
|
|
24
|
+
|
|
25
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
26
|
+
return asyncResource.runInAsyncScope(() => {
|
|
27
|
+
startCh.publish({ db, command: command.name, args: command.args, connectionOptions, connectionName })
|
|
28
|
+
|
|
29
|
+
const onResolve = asyncResource.bind(() => finishCh.publish())
|
|
30
|
+
const onReject = asyncResource.bind(err => finish(finishCh, errorCh, err))
|
|
31
|
+
|
|
32
|
+
command.promise.then(onResolve, onReject)
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
return sendCommand.apply(this, arguments)
|
|
36
|
+
} catch (err) {
|
|
37
|
+
errorCh.publish(err)
|
|
38
|
+
|
|
39
|
+
throw err
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
return Valkey
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
function finish (finishCh, errorCh, error) {
|
|
47
|
+
if (error) {
|
|
48
|
+
errorCh.publish(error)
|
|
49
|
+
}
|
|
50
|
+
finishCh.publish()
|
|
51
|
+
}
|