dd-trace 3.12.1 → 3.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/ci/init.js +1 -0
  2. package/index.d.ts +50 -0
  3. package/package.json +1 -1
  4. package/packages/datadog-instrumentations/src/fs.js +358 -0
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  6. package/packages/datadog-instrumentations/src/jest.js +11 -1
  7. package/packages/datadog-instrumentations/src/mocha.js +3 -2
  8. package/packages/datadog-instrumentations/src/mysql.js +7 -1
  9. package/packages/datadog-instrumentations/src/mysql2.js +7 -1
  10. package/packages/datadog-instrumentations/src/playwright.js +236 -0
  11. package/packages/datadog-plugin-fs/src/index.js +45 -0
  12. package/packages/datadog-plugin-jest/src/index.js +45 -23
  13. package/packages/datadog-plugin-mocha/src/index.js +34 -6
  14. package/packages/datadog-plugin-mysql/src/index.js +8 -7
  15. package/packages/datadog-plugin-playwright/src/index.js +171 -0
  16. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  17. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  18. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +60 -0
  19. package/packages/dd-trace/src/appsec/index.js +1 -1
  20. package/packages/dd-trace/src/appsec/recommended.json +247 -112
  21. package/packages/dd-trace/src/appsec/sdk/index.js +23 -0
  22. package/packages/dd-trace/src/appsec/sdk/noop.js +11 -0
  23. package/packages/dd-trace/src/appsec/sdk/track_event.js +74 -0
  24. package/packages/dd-trace/src/appsec/sdk/utils.js +10 -0
  25. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
  26. package/packages/dd-trace/src/config.js +7 -0
  27. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -4
  28. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +52 -37
  29. package/packages/dd-trace/src/log/channels.js +47 -0
  30. package/packages/dd-trace/src/log/index.js +79 -0
  31. package/packages/dd-trace/src/log/writer.js +108 -0
  32. package/packages/dd-trace/src/noop/proxy.js +3 -0
  33. package/packages/dd-trace/src/plugins/index.js +1 -0
  34. package/packages/dd-trace/src/plugins/util/ci.js +13 -21
  35. package/packages/dd-trace/src/{appsec → plugins/util}/ip_blocklist.js +0 -0
  36. package/packages/dd-trace/src/{appsec → plugins/util}/ip_extractor.js +1 -1
  37. package/packages/dd-trace/src/plugins/util/test.js +27 -10
  38. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -7
  39. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  40. package/packages/dd-trace/src/proxy.js +2 -0
  41. package/packages/dd-trace/src/startup-log.js +1 -1
  42. package/scripts/check-proposal-labels.js +71 -0
  43. package/packages/dd-trace/src/log.js +0 -143
