dd-trace 4.21.0 → 4.23.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/index.d.ts +5 -0
- package/package.json +6 -6
- package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
- package/packages/datadog-instrumentations/src/child-process.js +4 -5
- package/packages/datadog-instrumentations/src/couchbase.js +5 -4
- package/packages/datadog-instrumentations/src/crypto.js +2 -1
- package/packages/datadog-instrumentations/src/dns.js +2 -1
- package/packages/datadog-instrumentations/src/graphql.js +18 -4
- package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +8 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +18 -2
- package/packages/datadog-instrumentations/src/http/client.js +4 -16
- package/packages/datadog-instrumentations/src/http/server.js +7 -4
- package/packages/datadog-instrumentations/src/http2/client.js +3 -1
- package/packages/datadog-instrumentations/src/http2/server.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/net.js +10 -2
- package/packages/datadog-instrumentations/src/next.js +15 -5
- package/packages/datadog-instrumentations/src/rhea.js +15 -9
- package/packages/datadog-plugin-cucumber/src/index.js +34 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
- package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +38 -4
- package/packages/datadog-plugin-mocha/src/index.js +32 -1
- package/packages/datadog-plugin-next/src/index.js +32 -6
- package/packages/datadog-plugin-playwright/src/index.js +17 -1
- package/packages/dd-trace/src/appsec/activation.js +29 -0
- package/packages/dd-trace/src/appsec/addresses.js +1 -0
- package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
- package/packages/dd-trace/src/appsec/blocking.js +95 -43
- package/packages/dd-trace/src/appsec/channels.js +4 -1
- package/packages/dd-trace/src/appsec/graphql.js +146 -0
- package/packages/dd-trace/src/appsec/index.js +29 -40
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +25 -13
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +36 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +18 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
- package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
- package/packages/dd-trace/src/config.js +104 -58
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
- package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +44 -8
- package/packages/dd-trace/src/plugins/index.js +5 -0
- package/packages/dd-trace/src/plugins/util/exec.js +23 -2
- package/packages/dd-trace/src/plugins/util/git.js +94 -19
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
- package/packages/dd-trace/src/priority_sampler.js +30 -38
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +7 -6
- package/packages/dd-trace/src/profiling/profilers/events.js +18 -13
- package/packages/dd-trace/src/profiling/profilers/shared.js +34 -4
- package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/sampling_rule.js +130 -0
- package/packages/dd-trace/src/span_sampler.js +6 -64
- package/packages/dd-trace/src/telemetry/index.js +43 -5
package/index.d.ts
CHANGED
|
@@ -567,6 +567,11 @@ export declare interface TracerOptions {
|
|
|
567
567
|
*/
|
|
568
568
|
blockedTemplateJson?: string,
|
|
569
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Specifies a path to a custom blocking template json file for graphql requests
|
|
572
|
+
*/
|
|
573
|
+
blockedTemplateGraphql?: string,
|
|
574
|
+
|
|
570
575
|
/**
|
|
571
576
|
* Controls the automated user event tracking configuration
|
|
572
577
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.23.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"node": ">=16"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@datadog/native-appsec": "
|
|
72
|
-
"@datadog/native-iast-rewriter": "2.2.
|
|
71
|
+
"@datadog/native-appsec": "6.0.0",
|
|
72
|
+
"@datadog/native-iast-rewriter": "2.2.2",
|
|
73
73
|
"@datadog/native-iast-taint-tracking": "1.6.4",
|
|
74
74
|
"@datadog/native-metrics": "^2.0.0",
|
|
75
75
|
"@datadog/pprof": "4.1.0",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"crypto-randomuuid": "^1.0.0",
|
|
80
80
|
"dc-polyfill": "^0.1.2",
|
|
81
81
|
"ignore": "^5.2.4",
|
|
82
|
-
"import-in-the-middle": "^1.
|
|
82
|
+
"import-in-the-middle": "^1.7.1",
|
|
83
83
|
"int64-buffer": "^0.1.9",
|
|
84
84
|
"ipaddr.js": "^2.1.0",
|
|
85
85
|
"istanbul-lib-coverage": "3.2.0",
|
|
@@ -99,9 +99,9 @@
|
|
|
99
99
|
"path-to-regexp": "^0.1.2",
|
|
100
100
|
"pprof-format": "^2.0.7",
|
|
101
101
|
"protobufjs": "^7.2.5",
|
|
102
|
-
"tlhunter-sorted-set": "^0.1.0",
|
|
103
102
|
"retry": "^0.13.1",
|
|
104
|
-
"semver": "^7.5.4"
|
|
103
|
+
"semver": "^7.5.4",
|
|
104
|
+
"tlhunter-sorted-set": "^0.1.0"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
107
|
"@types/node": ">=16",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AbortController } = require('node-abort-controller')
|
|
4
|
+
const { addHook } = require('./helpers/instrument')
|
|
5
|
+
const shimmer = require('../../datadog-shimmer')
|
|
6
|
+
const dc = require('dc-polyfill')
|
|
7
|
+
|
|
8
|
+
const requestChannel = dc.tracingChannel('datadog:apollo-server-core:request')
|
|
9
|
+
|
|
10
|
+
addHook({ name: 'apollo-server-core', file: 'dist/runHttpQuery.js', versions: ['>3.0.0'] }, runHttpQueryModule => {
|
|
11
|
+
const HttpQueryError = runHttpQueryModule.HttpQueryError
|
|
12
|
+
|
|
13
|
+
shimmer.wrap(runHttpQueryModule, 'runHttpQuery', function wrapRunHttpQuery (originalRunHttpQuery) {
|
|
14
|
+
return async function runHttpQuery () {
|
|
15
|
+
if (!requestChannel.start.hasSubscribers) {
|
|
16
|
+
return originalRunHttpQuery.apply(this, arguments)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const abortController = new AbortController()
|
|
20
|
+
const abortData = {}
|
|
21
|
+
|
|
22
|
+
const runHttpQueryResult = requestChannel.tracePromise(
|
|
23
|
+
originalRunHttpQuery,
|
|
24
|
+
{ abortController, abortData },
|
|
25
|
+
this,
|
|
26
|
+
...arguments)
|
|
27
|
+
|
|
28
|
+
const abortPromise = new Promise((resolve, reject) => {
|
|
29
|
+
abortController.signal.addEventListener('abort', (event) => {
|
|
30
|
+
// runHttpQuery callbacks are writing the response on resolve/reject.
|
|
31
|
+
// We should return blocking data in the apollo-server-core HttpQueryError object
|
|
32
|
+
reject(new HttpQueryError(abortData.statusCode, abortData.message, true, abortData.headers))
|
|
33
|
+
}, { once: true })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
return Promise.race([runHttpQueryResult, abortPromise])
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return runHttpQueryModule
|
|
41
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AbortController } = require('node-abort-controller')
|
|
4
|
+
const dc = require('dc-polyfill')
|
|
5
|
+
|
|
6
|
+
const { addHook } = require('./helpers/instrument')
|
|
7
|
+
const shimmer = require('../../datadog-shimmer')
|
|
8
|
+
|
|
9
|
+
const graphqlMiddlewareChannel = dc.tracingChannel('datadog:apollo:middleware')
|
|
10
|
+
|
|
11
|
+
const requestChannel = dc.tracingChannel('datadog:apollo:request')
|
|
12
|
+
|
|
13
|
+
let HeaderMap
|
|
14
|
+
|
|
15
|
+
function wrapExecuteHTTPGraphQLRequest (originalExecuteHTTPGraphQLRequest) {
|
|
16
|
+
return async function executeHTTPGraphQLRequest () {
|
|
17
|
+
if (!HeaderMap || !requestChannel.start.hasSubscribers) {
|
|
18
|
+
return originalExecuteHTTPGraphQLRequest.apply(this, arguments)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const abortController = new AbortController()
|
|
22
|
+
const abortData = {}
|
|
23
|
+
|
|
24
|
+
const graphqlResponseData = requestChannel.tracePromise(
|
|
25
|
+
originalExecuteHTTPGraphQLRequest,
|
|
26
|
+
{ abortController, abortData },
|
|
27
|
+
this,
|
|
28
|
+
...arguments)
|
|
29
|
+
|
|
30
|
+
const abortPromise = new Promise((resolve, reject) => {
|
|
31
|
+
abortController.signal.addEventListener('abort', (event) => {
|
|
32
|
+
// This method is expected to return response data
|
|
33
|
+
// with headers, status and body
|
|
34
|
+
const headers = new HeaderMap()
|
|
35
|
+
Object.keys(abortData.headers).forEach(key => {
|
|
36
|
+
headers.set(key, abortData.headers[key])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
resolve({
|
|
40
|
+
headers: headers,
|
|
41
|
+
status: abortData.statusCode,
|
|
42
|
+
body: {
|
|
43
|
+
kind: 'complete',
|
|
44
|
+
string: abortData.message
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
}, { once: true })
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return Promise.race([abortPromise, graphqlResponseData])
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function apolloExpress4Hook (express4) {
|
|
55
|
+
shimmer.wrap(express4, 'expressMiddleware', function wrapExpressMiddleware (originalExpressMiddleware) {
|
|
56
|
+
return function expressMiddleware (server, options) {
|
|
57
|
+
const originalMiddleware = originalExpressMiddleware.apply(this, arguments)
|
|
58
|
+
|
|
59
|
+
return shimmer.wrap(originalMiddleware, function (req, res, next) {
|
|
60
|
+
if (!graphqlMiddlewareChannel.start.hasSubscribers) {
|
|
61
|
+
return originalMiddleware.apply(this, arguments)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return graphqlMiddlewareChannel.traceSync(originalMiddleware, { req }, this, ...arguments)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
return express4
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function apolloHeaderMapHook (headerMap) {
|
|
72
|
+
HeaderMap = headerMap.HeaderMap
|
|
73
|
+
return headerMap
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function apolloServerHook (apolloServer) {
|
|
77
|
+
shimmer.wrap(apolloServer.ApolloServer.prototype, 'executeHTTPGraphQLRequest', wrapExecuteHTTPGraphQLRequest)
|
|
78
|
+
return apolloServer
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
addHook({ name: '@apollo/server', file: 'dist/cjs/ApolloServer.js', versions: ['>=4.0.0'] }, apolloServerHook)
|
|
82
|
+
addHook({ name: '@apollo/server', file: 'dist/cjs/express4/index.js', versions: ['>=4.0.0'] }, apolloExpress4Hook)
|
|
83
|
+
addHook({ name: '@apollo/server', file: 'dist/cjs/utils/HeaderMap.js', versions: ['>=4.0.0'] }, apolloHeaderMapHook)
|
|
@@ -9,11 +9,10 @@ const shimmer = require('../../datadog-shimmer')
|
|
|
9
9
|
const childProcessChannel = channel('datadog:child_process:execution:start')
|
|
10
10
|
const execMethods = ['exec', 'execFile', 'fork', 'spawn', 'execFileSync', 'execSync', 'spawnSync']
|
|
11
11
|
const names = ['child_process', 'node:child_process']
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
12
|
+
|
|
13
|
+
addHook({ name: names }, childProcess => {
|
|
14
|
+
shimmer.massWrap(childProcess, execMethods, wrapChildProcessMethod())
|
|
15
|
+
return childProcess
|
|
17
16
|
})
|
|
18
17
|
|
|
19
18
|
function wrapChildProcessMethod () {
|
|
@@ -252,9 +252,10 @@ addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^3.0.7', '^3.1.
|
|
|
252
252
|
return Cluster
|
|
253
253
|
})
|
|
254
254
|
|
|
255
|
-
// semver >=3.2.
|
|
255
|
+
// semver >=3.2.2
|
|
256
|
+
// NOTE: <3.2.2 segfaults on cluster.close() https://issues.couchbase.com/browse/JSCBC-936
|
|
256
257
|
|
|
257
|
-
addHook({ name: 'couchbase', file: 'dist/collection.js', versions: ['>=3.2.
|
|
258
|
+
addHook({ name: 'couchbase', file: 'dist/collection.js', versions: ['>=3.2.2'] }, collection => {
|
|
258
259
|
const Collection = collection.Collection
|
|
259
260
|
|
|
260
261
|
wrapAllNames(['upsert', 'insert', 'replace'], name => {
|
|
@@ -264,7 +265,7 @@ addHook({ name: 'couchbase', file: 'dist/collection.js', versions: ['>=3.2.0'] }
|
|
|
264
265
|
return collection
|
|
265
266
|
})
|
|
266
267
|
|
|
267
|
-
addHook({ name: 'couchbase', file: 'dist/bucket.js', versions: ['>=3.2.
|
|
268
|
+
addHook({ name: 'couchbase', file: 'dist/bucket.js', versions: ['>=3.2.2'] }, bucket => {
|
|
268
269
|
const Bucket = bucket.Bucket
|
|
269
270
|
shimmer.wrap(Bucket.prototype, 'collection', getCollection => {
|
|
270
271
|
return function () {
|
|
@@ -278,7 +279,7 @@ addHook({ name: 'couchbase', file: 'dist/bucket.js', versions: ['>=3.2.0'] }, bu
|
|
|
278
279
|
return bucket
|
|
279
280
|
})
|
|
280
281
|
|
|
281
|
-
addHook({ name: 'couchbase', file: 'dist/cluster.js', versions: ['
|
|
282
|
+
addHook({ name: 'couchbase', file: 'dist/cluster.js', versions: ['>=3.2.2'] }, (cluster) => {
|
|
282
283
|
const Cluster = cluster.Cluster
|
|
283
284
|
|
|
284
285
|
shimmer.wrap(Cluster.prototype, 'query', wrapV3Query)
|
|
@@ -11,8 +11,9 @@ const cryptoCipherCh = channel('datadog:crypto:cipher:start')
|
|
|
11
11
|
|
|
12
12
|
const hashMethods = ['createHash', 'createHmac', 'createSign', 'createVerify', 'sign', 'verify']
|
|
13
13
|
const cipherMethods = ['createCipheriv', 'createDecipheriv']
|
|
14
|
+
const names = ['crypto', 'node:crypto']
|
|
14
15
|
|
|
15
|
-
addHook({ name:
|
|
16
|
+
addHook({ name: names }, crypto => {
|
|
16
17
|
shimmer.massWrap(crypto, hashMethods, wrapCryptoMethod(cryptoHashCh))
|
|
17
18
|
shimmer.massWrap(crypto, cipherMethods, wrapCryptoMethod(cryptoCipherCh))
|
|
18
19
|
return crypto
|
|
@@ -18,8 +18,9 @@ const rrtypes = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const rrtypeMap = new WeakMap()
|
|
21
|
+
const names = ['dns', 'node:dns']
|
|
21
22
|
|
|
22
|
-
addHook({ name:
|
|
23
|
+
addHook({ name: names }, dns => {
|
|
23
24
|
dns.lookup = wrap('apm:dns:lookup', dns.lookup, 2)
|
|
24
25
|
dns.lookupService = wrap('apm:dns:lookup_service', dns.lookupService, 3)
|
|
25
26
|
dns.resolve = wrap('apm:dns:resolve', dns.resolve, 2)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { AbortController } = require('node-abort-controller')
|
|
4
|
+
|
|
3
5
|
const {
|
|
4
6
|
addHook,
|
|
5
7
|
channel,
|
|
@@ -37,6 +39,13 @@ const validateStartCh = channel('apm:graphql:validate:start')
|
|
|
37
39
|
const validateFinishCh = channel('apm:graphql:validate:finish')
|
|
38
40
|
const validateErrorCh = channel('apm:graphql:validate:error')
|
|
39
41
|
|
|
42
|
+
class AbortError extends Error {
|
|
43
|
+
constructor (message) {
|
|
44
|
+
super(message)
|
|
45
|
+
this.name = 'AbortError'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
function getOperation (document, operationName) {
|
|
41
50
|
if (!document || !Array.isArray(document.definitions)) {
|
|
42
51
|
return
|
|
@@ -175,11 +184,11 @@ function wrapExecute (execute) {
|
|
|
175
184
|
docSource: documentSources.get(document)
|
|
176
185
|
})
|
|
177
186
|
|
|
178
|
-
const context = { source, asyncResource, fields: {} }
|
|
187
|
+
const context = { source, asyncResource, fields: {}, abortController: new AbortController() }
|
|
179
188
|
|
|
180
189
|
contexts.set(contextValue, context)
|
|
181
190
|
|
|
182
|
-
return callInAsyncScope(exe, asyncResource, this, arguments, (err, res) => {
|
|
191
|
+
return callInAsyncScope(exe, asyncResource, this, arguments, context.abortController, (err, res) => {
|
|
183
192
|
if (finishResolveCh.hasSubscribers) finishResolvers(context)
|
|
184
193
|
|
|
185
194
|
const error = err || (res && res.errors && res.errors[0])
|
|
@@ -207,7 +216,7 @@ function wrapResolve (resolve) {
|
|
|
207
216
|
|
|
208
217
|
const field = assertField(context, info, args)
|
|
209
218
|
|
|
210
|
-
return callInAsyncScope(resolve, field.asyncResource, this, arguments, (err) => {
|
|
219
|
+
return callInAsyncScope(resolve, field.asyncResource, this, arguments, context.abortController, (err) => {
|
|
211
220
|
updateFieldCh.publish({ field, info, err })
|
|
212
221
|
})
|
|
213
222
|
}
|
|
@@ -217,10 +226,15 @@ function wrapResolve (resolve) {
|
|
|
217
226
|
return resolveAsync
|
|
218
227
|
}
|
|
219
228
|
|
|
220
|
-
function callInAsyncScope (fn, aR, thisArg, args, cb) {
|
|
229
|
+
function callInAsyncScope (fn, aR, thisArg, args, abortController, cb) {
|
|
221
230
|
cb = cb || (() => {})
|
|
222
231
|
|
|
223
232
|
return aR.runInAsyncScope(() => {
|
|
233
|
+
if (abortController?.signal.aborted) {
|
|
234
|
+
cb(null, null)
|
|
235
|
+
throw new AbortError('Aborted')
|
|
236
|
+
}
|
|
237
|
+
|
|
224
238
|
try {
|
|
225
239
|
const result = fn.apply(thisArg, args)
|
|
226
240
|
if (result && typeof result.then === 'function') {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
+
'@apollo/server': () => require('../apollo-server'),
|
|
5
|
+
'apollo-server-core': () => require('../apollo-server-core'),
|
|
4
6
|
'@aws-sdk/smithy-client': () => require('../aws-sdk'),
|
|
5
7
|
'@cucumber/cucumber': () => require('../cucumber'),
|
|
6
8
|
'@playwright/test': () => require('../playwright'),
|
|
@@ -29,7 +31,6 @@ module.exports = {
|
|
|
29
31
|
'bunyan': () => require('../bunyan'),
|
|
30
32
|
'cassandra-driver': () => require('../cassandra-driver'),
|
|
31
33
|
'child_process': () => require('../child-process'),
|
|
32
|
-
'node:child_process': () => require('../child-process'),
|
|
33
34
|
'connect': () => require('../connect'),
|
|
34
35
|
'cookie': () => require('../cookie'),
|
|
35
36
|
'cookie-parser': () => require('../cookie-parser'),
|
|
@@ -43,7 +44,6 @@ module.exports = {
|
|
|
43
44
|
'fastify': () => require('../fastify'),
|
|
44
45
|
'find-my-way': () => require('../find-my-way'),
|
|
45
46
|
'fs': () => require('../fs'),
|
|
46
|
-
'node:fs': () => require('../fs'),
|
|
47
47
|
'generic-pool': () => require('../generic-pool'),
|
|
48
48
|
'graphql': () => require('../graphql'),
|
|
49
49
|
'grpc': () => require('../grpc'),
|
|
@@ -77,6 +77,13 @@ module.exports = {
|
|
|
77
77
|
'mysql2': () => require('../mysql2'),
|
|
78
78
|
'net': () => require('../net'),
|
|
79
79
|
'next': () => require('../next'),
|
|
80
|
+
'node:child_process': () => require('../child-process'),
|
|
81
|
+
'node:crypto': () => require('../crypto'),
|
|
82
|
+
'node:dns': () => require('../dns'),
|
|
83
|
+
'node:http': () => require('../http'),
|
|
84
|
+
'node:http2': () => require('../http2'),
|
|
85
|
+
'node:https': () => require('../http'),
|
|
86
|
+
'node:net': () => require('../net'),
|
|
80
87
|
'oracledb': () => require('../oracledb'),
|
|
81
88
|
'openai': () => require('../openai'),
|
|
82
89
|
'paperplane': () => require('../paperplane'),
|
|
@@ -21,11 +21,16 @@ exports.channel = function (name) {
|
|
|
21
21
|
* @param Function hook
|
|
22
22
|
*/
|
|
23
23
|
exports.addHook = function addHook ({ name, versions, file }, hook) {
|
|
24
|
-
if (
|
|
25
|
-
|
|
24
|
+
if (typeof name === 'string') {
|
|
25
|
+
name = [name]
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
for (const val of name) {
|
|
29
|
+
if (!instrumentations[val]) {
|
|
30
|
+
instrumentations[val] = []
|
|
31
|
+
}
|
|
32
|
+
instrumentations[val].push({ name: val, versions, file, hook })
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
// AsyncResource.bind exists and binds `this` properly only from 17.8.0 and up.
|
|
@@ -24,6 +24,7 @@ if (!disabledInstrumentations.has('fetch')) {
|
|
|
24
24
|
require('../fetch')
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const HOOK_SYMBOL = Symbol('hookExportsMap')
|
|
27
28
|
// TODO: make this more efficient
|
|
28
29
|
|
|
29
30
|
for (const packageName of names) {
|
|
@@ -42,14 +43,29 @@ for (const packageName of names) {
|
|
|
42
43
|
for (const { name, file, versions, hook } of instrumentations[packageName]) {
|
|
43
44
|
const fullFilename = filename(name, file)
|
|
44
45
|
|
|
46
|
+
// Create a WeakMap associated with the hook function so that patches on the same moduleExport only happens once
|
|
47
|
+
// for example by instrumenting both dns and node:dns double the spans would be created
|
|
48
|
+
// since they both patch the same moduleExport, this WeakMap is used to mitigate that
|
|
49
|
+
if (!hook[HOOK_SYMBOL]) {
|
|
50
|
+
hook[HOOK_SYMBOL] = new WeakMap()
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
if (moduleName === fullFilename) {
|
|
46
54
|
const version = moduleVersion || getVersion(moduleBaseDir)
|
|
47
55
|
|
|
48
56
|
if (matchVersion(version, versions)) {
|
|
57
|
+
// Check if the hook already has a set moduleExport
|
|
58
|
+
if (hook[HOOK_SYMBOL].has(moduleExports)) {
|
|
59
|
+
return moduleExports
|
|
60
|
+
}
|
|
61
|
+
|
|
49
62
|
try {
|
|
50
63
|
loadChannel.publish({ name, version, file })
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
// Send the name and version of the module back to the callback because now addHook
|
|
65
|
+
// takes in an array of names so by passing the name the callback will know which module name is being used
|
|
66
|
+
moduleExports = hook(moduleExports, version, name)
|
|
67
|
+
// Set the moduleExports in the hooks weakmap
|
|
68
|
+
hook[HOOK_SYMBOL].set(moduleExports, name)
|
|
53
69
|
} catch (e) {
|
|
54
70
|
log.error(e)
|
|
55
71
|
}
|
|
@@ -14,9 +14,9 @@ const endChannel = channel('apm:http:client:request:end')
|
|
|
14
14
|
const asyncStartChannel = channel('apm:http:client:request:asyncStart')
|
|
15
15
|
const errorChannel = channel('apm:http:client:request:error')
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const names = ['http', 'https', 'node:http', 'node:https']
|
|
18
18
|
|
|
19
|
-
addHook({ name:
|
|
19
|
+
addHook({ name: names }, hookFn)
|
|
20
20
|
|
|
21
21
|
function hookFn (http) {
|
|
22
22
|
patch(http, 'request')
|
|
@@ -69,29 +69,17 @@ function patch (http, methodName) {
|
|
|
69
69
|
try {
|
|
70
70
|
const req = request.call(this, options, callback)
|
|
71
71
|
const emit = req.emit
|
|
72
|
-
|
|
73
|
-
const requestSetTimeout = req.setTimeout
|
|
72
|
+
const setTimeout = req.setTimeout
|
|
74
73
|
|
|
75
74
|
ctx.req = req
|
|
76
75
|
|
|
77
76
|
// tracked to accurately discern custom request socket timeout
|
|
78
77
|
let customRequestTimeout = false
|
|
79
|
-
|
|
80
78
|
req.setTimeout = function () {
|
|
81
79
|
customRequestTimeout = true
|
|
82
|
-
return
|
|
80
|
+
return setTimeout.apply(this, arguments)
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
req.on('socket', socket => {
|
|
86
|
-
if (socket) {
|
|
87
|
-
const socketSetTimeout = socket.setTimeout
|
|
88
|
-
socket.setTimeout = function () {
|
|
89
|
-
customRequestTimeout = true
|
|
90
|
-
return socketSetTimeout.apply(this, arguments)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
83
|
req.emit = function (eventName, arg) {
|
|
96
84
|
switch (eventName) {
|
|
97
85
|
case 'response': {
|
|
@@ -15,14 +15,17 @@ const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finis
|
|
|
15
15
|
|
|
16
16
|
const requestFinishedSet = new WeakSet()
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const httpNames = ['http', 'node:http']
|
|
19
|
+
const httpsNames = ['https', 'node:https']
|
|
20
|
+
|
|
21
|
+
addHook({ name: httpNames }, http => {
|
|
22
|
+
shimmer.wrap(http.ServerResponse.prototype, 'emit', wrapResponseEmit)
|
|
20
23
|
shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
|
|
21
24
|
return http
|
|
22
25
|
})
|
|
23
26
|
|
|
24
|
-
addHook({ name:
|
|
25
|
-
|
|
27
|
+
addHook({ name: httpsNames }, http => {
|
|
28
|
+
// http.ServerResponse not present on https
|
|
26
29
|
shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
|
|
27
30
|
return http
|
|
28
31
|
})
|
|
@@ -10,6 +10,8 @@ const asyncStartChannel = channel('apm:http2:client:request:asyncStart')
|
|
|
10
10
|
const asyncEndChannel = channel('apm:http2:client:request:asyncEnd')
|
|
11
11
|
const errorChannel = channel('apm:http2:client:request:error')
|
|
12
12
|
|
|
13
|
+
const names = ['http2', 'node:http2']
|
|
14
|
+
|
|
13
15
|
function createWrapEmit (ctx) {
|
|
14
16
|
return function wrapEmit (emit) {
|
|
15
17
|
return function (event, arg1) {
|
|
@@ -66,7 +68,7 @@ function wrapConnect (connect) {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
addHook({ name:
|
|
71
|
+
addHook({ name: names }, http2 => {
|
|
70
72
|
shimmer.wrap(http2, 'connect', wrapConnect)
|
|
71
73
|
|
|
72
74
|
return http2
|
|
@@ -14,7 +14,9 @@ const startServerCh = channel('apm:http2:server:request:start')
|
|
|
14
14
|
const errorServerCh = channel('apm:http2:server:request:error')
|
|
15
15
|
const finishServerCh = channel('apm:http2:server:request:finish')
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const names = ['http2', 'node:http2']
|
|
18
|
+
|
|
19
|
+
addHook({ name: names }, http2 => {
|
|
18
20
|
shimmer.wrap(http2, 'createSecureServer', wrapCreateServer)
|
|
19
21
|
shimmer.wrap(http2, 'createServer', wrapCreateServer)
|
|
20
22
|
return http2
|
|
@@ -403,7 +403,7 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
|
|
|
403
403
|
const coverageFiles = getCoveredFilenamesFromCoverage(environment.global.__coverage__)
|
|
404
404
|
.map(filename => getTestSuitePath(filename, environment.rootDir))
|
|
405
405
|
asyncResource.runInAsyncScope(() => {
|
|
406
|
-
testSuiteCodeCoverageCh.publish(
|
|
406
|
+
testSuiteCodeCoverageCh.publish({ coverageFiles, testSuite: environment.testSuite })
|
|
407
407
|
})
|
|
408
408
|
}
|
|
409
409
|
testSuiteFinishCh.publish({ status, errorMessage })
|
|
@@ -17,8 +17,16 @@ const errorTCPCh = channel('apm:net:tcp:error')
|
|
|
17
17
|
|
|
18
18
|
const connectionCh = channel(`apm:net:tcp:connection`)
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const names = ['net', 'node:net']
|
|
21
|
+
|
|
22
|
+
addHook({ name: names }, (net, version, name) => {
|
|
23
|
+
// explicitly require dns so that net gets an instrumented instance
|
|
24
|
+
// so that we don't miss the dns calls
|
|
25
|
+
if (name === 'net') {
|
|
26
|
+
require('dns')
|
|
27
|
+
} else {
|
|
28
|
+
require('node:dns')
|
|
29
|
+
}
|
|
22
30
|
|
|
23
31
|
shimmer.wrap(net.Socket.prototype, 'connect', connect => function () {
|
|
24
32
|
if (!startICPCh.hasSubscribers || !startTCPCh.hasSubscribers) {
|
|
@@ -65,7 +65,7 @@ function wrapRenderToHTML (renderToHTML) {
|
|
|
65
65
|
|
|
66
66
|
function wrapRenderErrorToHTML (renderErrorToHTML) {
|
|
67
67
|
return function (err, req, res, pathname, query) {
|
|
68
|
-
return instrument(req, res, () => renderErrorToHTML.apply(this, arguments))
|
|
68
|
+
return instrument(req, res, err, () => renderErrorToHTML.apply(this, arguments))
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -76,8 +76,8 @@ function wrapRenderToResponse (renderToResponse) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function wrapRenderErrorToResponse (renderErrorToResponse) {
|
|
79
|
-
return function (ctx) {
|
|
80
|
-
return instrument(ctx.req, ctx.res, () => renderErrorToResponse.apply(this, arguments))
|
|
79
|
+
return function (ctx, err) {
|
|
80
|
+
return instrument(ctx.req, ctx.res, err, () => renderErrorToResponse.apply(this, arguments))
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -111,13 +111,23 @@ function getPageFromPath (page, dynamicRoutes = []) {
|
|
|
111
111
|
return getPagePath(page)
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
function instrument (req, res, handler) {
|
|
114
|
+
function instrument (req, res, error, handler) {
|
|
115
|
+
if (typeof error === 'function') {
|
|
116
|
+
handler = error
|
|
117
|
+
error = null
|
|
118
|
+
}
|
|
119
|
+
|
|
115
120
|
req = req.originalRequest || req
|
|
116
121
|
res = res.originalResponse || res
|
|
117
122
|
|
|
118
123
|
// TODO support middleware properly in the future?
|
|
119
124
|
const isMiddleware = req.headers[MIDDLEWARE_HEADER]
|
|
120
|
-
if (isMiddleware || requests.has(req))
|
|
125
|
+
if (isMiddleware || requests.has(req)) {
|
|
126
|
+
if (error) {
|
|
127
|
+
errorChannel.publish({ error })
|
|
128
|
+
}
|
|
129
|
+
return handler()
|
|
130
|
+
}
|
|
121
131
|
|
|
122
132
|
requests.add(req)
|
|
123
133
|
|
|
@@ -22,7 +22,7 @@ const dispatchReceiveCh = channel('apm:rhea:receive:dispatch')
|
|
|
22
22
|
const errorReceiveCh = channel('apm:rhea:receive:error')
|
|
23
23
|
const finishReceiveCh = channel('apm:rhea:receive:finish')
|
|
24
24
|
|
|
25
|
-
const contexts = new WeakMap()
|
|
25
|
+
const contexts = new WeakMap() // key: delivery Fn, val: context
|
|
26
26
|
|
|
27
27
|
addHook({ name: 'rhea', versions: ['>=1'] }, rhea => {
|
|
28
28
|
shimmer.wrap(rhea.message, 'encode', encode => function (msg) {
|
|
@@ -52,7 +52,8 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
|
|
|
52
52
|
startSendCh.publish({ targetAddress, host, port, msg })
|
|
53
53
|
const delivery = send.apply(this, arguments)
|
|
54
54
|
const context = {
|
|
55
|
-
asyncResource
|
|
55
|
+
asyncResource,
|
|
56
|
+
connection: this.connection
|
|
56
57
|
}
|
|
57
58
|
contexts.set(delivery, context)
|
|
58
59
|
|
|
@@ -80,7 +81,8 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
|
|
|
80
81
|
|
|
81
82
|
if (msgObj.delivery) {
|
|
82
83
|
const context = {
|
|
83
|
-
asyncResource
|
|
84
|
+
asyncResource,
|
|
85
|
+
connection: this.connection
|
|
84
86
|
}
|
|
85
87
|
contexts.set(msgObj.delivery, context)
|
|
86
88
|
msgObj.delivery.update = wrapDeliveryUpdate(msgObj.delivery, msgObj.delivery.update)
|
|
@@ -114,7 +116,7 @@ addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/connection.js' }, Connecti
|
|
|
114
116
|
|
|
115
117
|
asyncResource.runInAsyncScope(() => {
|
|
116
118
|
errorReceiveCh.publish(error)
|
|
117
|
-
beforeFinish(delivery, null)
|
|
119
|
+
exports.beforeFinish(delivery, null)
|
|
118
120
|
finishReceiveCh.publish()
|
|
119
121
|
})
|
|
120
122
|
})
|
|
@@ -187,7 +189,7 @@ function patchCircularBuffer (proto, Session) {
|
|
|
187
189
|
const state = remoteState && remoteState.constructor
|
|
188
190
|
? entry.remote_state.constructor.composite_type : undefined
|
|
189
191
|
asyncResource.runInAsyncScope(() => {
|
|
190
|
-
beforeFinish(entry, state)
|
|
192
|
+
exports.beforeFinish(entry, state)
|
|
191
193
|
finishSendCh.publish()
|
|
192
194
|
})
|
|
193
195
|
}
|
|
@@ -217,13 +219,13 @@ function addToInFlightDeliveries (connection, delivery) {
|
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
function beforeFinish (delivery, state) {
|
|
220
|
-
const
|
|
221
|
-
if (
|
|
222
|
+
const context = contexts.get(delivery)
|
|
223
|
+
if (context) {
|
|
222
224
|
if (state) {
|
|
223
225
|
dispatchReceiveCh.publish({ state })
|
|
224
226
|
}
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
+
if (context.connection && context.connection[inFlightDeliveries]) {
|
|
228
|
+
context.connection[inFlightDeliveries].delete(delivery)
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
}
|
|
@@ -238,3 +240,7 @@ function getStateFromData (stateData) {
|
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
}
|
|
243
|
+
|
|
244
|
+
module.exports.inFlightDeliveries = inFlightDeliveries
|
|
245
|
+
module.exports.beforeFinish = beforeFinish
|
|
246
|
+
module.exports.contexts = contexts
|