dd-trace 5.86.0 → 5.88.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/LICENSE-3rdparty.csv +60 -32
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +1 -0
- package/index.d.ts +243 -7
- package/package.json +9 -6
- package/packages/datadog-instrumentations/src/ai.js +54 -90
- package/packages/datadog-instrumentations/src/cucumber.js +14 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +55 -14
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +15 -13
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +21 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +138 -12
- package/packages/datadog-instrumentations/src/http/client.js +119 -1
- package/packages/datadog-instrumentations/src/jest.js +179 -15
- package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
- package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
- package/packages/datadog-instrumentations/src/mysql2.js +131 -64
- package/packages/datadog-instrumentations/src/playwright.js +9 -1
- package/packages/datadog-instrumentations/src/stripe.js +92 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -0
- package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
- package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
- package/packages/datadog-plugin-azure-functions/src/index.js +53 -37
- package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
- package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
- package/packages/datadog-plugin-cucumber/src/index.js +9 -6
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +33 -0
- package/packages/datadog-plugin-cypress/src/support.js +48 -8
- package/packages/datadog-plugin-jest/src/index.js +12 -2
- package/packages/datadog-plugin-jest/src/util.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
- package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
- package/packages/datadog-plugin-mocha/src/index.js +9 -6
- package/packages/datadog-plugin-playwright/src/index.js +10 -6
- package/packages/datadog-plugin-vitest/src/index.js +13 -8
- package/packages/dd-trace/src/appsec/addresses.js +11 -0
- package/packages/dd-trace/src/appsec/channels.js +5 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +302 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
- package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
- package/packages/dd-trace/src/appsec/index.js +103 -0
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +66 -4
- package/packages/dd-trace/src/azure_metadata.js +0 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +14 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +148 -195
- package/packages/dd-trace/src/config/helper.js +43 -1
- package/packages/dd-trace/src/config/index.js +42 -14
- package/packages/dd-trace/src/config/supported-configurations.json +4115 -510
- package/packages/dd-trace/src/constants.js +0 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
- package/packages/dd-trace/src/datastreams/pathway.js +22 -3
- package/packages/dd-trace/src/datastreams/processor.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -2
- package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -23
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +23 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +168 -36
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +18 -0
- package/packages/dd-trace/src/encode/agentless-json.js +141 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/writer.js +22 -8
- package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
- package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
- package/packages/dd-trace/src/exporters/common/agents.js +1 -1
- package/packages/dd-trace/src/exporters/common/request.js +4 -4
- package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
- package/packages/dd-trace/src/llmobs/sdk.js +34 -5
- package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
- package/packages/dd-trace/src/opentracing/span.js +6 -4
- package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
- package/packages/dd-trace/src/plugins/database.js +57 -45
- package/packages/dd-trace/src/plugins/outbound.js +27 -2
- package/packages/dd-trace/src/plugins/tracing.js +39 -4
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +7 -0
- package/packages/dd-trace/src/plugins/util/test.js +48 -0
- package/packages/dd-trace/src/plugins/util/web.js +8 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
- package/packages/dd-trace/src/propagation-hash/index.js +145 -0
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/startup-log.js +3 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
- package/packages/dd-trace/src/plugins/util/serverless.js +0 -8
- package/packages/dd-trace/src/scope/noop/scope.js +0 -21
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = [
|
|
4
|
+
{
|
|
5
|
+
module: {
|
|
6
|
+
name: 'bullmq',
|
|
7
|
+
versionRange: '>=5.66.0',
|
|
8
|
+
filePath: 'dist/cjs/classes/queue.js',
|
|
9
|
+
},
|
|
10
|
+
functionQuery: {
|
|
11
|
+
methodName: 'add',
|
|
12
|
+
className: 'Queue',
|
|
13
|
+
kind: 'Async',
|
|
14
|
+
},
|
|
15
|
+
channelName: 'Queue_add',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
module: {
|
|
19
|
+
name: 'bullmq',
|
|
20
|
+
versionRange: '>=5.66.0',
|
|
21
|
+
filePath: 'dist/cjs/classes/queue.js',
|
|
22
|
+
},
|
|
23
|
+
functionQuery: {
|
|
24
|
+
methodName: 'addBulk',
|
|
25
|
+
className: 'Queue',
|
|
26
|
+
kind: 'Async',
|
|
27
|
+
},
|
|
28
|
+
channelName: 'Queue_addBulk',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
module: {
|
|
32
|
+
name: 'bullmq',
|
|
33
|
+
versionRange: '>=5.66.0',
|
|
34
|
+
filePath: 'dist/cjs/classes/worker.js',
|
|
35
|
+
},
|
|
36
|
+
functionQuery: {
|
|
37
|
+
methodName: 'callProcessJob',
|
|
38
|
+
className: 'Worker',
|
|
39
|
+
kind: 'Async',
|
|
40
|
+
},
|
|
41
|
+
channelName: 'Worker_callProcessJob',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
module: {
|
|
45
|
+
name: 'bullmq',
|
|
46
|
+
versionRange: '>=5.66.0',
|
|
47
|
+
filePath: 'dist/cjs/classes/flow-producer.js',
|
|
48
|
+
},
|
|
49
|
+
functionQuery: {
|
|
50
|
+
methodName: 'add',
|
|
51
|
+
className: 'FlowProducer',
|
|
52
|
+
kind: 'Async',
|
|
53
|
+
},
|
|
54
|
+
channelName: 'FlowProducer_add',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
module: {
|
|
58
|
+
name: 'bullmq',
|
|
59
|
+
versionRange: '>=5.66.0',
|
|
60
|
+
filePath: 'dist/esm/classes/queue.js',
|
|
61
|
+
},
|
|
62
|
+
functionQuery: {
|
|
63
|
+
methodName: 'add',
|
|
64
|
+
className: 'Queue',
|
|
65
|
+
kind: 'Async',
|
|
66
|
+
},
|
|
67
|
+
channelName: 'Queue_add',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
module: {
|
|
71
|
+
name: 'bullmq',
|
|
72
|
+
versionRange: '>=5.66.0',
|
|
73
|
+
filePath: 'dist/esm/classes/queue.js',
|
|
74
|
+
},
|
|
75
|
+
functionQuery: {
|
|
76
|
+
methodName: 'addBulk',
|
|
77
|
+
className: 'Queue',
|
|
78
|
+
kind: 'Async',
|
|
79
|
+
},
|
|
80
|
+
channelName: 'Queue_addBulk',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
module: {
|
|
84
|
+
name: 'bullmq',
|
|
85
|
+
versionRange: '>=5.66.0',
|
|
86
|
+
filePath: 'dist/esm/classes/worker.js',
|
|
87
|
+
},
|
|
88
|
+
functionQuery: {
|
|
89
|
+
methodName: 'callProcessJob',
|
|
90
|
+
className: 'Worker',
|
|
91
|
+
kind: 'Async',
|
|
92
|
+
},
|
|
93
|
+
channelName: 'Worker_callProcessJob',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
module: {
|
|
97
|
+
name: 'bullmq',
|
|
98
|
+
versionRange: '>=5.66.0',
|
|
99
|
+
filePath: 'dist/esm/classes/flow-producer.js',
|
|
100
|
+
},
|
|
101
|
+
functionQuery: {
|
|
102
|
+
methodName: 'add',
|
|
103
|
+
className: 'FlowProducer',
|
|
104
|
+
kind: 'Async',
|
|
105
|
+
},
|
|
106
|
+
channelName: 'FlowProducer_add',
|
|
107
|
+
},
|
|
108
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const transforms = require('./transforms')
|
|
4
|
+
|
|
5
|
+
function transform (state, ...args) {
|
|
6
|
+
const operator = state.operator = getOperator(state)
|
|
7
|
+
|
|
8
|
+
transforms[operator](state, ...args)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getOperator ({ functionQuery: { kind } }) {
|
|
12
|
+
switch (kind) {
|
|
13
|
+
case 'Async': return 'tracePromise'
|
|
14
|
+
case 'AsyncIterator': return 'traceAsyncIterator'
|
|
15
|
+
case 'Callback': return 'traceCallback'
|
|
16
|
+
case 'Iterator': return 'traceIterator'
|
|
17
|
+
case 'Sync': return 'traceSync'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { transform }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { parse, query } = require('./compiler')
|
|
3
|
+
const { parse, query, traverse } = require('./compiler')
|
|
4
4
|
|
|
5
5
|
const tracingChannelPredicate = (node) => (
|
|
6
6
|
node.specifiers?.[0]?.local?.name === 'tr_ch_apm_tracingChannel' ||
|
|
@@ -8,15 +8,15 @@ const tracingChannelPredicate = (node) => (
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
const transforms = module.exports = {
|
|
11
|
-
tracingChannelImport ({
|
|
11
|
+
tracingChannelImport ({ sourceType }, node) {
|
|
12
12
|
if (node.body.some(tracingChannelPredicate)) return
|
|
13
13
|
|
|
14
14
|
const index = node.body.findIndex(child => child.directive === 'use strict')
|
|
15
|
-
const code =
|
|
15
|
+
const code = sourceType === 'module'
|
|
16
16
|
? 'import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel"'
|
|
17
17
|
: 'const {tracingChannel: tr_ch_apm_tracingChannel} = require("diagnostics_channel")'
|
|
18
18
|
|
|
19
|
-
node.body.splice(index + 1, 0, parse(code, {
|
|
19
|
+
node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
tracingChannelDeclaration (state, node) {
|
|
@@ -35,7 +35,9 @@ const transforms = module.exports = {
|
|
|
35
35
|
node.body.splice(index + 1, 0, parse(code).body[0])
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
+
traceAsyncIterator: traceAny,
|
|
38
39
|
traceCallback: traceAny,
|
|
40
|
+
traceIterator: traceAny,
|
|
39
41
|
tracePromise: traceAny,
|
|
40
42
|
traceSync: traceAny,
|
|
41
43
|
}
|
|
@@ -51,18 +53,25 @@ function traceAny (state, node, _parent, ancestry) {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
function traceFunction (state, node, program) {
|
|
54
|
-
const { operator } = state
|
|
55
|
-
|
|
56
56
|
transforms.tracingChannelDeclaration(state, program)
|
|
57
57
|
|
|
58
58
|
node.body = wrap(state, {
|
|
59
|
-
type: '
|
|
59
|
+
type: 'FunctionExpression',
|
|
60
60
|
params: node.params,
|
|
61
61
|
body: node.body,
|
|
62
|
-
async:
|
|
62
|
+
async: node.async,
|
|
63
63
|
expression: false,
|
|
64
|
-
generator:
|
|
65
|
-
})
|
|
64
|
+
generator: node.generator,
|
|
65
|
+
}, program)
|
|
66
|
+
|
|
67
|
+
// The original function no longer contains any calls to `await` or `yield` as
|
|
68
|
+
// the function body is copied to the internal wrapped function, so we set
|
|
69
|
+
// these to false to avoid altering the return value of the wrapper. The old
|
|
70
|
+
// values are instead copied to the new AST node above.
|
|
71
|
+
node.generator = false
|
|
72
|
+
node.async = false
|
|
73
|
+
|
|
74
|
+
wrapSuper(state, node)
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
function traceInstanceMethod (state, node, program) {
|
|
@@ -100,15 +109,19 @@ function traceInstanceMethod (state, node, program) {
|
|
|
100
109
|
const fn = ctorBody[1].expression.right
|
|
101
110
|
|
|
102
111
|
fn.async = operator === 'tracePromise'
|
|
103
|
-
fn.body = wrap(state, { type: 'Identifier', name: `__apm$${methodName}` })
|
|
112
|
+
fn.body = wrap(state, { type: 'Identifier', name: `__apm$${methodName}` }, program)
|
|
113
|
+
|
|
114
|
+
wrapSuper(state, fn)
|
|
104
115
|
|
|
105
116
|
ctor.value.body.body.push(...ctorBody)
|
|
106
117
|
}
|
|
107
118
|
|
|
108
|
-
function wrap (state, node) {
|
|
119
|
+
function wrap (state, node, program) {
|
|
109
120
|
const { channelName, operator } = state
|
|
110
121
|
|
|
122
|
+
if (operator === 'traceAsyncIterator') return wrapIterator(state, node, program)
|
|
111
123
|
if (operator === 'traceCallback') return wrapCallback(state, node)
|
|
124
|
+
if (operator === 'traceIterator') return wrapIterator(state, node, program)
|
|
112
125
|
|
|
113
126
|
const async = operator === 'tracePromise' ? 'async' : ''
|
|
114
127
|
const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
|
|
@@ -133,6 +146,55 @@ function wrap (state, node) {
|
|
|
133
146
|
return wrapper
|
|
134
147
|
}
|
|
135
148
|
|
|
149
|
+
function wrapSuper (_state, node) {
|
|
150
|
+
const members = new Set()
|
|
151
|
+
|
|
152
|
+
traverse(
|
|
153
|
+
node.body,
|
|
154
|
+
'[object.type=Super]',
|
|
155
|
+
(node, parent) => {
|
|
156
|
+
const { name } = node.property
|
|
157
|
+
|
|
158
|
+
let child
|
|
159
|
+
|
|
160
|
+
if (parent.callee) {
|
|
161
|
+
// This is needed because for generator functions we have to move the
|
|
162
|
+
// original function to a nested wrapped function, but we can't use an
|
|
163
|
+
// arrow function because arrow function cannot be generator functions,
|
|
164
|
+
// and `super` cannot be called from a nested function, so we have to
|
|
165
|
+
// rewrite any `super` call to not use the keyword.
|
|
166
|
+
const { expression } = parse(`__apm$super['${name}'].call(this)`).body[0]
|
|
167
|
+
|
|
168
|
+
parent.callee = child = expression.callee
|
|
169
|
+
parent.arguments.unshift(...expression.arguments)
|
|
170
|
+
} else {
|
|
171
|
+
parent.expression = child = parse(`__apm$super['${name}']`).body[0]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
child.computed = parent.callee.computed
|
|
175
|
+
child.optional = parent.callee.optional
|
|
176
|
+
|
|
177
|
+
members.add(name)
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
for (const name of members) {
|
|
182
|
+
const member = parse(`
|
|
183
|
+
class Wrapper {
|
|
184
|
+
wrapper () {
|
|
185
|
+
__apm$super['${name}'] = super['${name}']
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
`).body[0].body.body[0].value.body.body[0]
|
|
189
|
+
|
|
190
|
+
node.body.body.unshift(member)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (members.size > 0) {
|
|
194
|
+
node.body.body.unshift(parse('const __apm$super = {}').body[0])
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
136
198
|
function wrapCallback (state, node) {
|
|
137
199
|
const { channelName, functionQuery: { index = -1 } } = state
|
|
138
200
|
const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
|
|
@@ -194,3 +256,67 @@ function wrapCallback (state, node) {
|
|
|
194
256
|
|
|
195
257
|
return wrapper
|
|
196
258
|
}
|
|
259
|
+
|
|
260
|
+
function wrapIterator (state, node, program) {
|
|
261
|
+
const { channelName, operator } = state
|
|
262
|
+
const baseChannel = channelName.replaceAll(':', '_')
|
|
263
|
+
const channelVariable = 'tr_ch_apm$' + baseChannel
|
|
264
|
+
const nextChannel = baseChannel + '_next'
|
|
265
|
+
const traceMethod = operator === 'traceAsyncIterator' ? 'tracePromise' : 'traceSync'
|
|
266
|
+
const traceNext = `tr_ch_apm$${nextChannel}.${traceMethod}`
|
|
267
|
+
|
|
268
|
+
transforms.tracingChannelDeclaration({ ...state, channelName: nextChannel }, program)
|
|
269
|
+
|
|
270
|
+
const wrapper = parse(`
|
|
271
|
+
function wrapper () {
|
|
272
|
+
const __apm$traced = () => {
|
|
273
|
+
const __apm$wrapped = () => {};
|
|
274
|
+
return __apm$wrapped.apply(this, arguments);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
if (!${channelVariable}.start.hasSubscribers) return __apm$traced();
|
|
278
|
+
|
|
279
|
+
{
|
|
280
|
+
const wrap = iter => {
|
|
281
|
+
const { next: iterNext, return: iterReturn, throw: iterThrow } = iter;
|
|
282
|
+
|
|
283
|
+
iter.next = (...args) => ${traceNext}(iterNext, ctx, iter, ...args);
|
|
284
|
+
iter.return = (...args) => ${traceNext}(iterReturn, ctx, iter, ...args);
|
|
285
|
+
iter.throw = (...args) => ${traceNext}(iterThrow, ctx, iter, ...args);
|
|
286
|
+
|
|
287
|
+
return iter;
|
|
288
|
+
};
|
|
289
|
+
const ctx = {
|
|
290
|
+
arguments,
|
|
291
|
+
self: this,
|
|
292
|
+
moduleVersion: "1.0.0"
|
|
293
|
+
};
|
|
294
|
+
const iter = ${channelVariable}.traceSync(__apm$traced, ctx);
|
|
295
|
+
|
|
296
|
+
if (typeof iter.then !== 'function') return wrap(iter);
|
|
297
|
+
|
|
298
|
+
return iter.then(result => {
|
|
299
|
+
ctx.result = result;
|
|
300
|
+
|
|
301
|
+
${channelVariable}.asyncStart.publish(ctx);
|
|
302
|
+
${channelVariable}.asyncEnd.publish(ctx);
|
|
303
|
+
|
|
304
|
+
return wrap(result);
|
|
305
|
+
}, err => {
|
|
306
|
+
ctx.error = err;
|
|
307
|
+
|
|
308
|
+
${channelVariable}.error.publish(ctx);
|
|
309
|
+
${channelVariable}.asyncStart.publish(ctx);
|
|
310
|
+
${channelVariable}.asyncEnd.publish(ctx);
|
|
311
|
+
|
|
312
|
+
return Promise.reject(err);
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
`).body[0].body // Extract only block statement of function body.
|
|
317
|
+
|
|
318
|
+
// Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
|
|
319
|
+
query(wrapper, '[id.name=__apm$wrapped]')[0].init = node
|
|
320
|
+
|
|
321
|
+
return wrapper
|
|
322
|
+
}
|
|
@@ -14,6 +14,7 @@ const finishChannel = channel('apm:http:client:request:finish')
|
|
|
14
14
|
const endChannel = channel('apm:http:client:request:end')
|
|
15
15
|
const asyncStartChannel = channel('apm:http:client:request:asyncStart')
|
|
16
16
|
const errorChannel = channel('apm:http:client:request:error')
|
|
17
|
+
const responseFinishChannel = channel('apm:http:client:response:finish')
|
|
17
18
|
|
|
18
19
|
const names = ['http', 'https', 'node:http', 'node:https']
|
|
19
20
|
|
|
@@ -39,6 +40,112 @@ function normalizeCallback (inputOptions, callback, inputURL) {
|
|
|
39
40
|
return typeof inputOptions === 'function' ? [inputOptions, inputURL || {}] : [callback, inputOptions]
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Wires the downstream response so we can observe when the customer consumes
|
|
45
|
+
* the body and when the stream finishes
|
|
46
|
+
*
|
|
47
|
+
* @param {object} ctx - Instrumentation context
|
|
48
|
+
* @param {import('http').IncomingMessage} res - The downstream response object.
|
|
49
|
+
* @returns {{ finalizeIfNeeded: () => void }|null} Cleanup helper used for drain.
|
|
50
|
+
*/
|
|
51
|
+
function setupResponseInstrumentation (ctx, res) {
|
|
52
|
+
const shouldInstrumentFinish = responseFinishChannel.hasSubscribers
|
|
53
|
+
|
|
54
|
+
if (!shouldInstrumentFinish) {
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let bodyConsumed = false
|
|
59
|
+
let finishCalled = false
|
|
60
|
+
let originalRead = null
|
|
61
|
+
let dataListenerAdded = false
|
|
62
|
+
let dataReadStarted = false
|
|
63
|
+
|
|
64
|
+
const { shouldCollectBody } = ctx
|
|
65
|
+
const bodyChunks = shouldCollectBody ? [] : null
|
|
66
|
+
|
|
67
|
+
const collectChunk = chunk => {
|
|
68
|
+
if (!shouldCollectBody || !chunk) return
|
|
69
|
+
|
|
70
|
+
if (typeof chunk === 'string') {
|
|
71
|
+
bodyChunks.push(chunk)
|
|
72
|
+
} else if (Buffer.isBuffer(chunk)) {
|
|
73
|
+
bodyChunks.push(chunk)
|
|
74
|
+
} else {
|
|
75
|
+
// Handle Uint8Array or other array-like types
|
|
76
|
+
bodyChunks.push(Buffer.from(chunk))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Listen for body consumption
|
|
81
|
+
const onNewListener = (eventName) => {
|
|
82
|
+
if (eventName === 'data' || eventName === 'readable') {
|
|
83
|
+
bodyConsumed = true
|
|
84
|
+
|
|
85
|
+
// For 'data' events, add our own listener to collect chunks
|
|
86
|
+
if (eventName === 'data' && !dataListenerAdded && !dataReadStarted) {
|
|
87
|
+
dataListenerAdded = true
|
|
88
|
+
res.on('data', collectChunk)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// For 'readable' events, wrap the read() method
|
|
92
|
+
if (eventName === 'readable' && !originalRead && !dataListenerAdded && typeof res.read === 'function') {
|
|
93
|
+
originalRead = res.read
|
|
94
|
+
res.read = function () {
|
|
95
|
+
const chunk = originalRead.apply(this, arguments)
|
|
96
|
+
if (!dataListenerAdded) {
|
|
97
|
+
dataReadStarted = true
|
|
98
|
+
collectChunk(chunk)
|
|
99
|
+
}
|
|
100
|
+
return chunk
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
res.on('newListener', onNewListener)
|
|
107
|
+
|
|
108
|
+
// Cleanup function to restore original behavior
|
|
109
|
+
const cleanup = () => {
|
|
110
|
+
res.off('newListener', onNewListener)
|
|
111
|
+
res.off('data', collectChunk)
|
|
112
|
+
|
|
113
|
+
if (originalRead) {
|
|
114
|
+
res.read = originalRead
|
|
115
|
+
originalRead = null
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const notifyFinish = () => {
|
|
120
|
+
if (finishCalled) return
|
|
121
|
+
finishCalled = true
|
|
122
|
+
|
|
123
|
+
// Combine collected chunks into a single body
|
|
124
|
+
let body = null
|
|
125
|
+
if (bodyChunks?.length) {
|
|
126
|
+
const firstChunk = bodyChunks[0]
|
|
127
|
+
body = typeof firstChunk === 'string'
|
|
128
|
+
? bodyChunks.join('')
|
|
129
|
+
: Buffer.concat(bodyChunks)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
responseFinishChannel.publish({ ctx, res, body })
|
|
133
|
+
cleanup()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
res.once('end', notifyFinish)
|
|
137
|
+
res.once('close', notifyFinish)
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
finalizeIfNeeded () {
|
|
141
|
+
if (!bodyConsumed) {
|
|
142
|
+
// Body not consumed, resume to complete the response
|
|
143
|
+
notifyFinish()
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
42
149
|
function patch (http, methodName) {
|
|
43
150
|
shimmer.wrap(http, methodName, instrumentRequest)
|
|
44
151
|
|
|
@@ -103,7 +210,18 @@ function patch (http, methodName) {
|
|
|
103
210
|
ctx.res = res
|
|
104
211
|
res.once('end', finish)
|
|
105
212
|
res.once(errorMonitor, finish)
|
|
106
|
-
|
|
213
|
+
|
|
214
|
+
const instrumentation = setupResponseInstrumentation(ctx, res)
|
|
215
|
+
|
|
216
|
+
if (!instrumentation) {
|
|
217
|
+
break
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const result = emit.apply(this, arguments)
|
|
221
|
+
|
|
222
|
+
instrumentation.finalizeIfNeeded()
|
|
223
|
+
|
|
224
|
+
return result
|
|
107
225
|
}
|
|
108
226
|
case 'connect':
|
|
109
227
|
case 'upgrade':
|