dd-trace 6.9.0 → 6.10.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/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +42 -0
- package/package.json +4 -3
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -8
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +169 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +5 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +550 -39
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-vitest/src/index.js +8 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -0
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/manager.js +5 -1
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +1 -62
- package/packages/dd-trace/src/plugins/util/test.js +84 -5
- package/packages/dd-trace/src/plugins/util/web.js +53 -2
package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js
CHANGED
|
@@ -172,4 +172,85 @@ module.exports = [
|
|
|
172
172
|
},
|
|
173
173
|
channelName: 'testFrameworkFnWrapper',
|
|
174
174
|
},
|
|
175
|
+
{
|
|
176
|
+
module: {
|
|
177
|
+
name: '@wdio/utils',
|
|
178
|
+
versionRange: '>=9.0.0',
|
|
179
|
+
filePath: 'build/index.js',
|
|
180
|
+
},
|
|
181
|
+
functionQuery: {
|
|
182
|
+
functionName: 'executeAsync',
|
|
183
|
+
kind: 'Async',
|
|
184
|
+
},
|
|
185
|
+
channelName: 'executeAsync',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
module: {
|
|
189
|
+
name: '@wdio/utils',
|
|
190
|
+
versionRange: '>=9.0.0',
|
|
191
|
+
filePath: 'build/index.js',
|
|
192
|
+
},
|
|
193
|
+
astQuery: 'FunctionDeclaration[id.name="executeAsync"] CatchClause ' +
|
|
194
|
+
'IfStatement[test.operator=">"][test.left.object.name="retries"]' +
|
|
195
|
+
'[test.left.property.name="limit"][test.right.object.name="retries"]' +
|
|
196
|
+
'[test.right.property.name="attempts"]',
|
|
197
|
+
channelName: 'executeAsync',
|
|
198
|
+
transform: 'awaitContextCallback',
|
|
199
|
+
transformOptions: {
|
|
200
|
+
callbackArgumentNames: ['err'],
|
|
201
|
+
callbackName: 'retryCallback',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
module: {
|
|
206
|
+
name: 'jasmine-core',
|
|
207
|
+
versionRange: '>=5.0.0 <6.0.0',
|
|
208
|
+
filePath: 'lib/jasmine-core/jasmine.js',
|
|
209
|
+
},
|
|
210
|
+
astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
|
|
211
|
+
'[left.object.property.name="prototype"][left.property.name="execute"] > FunctionExpression',
|
|
212
|
+
functionQuery: {
|
|
213
|
+
kind: 'Sync',
|
|
214
|
+
},
|
|
215
|
+
channelName: 'Spec_execute',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
module: {
|
|
219
|
+
name: 'jasmine-core',
|
|
220
|
+
versionRange: '>=5.0.0 <6.0.0',
|
|
221
|
+
filePath: 'lib/jasmine-core/jasmine.js',
|
|
222
|
+
},
|
|
223
|
+
astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
|
|
224
|
+
'[left.object.property.name="prototype"][left.property.name="status"] > FunctionExpression',
|
|
225
|
+
functionQuery: {
|
|
226
|
+
kind: 'Sync',
|
|
227
|
+
},
|
|
228
|
+
channelName: 'Spec_attemptDone',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
module: {
|
|
232
|
+
name: 'jasmine-core',
|
|
233
|
+
versionRange: '>=5.0.0 <6.0.0',
|
|
234
|
+
filePath: 'lib/jasmine-core/jasmine.js',
|
|
235
|
+
},
|
|
236
|
+
functionQuery: {
|
|
237
|
+
className: 'Spec',
|
|
238
|
+
methodName: 'executionFinished',
|
|
239
|
+
kind: 'Sync',
|
|
240
|
+
},
|
|
241
|
+
channelName: 'Spec_attemptDone',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
module: {
|
|
245
|
+
name: 'jasmine-core',
|
|
246
|
+
versionRange: '>=5.0.0 <6.0.0',
|
|
247
|
+
filePath: 'lib/jasmine-core/jasmine.js',
|
|
248
|
+
},
|
|
249
|
+
functionQuery: {
|
|
250
|
+
className: 'TreeRunner',
|
|
251
|
+
methodName: '_executeSpec',
|
|
252
|
+
kind: 'Sync',
|
|
253
|
+
},
|
|
254
|
+
channelName: 'Spec_execute',
|
|
255
|
+
},
|
|
175
256
|
]
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"graphql/utilities/index.mjs": "graphql",
|
|
55
55
|
"graphql/validation/validate.js": "graphql",
|
|
56
56
|
"graphql/validation/validate.mjs": "graphql",
|
|
57
|
+
"jasmine-core/lib/jasmine-core/jasmine.js": "jasmine-core",
|
|
57
58
|
"mercurius/index.js": "mercurius",
|
|
58
59
|
"playwright-core/lib/coreBundle.js": "playwright-core",
|
|
59
60
|
"playwright/lib/index.js": "playwright",
|
|
@@ -14,7 +14,89 @@ const clone = require('../../../../../vendor/dist/rfdc')({ proto: false, circles
|
|
|
14
14
|
|
|
15
15
|
const { parse, query } = require('./compiler')
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const functionTypes = new Set(['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression'])
|
|
18
|
+
const identifierPattern = /^[$A-Z_a-z][$\w]*$/
|
|
19
|
+
|
|
20
|
+
module.exports = { awaitContextCallback, waitForAsyncEnd }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Awaits an optional context callback before continuing through a matched conditional branch.
|
|
24
|
+
*
|
|
25
|
+
* The branch condition is checked again after the callback settles so the
|
|
26
|
+
* original body does not run against state that changed while awaiting.
|
|
27
|
+
*
|
|
28
|
+
* @param {{
|
|
29
|
+
* transformOptions?: {
|
|
30
|
+
* callbackArgumentNames?: string[],
|
|
31
|
+
* callbackName?: string
|
|
32
|
+
* }
|
|
33
|
+
* }} state
|
|
34
|
+
* @param {import('estree').IfStatement} node
|
|
35
|
+
* @param {import('estree').Node} _parent
|
|
36
|
+
* @param {import('estree').Node[]} ancestry
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
function awaitContextCallback (state, node, _parent, ancestry) {
|
|
40
|
+
assert(node.type === 'IfStatement' && node.consequent?.type === 'BlockStatement',
|
|
41
|
+
'awaitContextCallback: expected an if statement with a block body')
|
|
42
|
+
|
|
43
|
+
const { callbackArgumentNames = [], callbackName } = state.transformOptions ?? {}
|
|
44
|
+
|
|
45
|
+
assert(identifierPattern.test(callbackName), 'awaitContextCallback: callbackName must be an identifier')
|
|
46
|
+
assert(callbackArgumentNames.every(name => identifierPattern.test(name)),
|
|
47
|
+
'awaitContextCallback: callbackArgumentNames must be identifiers')
|
|
48
|
+
|
|
49
|
+
let enclosingFunction
|
|
50
|
+
let hasTraceWrapper = false
|
|
51
|
+
for (const ancestor of ancestry) {
|
|
52
|
+
if (!functionTypes.has(ancestor.type)) continue
|
|
53
|
+
|
|
54
|
+
enclosingFunction ??= ancestor
|
|
55
|
+
if (ancestor.body?.type !== 'BlockStatement') continue
|
|
56
|
+
|
|
57
|
+
let hasContextBinding = false
|
|
58
|
+
let hasTracedBinding = false
|
|
59
|
+
for (const statement of ancestor.body.body) {
|
|
60
|
+
if (statement.type !== 'VariableDeclaration') continue
|
|
61
|
+
|
|
62
|
+
for (const declaration of statement.declarations) {
|
|
63
|
+
hasContextBinding ||= declaration.id?.name === '__apm$ctx'
|
|
64
|
+
hasTracedBinding ||= declaration.id?.name === '__apm$traced'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
hasTraceWrapper ||= hasContextBinding && hasTracedBinding
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
assert(enclosingFunction?.async, 'awaitContextCallback: expected an enclosing async function')
|
|
71
|
+
assert(hasTraceWrapper, 'awaitContextCallback: expected an enclosing trace wrapper')
|
|
72
|
+
|
|
73
|
+
const callbackVariable = `__apm$${callbackName}`
|
|
74
|
+
if (query(node, `[id.name="${callbackVariable}"]`).length > 0) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const originalStatements = node.consequent.body
|
|
79
|
+
const callbackStatements = parse(`
|
|
80
|
+
async function wrapper () {
|
|
81
|
+
const ${callbackVariable} = __apm$ctx.${callbackName};
|
|
82
|
+
if (typeof ${callbackVariable} === 'function') {
|
|
83
|
+
try {
|
|
84
|
+
await ${callbackVariable}(${callbackArgumentNames.join(', ')});
|
|
85
|
+
} catch {}
|
|
86
|
+
if (true) {}
|
|
87
|
+
} else {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`).body[0].body.body
|
|
91
|
+
|
|
92
|
+
const callbackBranch = callbackStatements[1]
|
|
93
|
+
const recheckedBranch = callbackBranch.consequent.body[1]
|
|
94
|
+
recheckedBranch.test = clone(node.test)
|
|
95
|
+
recheckedBranch.consequent.body.push(...clone(originalStatements))
|
|
96
|
+
recheckedBranch.alternate = clone(node.alternate)
|
|
97
|
+
callbackBranch.alternate.body.push(...originalStatements)
|
|
98
|
+
node.consequent.body = callbackStatements
|
|
99
|
+
}
|
|
18
100
|
|
|
19
101
|
/**
|
|
20
102
|
* Injects settlement-specific asyncEnd waits into a generated `tracePromise`
|
|
@@ -49,7 +49,7 @@ const {
|
|
|
49
49
|
getRawJestTestName,
|
|
50
50
|
getJestSuitesToRun,
|
|
51
51
|
removeSeedSuffixFromTestName,
|
|
52
|
-
} = require('../../
|
|
52
|
+
} = require('../../dd-trace/src/plugins/util/jest')
|
|
53
53
|
const {
|
|
54
54
|
addCoverageBackfillUntestedFiles,
|
|
55
55
|
getCoverageBackfillFiles,
|
|
@@ -10,7 +10,6 @@ const {
|
|
|
10
10
|
} = require('../helpers/channel')
|
|
11
11
|
const { addHook, channel } = require('../helpers/instrument')
|
|
12
12
|
const shimmer = require('../../../datadog-shimmer')
|
|
13
|
-
const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
|
|
14
13
|
const { EMPTY_EFD_RETRY_POLICY } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
15
14
|
const { writeCoverageBackfillToCache } = require('../../../dd-trace/src/ci-visibility/test-optimization-cache')
|
|
16
15
|
const log = require('../../../dd-trace/src/log')
|
|
@@ -33,6 +32,7 @@ const {
|
|
|
33
32
|
TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
|
|
34
33
|
getTestOptimizationRequestResults,
|
|
35
34
|
isModifiedTest,
|
|
35
|
+
isMarkedAsUnskippable,
|
|
36
36
|
} = require('../../../dd-trace/src/plugins/util/test')
|
|
37
37
|
|
|
38
38
|
const {
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
const {
|
|
12
12
|
getTestSuitePath,
|
|
13
13
|
DYNAMIC_NAME_RE,
|
|
14
|
+
getFailedTestReplayPromise,
|
|
14
15
|
recordTestManagementExecution,
|
|
15
16
|
recordAttemptToFixExecution,
|
|
16
17
|
logAttemptToFixTestExecution,
|
|
@@ -953,14 +954,7 @@ function getOnTestRetryHandler (config) {
|
|
|
953
954
|
promises,
|
|
954
955
|
...ctx.currentStore,
|
|
955
956
|
})
|
|
956
|
-
|
|
957
|
-
test._ddFailedTestReplayPromise = Promise.all([
|
|
958
|
-
promises.setProbePromise,
|
|
959
|
-
promises.finishTestPromise,
|
|
960
|
-
]).then(() => {})
|
|
961
|
-
} else if (promises.setProbePromise || promises.finishTestPromise) {
|
|
962
|
-
test._ddFailedTestReplayPromise = promises.setProbePromise || promises.finishTestPromise
|
|
963
|
-
}
|
|
957
|
+
test._ddFailedTestReplayPromise = getFailedTestReplayPromise(promises)
|
|
964
958
|
}
|
|
965
959
|
const key = getTestToContextKey(test)
|
|
966
960
|
testToContext.delete(key)
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
4
|
const { channel, addHook } = require('./helpers/instrument')
|
|
5
|
+
const {
|
|
6
|
+
dispatchesAcquireSynchronously,
|
|
7
|
+
takePoolWaitTime,
|
|
8
|
+
wrapPoolClusterQueryMethod,
|
|
9
|
+
wrapPoolGetConnection,
|
|
10
|
+
wrapPoolQueryMethod,
|
|
11
|
+
} = require('./helpers/pool-acquire')
|
|
5
12
|
|
|
6
13
|
addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connection => {
|
|
7
14
|
const startCh = channel('apm:mysql:query:start')
|
|
@@ -17,6 +24,11 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
|
|
|
17
24
|
const conf = this.config
|
|
18
25
|
const ctx = { sql, conf }
|
|
19
26
|
|
|
27
|
+
const waitTime = takePoolWaitTime(this)
|
|
28
|
+
if (waitTime !== undefined) {
|
|
29
|
+
ctx.poolWaitTime = waitTime
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
return startCh.runStores(ctx, () => {
|
|
21
33
|
if (args[0].sql) {
|
|
22
34
|
args[0].sql = ctx.sql
|
|
@@ -61,20 +73,24 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
61
73
|
const connectionFinishCh = channel('apm:mysql:connection:finish')
|
|
62
74
|
const startPoolQueryCh = channel('datadog:mysql:pool:query:start')
|
|
63
75
|
const finishPoolQueryCh = channel('datadog:mysql:pool:query:finish')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const acquireStartCh = channel('apm:mysql:pool:acquire:start')
|
|
77
|
+
const acquireFinishCh = channel('apm:mysql:pool:acquire:finish')
|
|
78
|
+
const deferredPoolAcquire = !dispatchesAcquireSynchronously(
|
|
79
|
+
Pool.prototype.query,
|
|
80
|
+
Object.assign(Object.create(Pool.prototype), { config: { connectionConfig: {} } }),
|
|
81
|
+
'getConnection',
|
|
82
|
+
['SELECT 1', () => {}]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
shimmer.wrap(Pool.prototype, 'getConnection', getConnection => wrapPoolGetConnection(getConnection, {
|
|
86
|
+
connectionStartCh,
|
|
87
|
+
connectionFinishCh,
|
|
88
|
+
acquireStartCh,
|
|
89
|
+
acquireFinishCh,
|
|
90
|
+
reentersQueuedCallbacks: true,
|
|
91
|
+
}, deferredPoolAcquire))
|
|
92
|
+
|
|
93
|
+
shimmer.wrap(Pool.prototype, 'query', query => wrapPoolQueryMethod(function (...args) {
|
|
78
94
|
if (!startPoolQueryCh.hasSubscribers) {
|
|
79
95
|
return query.apply(this, args)
|
|
80
96
|
}
|
|
@@ -99,7 +115,21 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
99
115
|
|
|
100
116
|
return retval
|
|
101
117
|
})
|
|
102
|
-
})
|
|
118
|
+
}, connectionStartCh, deferredPoolAcquire))
|
|
103
119
|
|
|
104
120
|
return Pool
|
|
105
121
|
})
|
|
122
|
+
|
|
123
|
+
// A `canRetry` failover re-invokes `query` with the same Query object. Key the acquire state by that
|
|
124
|
+
// object so a retry remains part of the query even if a future mysql version defers it.
|
|
125
|
+
addHook({ name: 'mysql', file: 'lib/PoolNamespace.js', versions: ['>=2'] }, PoolNamespace => {
|
|
126
|
+
const channels = {
|
|
127
|
+
connectionStartCh: channel('apm:mysql:connection:start'),
|
|
128
|
+
connectionFinishCh: channel('apm:mysql:connection:finish'),
|
|
129
|
+
acquireStartCh: channel('apm:mysql:pool:acquire:start'),
|
|
130
|
+
acquireFinishCh: channel('apm:mysql:pool:acquire:finish'),
|
|
131
|
+
}
|
|
132
|
+
shimmer.wrap(PoolNamespace.prototype, 'query', query => wrapPoolClusterQueryMethod(query, channels))
|
|
133
|
+
|
|
134
|
+
return PoolNamespace
|
|
135
|
+
})
|
|
@@ -5,10 +5,55 @@ const { errorMonitor } = require('node:events')
|
|
|
5
5
|
const shimmer = require('../../datadog-shimmer')
|
|
6
6
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
7
7
|
const { channel, addHook } = require('./helpers/instrument')
|
|
8
|
+
const {
|
|
9
|
+
dispatchesAcquireSynchronously,
|
|
10
|
+
takePoolWaitTime,
|
|
11
|
+
wrapPoolClusterGetConnection,
|
|
12
|
+
wrapPoolClusterMethod,
|
|
13
|
+
wrapPoolGetConnection,
|
|
14
|
+
wrapPoolQueryMethod,
|
|
15
|
+
} = require('./helpers/pool-acquire')
|
|
8
16
|
|
|
9
17
|
/** @type {WeakMap<object, Function>} */
|
|
10
18
|
const wrappedOnResult = new WeakMap()
|
|
11
19
|
|
|
20
|
+
/** @type {WeakSet<object>} */
|
|
21
|
+
const flagWrappedNamespaces = new WeakSet()
|
|
22
|
+
|
|
23
|
+
const connectionStartCh = channel('apm:mysql2:connection:start')
|
|
24
|
+
const connectionFinishCh = channel('apm:mysql2:connection:finish')
|
|
25
|
+
const acquireStartCh = channel('apm:mysql2:pool:acquire:start')
|
|
26
|
+
const acquireFinishCh = channel('apm:mysql2:pool:acquire:finish')
|
|
27
|
+
const poolAcquireChannels = {
|
|
28
|
+
connectionStartCh,
|
|
29
|
+
connectionFinishCh,
|
|
30
|
+
acquireStartCh,
|
|
31
|
+
acquireFinishCh,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Bracket a pool-cluster namespace's internal acquires and key retry state by its stable callback.
|
|
36
|
+
* @param {{ query?: Function, execute?: Function, getConnection?: Function }} poolNamespace
|
|
37
|
+
*/
|
|
38
|
+
function flagWrapNamespace (poolNamespace) {
|
|
39
|
+
if (poolNamespace == null || flagWrappedNamespaces.has(poolNamespace)) return
|
|
40
|
+
flagWrappedNamespaces.add(poolNamespace)
|
|
41
|
+
|
|
42
|
+
if (typeof poolNamespace.query === 'function') {
|
|
43
|
+
shimmer.wrap(poolNamespace, 'query', query => wrapPoolClusterMethod(query, connectionStartCh))
|
|
44
|
+
}
|
|
45
|
+
if (typeof poolNamespace.execute === 'function') {
|
|
46
|
+
shimmer.wrap(poolNamespace, 'execute', execute => wrapPoolClusterMethod(execute, connectionStartCh))
|
|
47
|
+
}
|
|
48
|
+
if (typeof poolNamespace.getConnection === 'function') {
|
|
49
|
+
shimmer.wrap(
|
|
50
|
+
poolNamespace,
|
|
51
|
+
'getConnection',
|
|
52
|
+
getConnection => wrapPoolClusterGetConnection(getConnection, poolAcquireChannels)
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
12
57
|
/**
|
|
13
58
|
* @param {unknown} sql
|
|
14
59
|
* @returns {string|undefined}
|
|
@@ -43,6 +88,11 @@ function wrapConnection (Connection, version) {
|
|
|
43
88
|
const ctx = {}
|
|
44
89
|
|
|
45
90
|
if (isQuery) {
|
|
91
|
+
const waitTime = takePoolWaitTime(this)
|
|
92
|
+
if (waitTime !== undefined) {
|
|
93
|
+
ctx.poolWaitTime = waitTime
|
|
94
|
+
}
|
|
95
|
+
|
|
46
96
|
command.execute = wrapExecute(command, command.execute, ctx, this.config)
|
|
47
97
|
|
|
48
98
|
return commandAddCh.runStores(ctx, addCommand, this, ...arguments)
|
|
@@ -212,22 +262,41 @@ function wrapConnection (Connection, version) {
|
|
|
212
262
|
* by the queued query attach to the caller rather than the previous query that freed the connection.
|
|
213
263
|
*
|
|
214
264
|
* @param {Function} Pool
|
|
265
|
+
* @param {boolean} deferredPoolAcquire
|
|
215
266
|
* @returns {Function}
|
|
216
267
|
*/
|
|
217
|
-
function wrapGetConnection (Pool) {
|
|
218
|
-
|
|
219
|
-
|
|
268
|
+
function wrapGetConnection (Pool, deferredPoolAcquire) {
|
|
269
|
+
shimmer.wrap(
|
|
270
|
+
Pool.prototype,
|
|
271
|
+
'getConnection',
|
|
272
|
+
getConnection => wrapPoolGetConnection(getConnection, poolAcquireChannels, deferredPoolAcquire)
|
|
273
|
+
)
|
|
220
274
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
arguments[0] = function (...args) {
|
|
224
|
-
return connectionFinishCh.runStores(ctx, cb, this, ...args)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
connectionStartCh.publish(ctx)
|
|
275
|
+
return Pool
|
|
276
|
+
}
|
|
228
277
|
|
|
229
|
-
|
|
230
|
-
|
|
278
|
+
/**
|
|
279
|
+
* mysql2 >=3.11.5 keeps the outer-query abort at the connection level, so the pool only needs the
|
|
280
|
+
* acquire wrap plus the pool-query flag around `query` / `execute`.
|
|
281
|
+
*
|
|
282
|
+
* @param {Function} Pool
|
|
283
|
+
* @returns {Function}
|
|
284
|
+
*/
|
|
285
|
+
function wrapBasePool (Pool) {
|
|
286
|
+
const deferredQueryAcquire = defersPoolAcquire(Pool, 'query')
|
|
287
|
+
const deferredExecuteAcquire = defersPoolAcquire(Pool, 'execute')
|
|
288
|
+
wrapGetConnection(Pool, deferredQueryAcquire || deferredExecuteAcquire)
|
|
289
|
+
|
|
290
|
+
shimmer.wrap(
|
|
291
|
+
Pool.prototype,
|
|
292
|
+
'query',
|
|
293
|
+
query => wrapPoolQueryMethod(query, connectionStartCh, deferredQueryAcquire)
|
|
294
|
+
)
|
|
295
|
+
shimmer.wrap(
|
|
296
|
+
Pool.prototype,
|
|
297
|
+
'execute',
|
|
298
|
+
execute => wrapPoolQueryMethod(execute, connectionStartCh, deferredExecuteAcquire)
|
|
299
|
+
)
|
|
231
300
|
|
|
232
301
|
return Pool
|
|
233
302
|
}
|
|
@@ -240,10 +309,12 @@ function wrapGetConnection (Pool) {
|
|
|
240
309
|
function wrapPool (Pool, version) {
|
|
241
310
|
const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
|
|
242
311
|
const shouldEmitEndAfterQueryAbort = satisfies(version, '>=1.3.3')
|
|
312
|
+
const deferredQueryAcquire = defersPoolAcquire(Pool, 'query')
|
|
313
|
+
const deferredExecuteAcquire = defersPoolAcquire(Pool, 'execute')
|
|
243
314
|
|
|
244
|
-
wrapGetConnection(Pool)
|
|
315
|
+
wrapGetConnection(Pool, deferredQueryAcquire || deferredExecuteAcquire)
|
|
245
316
|
|
|
246
|
-
shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
|
|
317
|
+
shimmer.wrap(Pool.prototype, 'query', query => wrapPoolQueryMethod(function (sql, values, cb) {
|
|
247
318
|
if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
|
|
248
319
|
|
|
249
320
|
const resolvedSql = resolveSqlString(sql)
|
|
@@ -279,9 +350,9 @@ function wrapPool (Pool, version) {
|
|
|
279
350
|
}
|
|
280
351
|
|
|
281
352
|
return query.apply(this, arguments)
|
|
282
|
-
})
|
|
353
|
+
}, connectionStartCh, deferredQueryAcquire))
|
|
283
354
|
|
|
284
|
-
shimmer.wrap(Pool.prototype, 'execute', execute => function (sql, values, cb) {
|
|
355
|
+
shimmer.wrap(Pool.prototype, 'execute', execute => wrapPoolQueryMethod(function (sql, values, cb) {
|
|
285
356
|
if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
|
|
286
357
|
|
|
287
358
|
const resolvedSql = resolveSqlString(sql)
|
|
@@ -304,11 +375,25 @@ function wrapPool (Pool, version) {
|
|
|
304
375
|
}
|
|
305
376
|
|
|
306
377
|
return execute.apply(this, arguments)
|
|
307
|
-
})
|
|
378
|
+
}, connectionStartCh, deferredExecuteAcquire))
|
|
308
379
|
|
|
309
380
|
return Pool
|
|
310
381
|
}
|
|
311
382
|
|
|
383
|
+
/**
|
|
384
|
+
* @param {Function} Pool
|
|
385
|
+
* @param {'query'|'execute'} method
|
|
386
|
+
* @returns {boolean}
|
|
387
|
+
*/
|
|
388
|
+
function defersPoolAcquire (Pool, method) {
|
|
389
|
+
return !dispatchesAcquireSynchronously(
|
|
390
|
+
Pool.prototype[method],
|
|
391
|
+
Object.assign(Object.create(Pool.prototype), { config: { connectionConfig: {} } }),
|
|
392
|
+
'getConnection',
|
|
393
|
+
['SELECT 1', () => {}]
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
312
397
|
/**
|
|
313
398
|
* @param {Function} PoolCluster
|
|
314
399
|
* @returns {Function}
|
|
@@ -319,6 +404,7 @@ function wrapPoolCluster (PoolCluster) {
|
|
|
319
404
|
|
|
320
405
|
shimmer.wrap(PoolCluster.prototype, 'of', of => function (...args) {
|
|
321
406
|
const poolNamespace = of.apply(this, args)
|
|
407
|
+
flagWrapNamespace(poolNamespace)
|
|
322
408
|
|
|
323
409
|
if (startOuterQueryCh.hasSubscribers && !wrappedPoolNamespaces.has(poolNamespace)) {
|
|
324
410
|
shimmer.wrap(poolNamespace, 'query', query => function (sql, values, cb) {
|
|
@@ -404,7 +490,7 @@ addHook(
|
|
|
404
490
|
// mysql2 >=3.11.5 moved the pool onto BasePool in lib/base/pool.js.
|
|
405
491
|
addHook(
|
|
406
492
|
{ name: 'mysql2', file: 'lib/base/pool.js', versions: ['>=3.11.5'] },
|
|
407
|
-
/** @type {(moduleExports: unknown, version: string) => unknown} */ (
|
|
493
|
+
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapBasePool)
|
|
408
494
|
)
|
|
409
495
|
|
|
410
496
|
// PoolNamespace.prototype.query does not exist in mysql2<2.3.0
|
|
@@ -412,3 +498,18 @@ addHook(
|
|
|
412
498
|
{ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] },
|
|
413
499
|
/** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapPoolCluster)
|
|
414
500
|
)
|
|
501
|
+
|
|
502
|
+
// mysql2 >=3.11.5 keeps the outer-query abort at the connection level, so a cluster namespace only
|
|
503
|
+
// needs the pool-query flag so its internal query/execute acquire does not open an acquire span.
|
|
504
|
+
addHook(
|
|
505
|
+
{ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=3.11.5'] },
|
|
506
|
+
/** @type {(moduleExports: unknown, version: string) => unknown} */ (PoolCluster => {
|
|
507
|
+
shimmer.wrap(PoolCluster.prototype, 'of', of => function () {
|
|
508
|
+
const poolNamespace = of.apply(this, arguments)
|
|
509
|
+
flagWrapNamespace(poolNamespace)
|
|
510
|
+
return poolNamespace
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
return PoolCluster
|
|
514
|
+
})
|
|
515
|
+
)
|
|
@@ -5,6 +5,12 @@ const { addHook } = require('./helpers/instrument')
|
|
|
5
5
|
/** @type {((pattern: string | RegExp) => RegExp | undefined) | undefined} */
|
|
6
6
|
let compileToRegexp
|
|
7
7
|
|
|
8
|
+
/** @type {((pattern: string) => { tokens: object[] } | undefined) | undefined} */
|
|
9
|
+
let parseTokens
|
|
10
|
+
|
|
11
|
+
/** @type {((route: string) => ((url: string) => object | undefined) | undefined) | undefined} */
|
|
12
|
+
let makeMatcher
|
|
13
|
+
|
|
8
14
|
addHook({ name: 'path-to-regexp', versions: ['*'] }, moduleExports => {
|
|
9
15
|
// 0.1.x and 6.x: `module.exports = (path, ...) => RegExp`.
|
|
10
16
|
// 7.x: `module.exports = { pathToRegexp(path, ...) => RegExp }`.
|
|
@@ -29,6 +35,47 @@ addHook({ name: 'path-to-regexp', versions: ['*'] }, moduleExports => {
|
|
|
29
35
|
return moduleExports
|
|
30
36
|
})
|
|
31
37
|
|
|
38
|
+
// 8.x only: earlier majors also expose `parse`/`match`, but with incompatible shapes — 7.x returns
|
|
39
|
+
// `{ tokens }` too, yet its tokens are bare strings rather than typed nodes. Gating on the version
|
|
40
|
+
// keeps those from installing adapters the consumers would misread.
|
|
41
|
+
addHook({ name: 'path-to-regexp', versions: ['>=8'] }, moduleExports => {
|
|
42
|
+
const { parse, match } = moduleExports
|
|
43
|
+
|
|
44
|
+
if (typeof parse === 'function' && typeof match === 'function') {
|
|
45
|
+
parseTokens = pattern => {
|
|
46
|
+
let result
|
|
47
|
+
try {
|
|
48
|
+
result = parse(pattern)
|
|
49
|
+
} catch {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(result?.tokens)) return result
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
makeMatcher = route => {
|
|
56
|
+
let matcher
|
|
57
|
+
try {
|
|
58
|
+
// Callers read which params matched, never their values: skipping decode is faster and
|
|
59
|
+
// stops a malformed escape ('%ZZ') from throwing URIError, which would read as "no match".
|
|
60
|
+
matcher = match(route, { decode: false })
|
|
61
|
+
} catch {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
return url => {
|
|
65
|
+
let result
|
|
66
|
+
try {
|
|
67
|
+
result = matcher(url)
|
|
68
|
+
} catch {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
return result ? result.params : undefined
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return moduleExports
|
|
77
|
+
})
|
|
78
|
+
|
|
32
79
|
/**
|
|
33
80
|
* Returns whatever path-to-regexp compile adapter the host most recently
|
|
34
81
|
* loaded. Capture this once at addHook fire time so each express/router
|
|
@@ -41,4 +88,23 @@ function getCompileToRegexp () {
|
|
|
41
88
|
return compileToRegexp
|
|
42
89
|
}
|
|
43
90
|
|
|
44
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Returns the host's path-to-regexp `parse` adapter (8.x token tree), or `undefined` when the
|
|
93
|
+
* host has not loaded an 8.x path-to-regexp (e.g. Express 4 ships 0.1.x, which has no `parse`).
|
|
94
|
+
* @returns {((pattern: string) => { tokens: object[] } | undefined) | undefined}
|
|
95
|
+
*/
|
|
96
|
+
function getParse () {
|
|
97
|
+
return parseTokens
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the host's path-to-regexp 8.x `match` adapter: a factory `route => (url => params)` that
|
|
102
|
+
* compiles a route once and returns a matcher yielding the captured params (or undefined on no
|
|
103
|
+
* match / unusable route). `undefined` when the host has not loaded an 8.x path-to-regexp.
|
|
104
|
+
* @returns {((route: string) => ((url: string) => object | undefined) | undefined) | undefined}
|
|
105
|
+
*/
|
|
106
|
+
function getMatch () {
|
|
107
|
+
return makeMatcher
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = { getCompileToRegexp, getParse, getMatch }
|