package/ci/init.js CHANGED
@@ -34,6 +34,7 @@ so dd-trace will not be initialized.`)
34
34
 
35
35
  if (shouldInit) {
36
36
  tracer.init(options)
37
+ tracer.use('fs', false)
37
38
  }
38
39
 
39
40
  module.exports = tracer
package/index.d.ts CHANGED
@@ -115,6 +115,8 @@ export declare interface Tracer extends opentracing.Tracer {
115
115
  * @returns {Tracer} The Tracer instance for chaining.
116
116
  */
117
117
  setUser (user: User): Tracer;
118
+
119
+ appsec: Appsec;
118
120
  }
119
121
 
120
122
  export declare interface TraceOptions extends Analyzable {
@@ -537,6 +539,17 @@ export declare interface TracerOptions {
537
539
  */
538
540
  pollInterval?: number,
539
541
  }
542
+
543
+ /**
544
+ * Whether to enable client IP collection from relevant IP headers
545
+ * @default false
546
+ */
547
+ clientIpEnabled?: boolean
548
+
549
+ /**
550
+ * Custom header name to source the http.client_ip tag from.
551
+ */
552
+ clientIpHeader?: string,
540
553
  }
541
554
 
542
555
  /**
@@ -582,6 +595,36 @@ export declare interface User {
582
595
  [key: string]: string | undefined
583
596
  }
584
597
 
598
+ export declare interface Appsec {
599
+ /**
600
+ * Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
601
+ * @param {User} user Properties of the authenticated user. Accepts custom fields.
602
+ * @param {[key: string]: string} metadata Custom fields to link to the login success event.
603
+ *
604
+ * @beta This method is in beta and could change in future versions.
605
+ */
606
+ trackUserLoginSuccessEvent(user: User, metadata?: { [key: string]: string }): void
607
+
608
+ /**
609
+ * Links a failed login event to the current trace.
610
+ * @param {string} userId The user id of the attemped login.
611
+ * @param {boolean} exists If the user id exists.
612
+ * @param {[key: string]: string} metadata Custom fields to link to the login failure event.
613
+ *
614
+ * @beta This method is in beta and could change in future versions.
615
+ */
616
+ trackUserLoginFailureEvent(userId: string, exists: boolean, metadata?: { [key: string]: string }): void
617
+
618
+ /**
619
+ * Links a custom event to the current trace.
620
+ * @param {string} eventName The name of the event.
621
+ * @param {[key: string]: string} metadata Custom fields to link to the event.
622
+ *
623
+ * @beta This method is in beta and could change in future versions.
624
+ */
625
+ trackCustomEvent(eventName: string, metadata?: { [key: string]: string }): void
626
+ }
627
+
585
628
  /** @hidden */
586
629
  declare type anyObject = {
587
630
  [key: string]: any;
@@ -669,6 +712,7 @@ interface Plugins {
669
712
  "opensearch": plugins.opensearch;
670
713
  "oracledb": plugins.oracledb;
671
714
  "paperplane": plugins.paperplane;
715
+ "playwright": plugins.playwright;
672
716
  "pg": plugins.pg;
673
717
  "pino": plugins.pino;
674
718
  "redis": plugins.redis;
@@ -1358,6 +1402,12 @@ declare namespace plugins {
1358
1402
  */
1359
1403
  interface paperplane extends HttpServer {}
1360
1404
 
1405
+ /**
1406
+ * This plugin automatically instruments the
1407
+ * [playwright](https://github.com/microsoft/playwright) module.
1408
+ */
1409
+ interface playwright extends Integration {}
1410
+
1361
1411
  /**
1362
1412
  * This plugin automatically instruments the
1363
1413
  * [pg](https://node-postgres.com/) module.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "3.12.1",
3
+ "version": "3.13.1",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -0,0 +1,358 @@
1
+
2
+ 'use strict'
3
+
4
+ const {
5
+ channel,
6
+ addHook,
7
+ AsyncResource
8
+ } = require('./helpers/instrument')
9
+ const shimmer = require('../../datadog-shimmer')
10
+
11
+ const startChannel = channel('apm:fs:operation:start')
12
+ const finishChannel = channel('apm:fs:operation:finish')
13
+ const errorChannel = channel('apm:fs:operation:error')
14
+ const ddFhSym = Symbol('ddFileHandle')
15
+ let kHandle, kDirReadPromisified, kDirClosePromisified
16
+
17
+ const paramsByMethod = {
18
+ access: ['path', 'mode'],
19
+ appendFile: ['path', 'data', 'options'],
20
+ chmod: ['path', 'mode'],
21
+ chown: ['path', 'uid', 'gid'],
22
+ close: ['fd'],
23
+ copyFile: ['src', 'dest', 'mode'],
24
+ cp: ['src', 'dest', 'options'],
25
+ exists: ['path'],
26
+ fchmod: ['fd', 'mode'],
27
+ fchown: ['fd', 'uid', 'gid'],
28
+ fdatasync: ['fd'],
29
+ fstat: ['fd', 'options'],
30
+ fsync: ['fd'],
31
+ ftruncate: ['fd', 'len'],
32
+ futimes: ['fd', 'atime', 'mtime'],
33
+ lchmod: ['path', 'mode'],
34
+ lchown: ['path', 'uid', 'gid'],
35
+ link: ['existingPath', 'newPath'],
36
+ lstat: ['path', 'options'],
37
+ lutimes: ['path', 'atime', 'mtime'],
38
+ mkdir: ['path', 'options'],
39
+ mkdtemp: ['prefix', 'options'],
40
+ open: ['path', 'flag', 'mode'],
41
+ opendir: ['path', 'options'],
42
+ read: ['fd'],
43
+ readdir: ['path', 'options'],
44
+ readFile: ['path', 'options'],
45
+ readlink: ['path', 'options'],
46
+ readv: ['fd'],
47
+ realpath: ['path', 'options'],
48
+ rename: ['oldPath', 'newPath'],
49
+ rmdir: ['path', 'options'],
50
+ rm: ['path', 'options'],
51
+ stat: ['path', 'options'],
52
+ symlink: ['target', 'path', 'type'],
53
+ truncate: ['path', 'len'],
54
+ unlink: ['path'],
55
+ utimes: ['path', 'atime', 'mtime'],
56
+ write: ['fd'],
57
+ writeFile: ['file', 'data', 'options'],
58
+ writev: ['fd']
59
+ }
60
+
61
+ const watchMethods = {
62
+ unwatchFile: ['path', 'listener'],
63
+ watch: ['path', 'options', 'listener'],
64
+ watchFile: ['path', 'options', 'listener']
65
+ }
66
+
67
+ const paramsByFileHandleMethods = {
68
+ appendFile: ['data', 'options'],
69
+ chmod: ['mode'],
70
+ chown: ['uid', 'gid'],
71
+ close: [],
72
+ createReadStream: ['options'],
73
+ createWriteStream: ['options'],
74
+ datasync: [],
75
+ read: ['buffer', 'offset', 'length', 'position'],
76
+ readableWebStream: [],
77
+ readFile: ['options'],
78
+ readLines: ['options'],
79
+ readv: ['buffers', 'position'],
80
+ stat: ['options'],
81
+ sync: [],
82
+ truncate: ['len'],
83
+ utimes: ['atime', 'mtime'],
84
+ write: ['buffer', 'offset', 'length', 'position'],
85
+ writeFile: ['data', 'options'],
86
+ writev: ['buffers', 'position']
87
+ }
88
+ const names = ['fs', 'node:fs']
89
+ names.forEach(name => {
90
+ addHook({ name }, fs => {
91
+ const asyncMethods = Object.keys(paramsByMethod)
92
+ const syncMethods = asyncMethods.map(name => `${name}Sync`)
93
+
94
+ massWrap(fs, asyncMethods, createWrapFunction())
95
+ massWrap(fs, syncMethods, createWrapFunction())
96
+ massWrap(fs.promises, asyncMethods, createWrapFunction('promises.'))
97
+
98
+ wrap(fs.realpath, 'native', createWrapFunction('', 'realpath.native'))
99
+ wrap(fs.realpathSync, 'native', createWrapFunction('', 'realpath.native'))
100
+ wrap(fs.promises.realpath, 'native', createWrapFunction('', 'realpath.native'))
101
+
102
+ wrap(fs, 'createReadStream', wrapCreateStream)
103
+ wrap(fs, 'createWriteStream', wrapCreateStream)
104
+ if (fs.Dir) {
105
+ wrap(fs.Dir.prototype, 'close', createWrapFunction('dir.'))
106
+ wrap(fs.Dir.prototype, 'closeSync', createWrapFunction('dir.'))
107
+ wrap(fs.Dir.prototype, 'read', createWrapFunction('dir.'))
108
+ wrap(fs.Dir.prototype, 'readSync', createWrapFunction('dir.'))
109
+ wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator())
110
+ }
111
+
112
+ wrap(fs, 'unwatchFile', createWatchWrapFunction())
113
+ wrap(fs, 'watch', createWatchWrapFunction())
114
+ wrap(fs, 'watchFile', createWatchWrapFunction())
115
+
116
+ return fs
117
+ })
118
+ })
119
+ function isFirstMethodReturningFileHandle (original) {
120
+ return !kHandle && original.name === 'open'
121
+ }
122
+ function wrapFileHandle (fh) {
123
+ const fileHandlePrototype = getFileHandlePrototype(fh)
124
+ const desc = Reflect.getOwnPropertyDescriptor(fileHandlePrototype, kHandle)
125
+ if (!desc || !desc.get) {
126
+ Reflect.defineProperty(fileHandlePrototype, kHandle, {
127
+ get () {
128
+ return this[ddFhSym]
129
+ },
130
+ set (h) {
131
+ this[ddFhSym] = h
132
+ wrap(this, 'close', createWrapFunction('filehandle.'))
133
+ },
134
+ configurable: true
135
+ })
136
+ }
137
+ for (const name of Reflect.ownKeys(fileHandlePrototype)) {
138
+ if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') {
139
+ continue
140
+ }
141
+ wrap(fileHandlePrototype, name, createWrapFunction('filehandle.'))
142
+ }
143
+ }
144
+
145
+ function getFileHandlePrototype (fh) {
146
+ if (!kHandle) {
147
+ kHandle = Reflect.ownKeys(fh).find(key => typeof key === 'symbol' && key.toString().includes('kHandle'))
148
+ }
149
+ return Object.getPrototypeOf(fh)
150
+ }
151
+
152
+ function getSymbolName (sym) {
153
+ return sym.description || sym.toString()
154
+ }
155
+ function initDirAsyncIteratorProperties (iterator) {
156
+ const keys = Reflect.ownKeys(iterator)
157
+ for (const key of keys) {
158
+ if (kDirReadPromisified && kDirClosePromisified) break
159
+ if (typeof key !== 'symbol') continue
160
+ if (!kDirReadPromisified && getSymbolName(key).includes('kDirReadPromisified')) {
161
+ kDirReadPromisified = key
162
+ }
163
+ if (!kDirClosePromisified && getSymbolName(key).includes('kDirClosePromisified')) {
164
+ kDirClosePromisified = key
165
+ }
166
+ }
167
+ }
168
+
169
+ function createWrapDirAsyncIterator () {
170
+ return function wrapDirAsyncIterator (asyncIterator) {
171
+ return function wrappedAsyncIterator () {
172
+ if (!kDirReadPromisified || !kDirClosePromisified) {
173
+ initDirAsyncIteratorProperties(this)
174
+ }
175
+ wrap(this, kDirReadPromisified, createWrapFunction('dir.', 'read'))
176
+ wrap(this, kDirClosePromisified, createWrapFunction('dir.', 'close'))
177
+ return asyncIterator.apply(this, arguments)
178
+ }
179
+ }
180
+ }
181
+
182
+ function wrapCreateStream (original) {
183
+ const classes = {
184
+ createReadStream: 'ReadStream',
185
+ createWriteStream: 'WriteStream'
186
+ }
187
+ const name = classes[original.name]
188
+
189
+ return function (path, options) {
190
+ if (!startChannel.hasSubscribers) return original.apply(this, arguments)
191
+
192
+ const innerResource = new AsyncResource('bound-anonymous-fn')
193
+ const message = getMessage(name, ['path', 'options'], arguments)
194
+
195
+ return innerResource.runInAsyncScope(() => {
196
+ startChannel.publish(message)
197
+
198
+ try {
199
+ const stream = original.apply(this, arguments)
200
+ const onError = innerResource.bind(error => {
201
+ errorChannel.publish(error)
202
+ onFinish()
203
+ })
204
+ const onFinish = innerResource.bind(() => {
205
+ finishChannel.publish()
206
+ stream.off('close', onFinish)
207
+ stream.off('end', onFinish)
208
+ stream.off('finish', onFinish)
209
+ stream.off('error', onError)
210
+ })
211
+
212
+ stream.once('close', onFinish)
213
+ stream.once('end', onFinish)
214
+ stream.once('finish', onFinish)
215
+ stream.once('error', onError)
216
+
217
+ return stream
218
+ } catch (error) {
219
+ errorChannel.publish(error)
220
+ finishChannel.publish()
221
+ }
222
+ })
223
+ }
224
+ }
225
+
226
+ function getMethodParamsRelationByPrefix (prefix) {
227
+ if (prefix === 'filehandle.') {
228
+ return paramsByFileHandleMethods
229
+ }
230
+ return paramsByMethod
231
+ }
232
+
233
+ function createWatchWrapFunction (override = '') {
234
+ return function wrapFunction (original) {
235
+ const name = override || original.name
236
+ const method = name
237
+ const operation = name
238
+ return function () {
239
+ if (!startChannel.hasSubscribers) return original.apply(this, arguments)
240
+ const message = getMessage(method, watchMethods[operation], arguments, this)
241
+ const innerResource = new AsyncResource('bound-anonymous-fn')
242
+ return innerResource.runInAsyncScope(() => {
243
+ startChannel.publish(message)
244
+ try {
245
+ const result = original.apply(this, arguments)
246
+ finishChannel.publish()
247
+ return result
248
+ } catch (error) {
249
+ errorChannel.publish(error)
250
+ finishChannel.publish()
251
+ throw error
252
+ }
253
+ })
254
+ }
255
+ }
256
+ }
257
+
258
+ function createWrapFunction (prefix = '', override = '') {
259
+ return function wrapFunction (original) {
260
+ const name = override || original.name
261
+ const method = `${prefix}${name}`
262
+ const operation = name.match(/^(.+?)(Sync)?(\.native)?$/)[1]
263
+
264
+ return function () {
265
+ if (!startChannel.hasSubscribers) return original.apply(this, arguments)
266
+
267
+ const lastIndex = arguments.length - 1
268
+ const cb = typeof arguments[lastIndex] === 'function' && arguments[lastIndex]
269
+ const innerResource = new AsyncResource('bound-anonymous-fn')
270
+ const message = getMessage(method, getMethodParamsRelationByPrefix(prefix)[operation], arguments, this)
271
+
272
+ if (cb) {
273
+ const outerResource = new AsyncResource('bound-anonymous-fn')
274
+
275
+ arguments[lastIndex] = innerResource.bind(function (e) {
276
+ if (typeof e === 'object') { // fs.exists receives a boolean
277
+ errorChannel.publish(e)
278
+ }
279
+
280
+ finishChannel.publish()
281
+
282
+ return outerResource.runInAsyncScope(() => cb.apply(this, arguments))
283
+ })
284
+ }
285
+
286
+ return innerResource.runInAsyncScope(() => {
287
+ startChannel.publish(message)
288
+ try {
289
+ const result = original.apply(this, arguments)
290
+ if (cb) return result
291
+ if (result && typeof result.then === 'function') {
292
+ // TODO method open returning promise and filehandle prototype not initialized, initialize it
293
+
294
+ return result.then(
295
+ value => {
296
+ if (isFirstMethodReturningFileHandle(original)) {
297
+ wrapFileHandle(value)
298
+ }
299
+ finishChannel.publish()
300
+ return value
301
+ },
302
+ error => {
303
+ errorChannel.publish(error)
304
+ finishChannel.publish()
305
+ throw error
306
+ }
307
+ )
308
+ }
309
+
310
+ finishChannel.publish()
311
+
312
+ return result
313
+ } catch (error) {
314
+ errorChannel.publish(error)
315
+ finishChannel.publish()
316
+ throw error
317
+ }
318
+ })
319
+ }
320
+ }
321
+ }
322
+
323
+ function getMessage (operation, params, args, self) {
324
+ const metadata = {}
325
+ if (params) {
326
+ for (let i = 0; i < params.length; i++) {
327
+ if (!params[i] || typeof args[i] === 'function') continue
328
+ metadata[params[i]] = args[i]
329
+ }
330
+ }
331
+
332
+ if (self) {
333
+ // For `Dir` the path is available on `this.path`
334
+ if (self.path) {
335
+ metadata.path = self.path
336
+ }
337
+ // For FileHandle fs is available on `this.fd`
338
+ if (self.fd) {
339
+ metadata.fd = self.fd
340
+ }
341
+ }
342
+
343
+ return { operation, ...metadata }
344
+ }
345
+
346
+ function massWrap (target, methods, wrapper) {
347
+ for (const method of methods) {
348
+ wrap(target, method, wrapper)
349
+ }
350
+ }
351
+
352
+ function wrap (target, method, wrapper) {
353
+ try {
354
+ shimmer.wrap(target, method, wrapper)
355
+ } catch (e) {
356
+ // skip unavailable method
357
+ }
358
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  module.exports = {
4
4
  '@cucumber/cucumber': () => require('../cucumber'),
5
+ '@playwright/test': () => require('../playwright'),
5
6
  '@elastic/elasticsearch': () => require('../elasticsearch'),
6
7
  '@elastic/transport': () => require('../elasticsearch'),
7
8
  '@google-cloud/pubsub': () => require('../google-cloud-pubsub'),
@@ -31,6 +32,8 @@ module.exports = {
31
32
  'express': () => require('../express'),
32
33
  'fastify': () => require('../fastify'),
33
34
  'find-my-way': () => require('../find-my-way'),
35
+ 'fs': () => require('../fs'),
36
+ 'node:fs': () => require('../fs'),
34
37
  'graphql': () => require('../graphql'),
35
38
  'grpc': () => require('../grpc'),
36
39
  'hapi': () => require('../hapi'),
@@ -218,9 +218,19 @@ function cliWrapper (cli) {
218
218
 
219
219
  const isSuitesSkipped = !!skippableSuites.length
220
220
 
221
+ let testFrameworkVersion
222
+ try {
223
+ testFrameworkVersion = this.getVersion()
224
+ } catch (e) {
225
+ try {
226
+ testFrameworkVersion = this.default.getVersion()
227
+ } catch (e) {
228
+ // ignore errors
229
+ }
230
+ }
221
231
  const processArgv = process.argv.slice(2).join(' ')
222
232
  sessionAsyncResource.runInAsyncScope(() => {
223
- testSessionStartCh.publish(`jest ${processArgv}`)
233
+ testSessionStartCh.publish({ command: `jest ${processArgv}`, testFrameworkVersion })
224
234
  })
225
235
 
226
236
  const result = await runCLI.apply(this, arguments)
@@ -39,6 +39,7 @@ const testFileToSuiteAr = new Map()
39
39
  const originalCoverageMap = createCoverageMap()
40
40
 
41
41
  let suitesToSkip = []
42
+ let mochaVersion
42
43
 
43
44
  function getSuitesByTestFile (root) {
44
45
  const suitesByTestFile = {}
@@ -128,7 +129,7 @@ function mochaHook (Runner) {
128
129
  this.once('start', testRunAsyncResource.bind(function () {
129
130
  const processArgv = process.argv.slice(2).join(' ')
130
131
  const command = `mocha ${processArgv}`
131
- testSessionStartCh.publish(command)
132
+ testSessionStartCh.publish({ command, frameworkVersion: mochaVersion })
132
133
  }))
133
134
 
134
135
  this.on('suite', function (suite) {
@@ -315,12 +316,12 @@ addHook({
315
316
  file: 'lib/mocha.js'
316
317
  }, (Mocha) => {
317
318
  const mochaRunAsyncResource = new AsyncResource('bound-anonymous-fn')
318
-
319
319
  /**
320
320
  * Get ITR configuration and skippable suites
321
321
  * If ITR is disabled, `onDone` is called immediately on the subscriber
322
322
  */
323
323
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
324
+ mochaVersion = this.version
324
325
  if (!itrConfigurationCh.hasSubscribers) {
325
326
  return run.apply(this, arguments)
326
327
  }
@@ -19,13 +19,19 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
19
19
 
20
20
  const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
21
21
  const conf = this.config
22
+ const payload = { sql, conf }
22
23
 
23
24
  const callbackResource = new AsyncResource('bound-anonymous-fn')
24
25
  const asyncResource = new AsyncResource('bound-anonymous-fn')
25
26
 
26
27
  return asyncResource.runInAsyncScope(() => {
27
- startCh.publish({ sql, conf })
28
+ startCh.publish(payload)
28
29
 
30
+ if (arguments[0].sql) {
31
+ arguments[0].sql = payload.sql
32
+ } else {
33
+ arguments[0] = payload.sql
34
+ }
29
35
  try {
30
36
  const res = query.apply(this, arguments)
31
37
 
@@ -45,8 +45,14 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connec
45
45
 
46
46
  return asyncResource.bind(function executeWithTrace (packet, connection) {
47
47
  const sql = cmd.statement ? cmd.statement.query : cmd.sql
48
+ const payload = { sql, conf: config }
49
+ startCh.publish(payload)
48
50
 
49
- startCh.publish({ sql, conf: config })
51
+ if (cmd.statement) {
52
+ cmd.statement.query = payload.sql
53
+ } else {
54
+ cmd.sql = payload.sql
55
+ }
50
56
 
51
57
  if (this.onResult) {
52
58
  const onResult = callbackResource.bind(this.onResult)