@aidra/pmx-io 0.0.0-stage → 9.14.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.
Files changed (67) hide show
  1. package/README.md +69 -2
  2. package/bin/pmx-io-node.js +143 -0
  3. package/index.d.ts +381 -0
  4. package/index.js +27 -0
  5. package/index.mjs +47 -0
  6. package/lib/actions.js +120 -0
  7. package/lib/activate.js +402 -0
  8. package/lib/application.js +106 -0
  9. package/lib/bun.js +155 -0
  10. package/lib/config.js +67 -0
  11. package/lib/constants.js +17 -0
  12. package/lib/debug.js +48 -0
  13. package/lib/esm-hooks.mjs +111 -0
  14. package/lib/exit.js +79 -0
  15. package/lib/features/metrics.js +409 -0
  16. package/lib/features/notify.js +358 -0
  17. package/lib/features/profiling.js +208 -0
  18. package/lib/hooks.js +151 -0
  19. package/lib/inbound.js +174 -0
  20. package/lib/integrations.js +372 -0
  21. package/lib/itg/amqplib.js +169 -0
  22. package/lib/itg/bullmq.js +141 -0
  23. package/lib/itg/common.js +657 -0
  24. package/lib/itg/dbcomment.js +114 -0
  25. package/lib/itg/index.js +103 -0
  26. package/lib/itg/ioredis.js +212 -0
  27. package/lib/itg/kafkajs.js +205 -0
  28. package/lib/itg/mongodb.js +322 -0
  29. package/lib/itg/mysql2.js +205 -0
  30. package/lib/itg/nats.js +531 -0
  31. package/lib/itg/noderedis.js +236 -0
  32. package/lib/itg/pg.js +253 -0
  33. package/lib/itg/pgqueue.js +305 -0
  34. package/lib/itg/postgres.js +252 -0
  35. package/lib/itg/redis-common.js +265 -0
  36. package/lib/itg/sql.js +302 -0
  37. package/lib/metrics/service.js +286 -0
  38. package/lib/metrics/types.js +342 -0
  39. package/lib/modules.js +260 -0
  40. package/lib/node-options.js +62 -0
  41. package/lib/otel/bridge.js +797 -0
  42. package/lib/otel/env.js +114 -0
  43. package/lib/outbound.js +362 -0
  44. package/lib/pmx.js +757 -0
  45. package/lib/relay/hub.js +279 -0
  46. package/lib/relay/transport.js +253 -0
  47. package/lib/standalone/activate.js +307 -0
  48. package/lib/standalone/capture.js +55 -0
  49. package/lib/standalone/container.js +909 -0
  50. package/lib/standalone/transport.js +1023 -0
  51. package/lib/tracing/context.js +90 -0
  52. package/lib/tracing/control.js +124 -0
  53. package/lib/tracing/exporter.js +115 -0
  54. package/lib/tracing/index.js +242 -0
  55. package/lib/tracing/logs.js +176 -0
  56. package/lib/tracing/options.js +105 -0
  57. package/lib/tracing/paths.js +34 -0
  58. package/lib/tracing/tracer.js +850 -0
  59. package/lib/tracing/w3c.js +415 -0
  60. package/lib/transport/ipc.js +97 -0
  61. package/lib/transport/ws.js +511 -0
  62. package/lib/util.js +109 -0
  63. package/package.json +48 -4
  64. package/preload.cjs +26 -0
  65. package/preload.mjs +25 -0
  66. package/standalone.cjs +16 -0
  67. package/standalone.mjs +17 -0
@@ -0,0 +1,322 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // MongoDB Node driver (82 §8 ITG-142, ITG-143, ITG-033), through the driver's command monitoring.
5
+ // `MongoClient` (the export of lib/mongo_client.js) is replaced by a Proxy whose construct trap
6
+ // enables `monitorCommands` unless the app passed `monitorCommands: false` explicitly, and
7
+ // observes `commandStarted` / `commandSucceeded` / `commandFailed` through the instance's `emit`
8
+ // (no listener is added, so the client's listener APIs show only the app's own). The driver emits
9
+ // these events synchronously in the async context of the operation, so a span's parent is the
10
+ // span current when the app called the operation. Documented side effect (82 open issue 3): an app
11
+ // listener registered for command events on a client created without the option now fires.
12
+ // The Proxy keeps `name`, statics, `instanceof` and subclassing (`class X extends MongoClient`).
13
+
14
+ const crypto = require('crypto')
15
+ const c = require('./common')
16
+ const dbc = require('./dbcomment')
17
+
18
+ const MAX_STATEMENT = 2000
19
+ const SKIPPED = new Set(['saslstart', 'saslcontinue', 'authenticate', 'getnonce', 'hello', 'ismaster',
20
+ 'copydbsaslstart', 'createuser', 'updateuser'])
21
+ const DROPPED = new Set(['lsid', '$clusterTime', '$db', 'txnNumber', 'signature', '$readPreference'])
22
+
23
+ let ejson = null
24
+
25
+ function isPlain (v) {
26
+ if (!v || typeof v !== 'object') return false
27
+ if (Array.isArray(v)) return false
28
+ if (v._bsontype) return false
29
+ if (v instanceof Date || v instanceof Uint8Array || v instanceof RegExp) return false
30
+ return true
31
+ }
32
+
33
+ function shape (v, depth) {
34
+ if (depth > 32) return '?'
35
+ if (Array.isArray(v)) {
36
+ if (v.length === 0) return []
37
+ const first = shape(v[0], depth + 1)
38
+ return v.length > 1 ? [first, '…'] : [first]
39
+ }
40
+ if (v instanceof Map) {
41
+ const o = {}
42
+ for (const [k, x] of v) if (!DROPPED.has(k)) o[String(k)] = shape(x, depth + 1)
43
+ return o
44
+ }
45
+ if (isPlain(v)) {
46
+ const o = {}
47
+ for (const k of Object.keys(v)) if (!DROPPED.has(k)) o[k] = shape(v[k], depth + 1)
48
+ return o
49
+ }
50
+ return '?'
51
+ }
52
+
53
+ function truncate (s) {
54
+ return s.length > MAX_STATEMENT ? s.slice(0, MAX_STATEMENT - 1) + '…' : s
55
+ }
56
+
57
+ /** ITG-033: the command document, values replaced by "?" (sanitized) or as extended JSON (raw). */
58
+ function statement (command, mode) {
59
+ if (mode === 'off' || !command || typeof command !== 'object') return undefined
60
+ try {
61
+ if (mode === 'raw') {
62
+ const doc = {}
63
+ for (const k of Object.keys(command)) if (!DROPPED.has(k)) doc[k] = command[k]
64
+ const text = ejson ? ejson.stringify(doc, { relaxed: true }) : JSON.stringify(doc, (k, v) => (typeof v === 'bigint' ? String(v) : v))
65
+ return truncate(text)
66
+ }
67
+ return truncate(JSON.stringify(shape(command, 0)))
68
+ } catch (_) {
69
+ return undefined
70
+ }
71
+ }
72
+
73
+ function collectionOf (name, command) {
74
+ try {
75
+ const v = name === 'getMore' ? command.collection : command[name]
76
+ return typeof v === 'string' ? v : undefined
77
+ } catch (_) {
78
+ return undefined
79
+ }
80
+ }
81
+
82
+ function replyRows (reply) {
83
+ try {
84
+ const cur = reply && reply.cursor
85
+ if (cur && typeof cur === 'object') {
86
+ for (const k of ['firstBatch', 'nextBatch']) if (Array.isArray(cur[k])) return cur[k].length
87
+ }
88
+ if (reply && typeof reply.n === 'number') return reply.n
89
+ } catch (_) {}
90
+ return undefined
91
+ }
92
+
93
+ function peer (address) {
94
+ const tags = {}
95
+ if (typeof address !== 'string') return tags
96
+ const i = address.lastIndexOf(':')
97
+ if (address.startsWith('/') || i === -1) tags['net.peer.name'] = address
98
+ else {
99
+ tags['net.peer.name'] = address.slice(0, i).replace(/^\[|\]$/g, '')
100
+ tags['net.peer.port'] = address.slice(i + 1)
101
+ }
102
+ return tags
103
+ }
104
+
105
+ function keyOf (ev) {
106
+ return String(ev.requestId) + '|' + String(ev.connectionId) + '|' + String(ev.address)
107
+ }
108
+
109
+ /** Per client: open spans by (requestId, connectionId, address). */
110
+ function observe (client) {
111
+ const open = new Map()
112
+ const emit = client.emit
113
+ if (typeof emit !== 'function') return
114
+ Object.defineProperty(client, 'emit', {
115
+ configurable: true,
116
+ writable: true,
117
+ enumerable: false,
118
+ value: function (event, ev) {
119
+ try {
120
+ if (event === 'commandStarted') started(open, ev)
121
+ else if (event === 'commandSucceeded') succeeded(open, ev)
122
+ else if (event === 'commandFailed') failed(open, ev)
123
+ } catch (_) {}
124
+ return emit.apply(this, arguments)
125
+ }
126
+ })
127
+ }
128
+
129
+ function started (open, ev) {
130
+ if (!ev || typeof ev.commandName !== 'string') return
131
+ const name = ev.commandName
132
+ if (SKIPPED.has(name.toLowerCase())) return
133
+ const t = c.dbTracer()
134
+ if (!t || c.suppressedLib('mongodb')) return
135
+ let command = ev.command || {}
136
+ // our trace comment (96 SVM-503): the span gets the id the comment carries, and the comment
137
+ // is not part of db.statement
138
+ let plan = null
139
+ if (typeof command.comment === 'string' && planned.has(command.comment)) {
140
+ plan = planned.get(command.comment)
141
+ planned.delete(command.comment)
142
+ const rest = {}
143
+ for (const k of Object.keys(command)) if (k !== 'comment') rest[k] = command[k]
144
+ command = rest
145
+ }
146
+ const coll = collectionOf(name, command)
147
+ const tags = { 'db.system': 'mongodb', 'db.operation': name }
148
+ if (ev.databaseName) tags['db.name'] = String(ev.databaseName)
149
+ if (coll) tags['db.mongodb.collection'] = coll
150
+ Object.assign(tags, peer(ev.address))
151
+ const st = statement(command, t.db.statement)
152
+ if (st !== undefined) tags['db.statement'] = st
153
+ const span = c.clientSpan(t, coll ? name + ' ' + coll : name, tags)
154
+ // only when the span belongs to the trace the comment names (else the comment stays unmatched)
155
+ if (span && plan && !t.bridge && typeof span.spanId === 'string' && span.traceId === plan.traceId) {
156
+ span.spanId = plan.spanId
157
+ span._ctx = null
158
+ }
159
+ if (span) open.set(keyOf(ev), span)
160
+ }
161
+
162
+ function succeeded (open, ev) {
163
+ const k = keyOf(ev)
164
+ const span = open.get(k)
165
+ if (!span) return
166
+ open.delete(k)
167
+ const reply = ev.reply
168
+ // a write the server answers with writeErrors succeeded at the protocol level (same as pmx-io)
169
+ const we = reply && Array.isArray(reply.writeErrors) && reply.writeErrors[0]
170
+ if (we) {
171
+ const code = we.code
172
+ const err = { name: code === 11000 ? 'DuplicateKeyError' : 'WriteError', message: String(we.errmsg || '') }
173
+ c.end(span, err, code)
174
+ return
175
+ }
176
+ const rows = replyRows(reply)
177
+ if (rows !== undefined) span.setAttribute('db.rows', String(rows))
178
+ c.end(span)
179
+ }
180
+
181
+ function failed (open, ev) {
182
+ const k = keyOf(ev)
183
+ const span = open.get(k)
184
+ if (!span) return
185
+ open.delete(k)
186
+ const err = ev.failure || new Error('command failed')
187
+ c.end(span, err, err && err.code !== undefined ? err.code : undefined)
188
+ }
189
+
190
+ // 96 SVM-503: comments added by the Connection.command wrapper → the span id they carry
191
+ const planned = new Map()
192
+ const PLANNED_MAX = 1024
193
+ // commands that accept `comment` from MongoDB 4.4 (wire version 9); getMore inherits it
194
+ const COMMENTED = new Set(['find', 'aggregate', 'count', 'distinct', 'insert', 'update', 'delete', 'findAndModify'])
195
+ const WIRE_4_4 = 9
196
+
197
+ function randomSpanId () {
198
+ let id
199
+ do id = crypto.randomBytes(8).toString('hex')
200
+ while (id === '0000000000000000')
201
+ return id
202
+ }
203
+
204
+ function maxWire (conn) {
205
+ const d = conn && (conn.description || conn.hello)
206
+ return d && typeof d.maxWireVersion === 'number' ? d.maxWireVersion : 0
207
+ }
208
+
209
+ /** The command document with the trace comment (a copy), or `cmd` unchanged. */
210
+ function withComment (conn, cmd) {
211
+ const t = c.dbTracer()
212
+ if (!t || t.bridge || c.suppressedLib('mongodb')) return cmd
213
+ const mode = t.db.comment
214
+ if (mode !== 'on' && mode !== 'force') return cmd
215
+ if (!cmd || typeof cmd !== 'object' || cmd.comment !== undefined) return cmd
216
+ // auto-encryption connections pass an already encrypted document down: left alone
217
+ if (conn && conn.constructor && conn.constructor.name === 'CryptoConnection') return cmd
218
+ const proto = Object.getPrototypeOf(cmd)
219
+ if (proto !== Object.prototype && proto !== null) return cmd
220
+ const name = Object.keys(cmd)[0]
221
+ if (!COMMENTED.has(name) || maxWire(conn) < WIRE_4_4) return cmd
222
+ const parent = dbc.spanIds(c.currentSpan())
223
+ if (!parent) return cmd
224
+ const id = randomSpanId()
225
+ if (!dbc.validIds(parent.traceId, id)) return cmd
226
+ const comment = dbc.mongoComment(t, parent.traceId, id)
227
+ if (planned.size >= PLANNED_MAX) planned.clear()
228
+ planned.set(comment, { traceId: parent.traceId, spanId: id })
229
+ return Object.assign({}, cmd, { comment })
230
+ }
231
+
232
+ function wrapCommand (orig) {
233
+ return function command (ns, cmd) {
234
+ let next = cmd
235
+ try {
236
+ next = withComment(this, cmd)
237
+ } catch (_) {
238
+ next = cmd
239
+ }
240
+ if (next === cmd) return orig.apply(this, arguments)
241
+ const args = Array.prototype.slice.call(arguments)
242
+ args[1] = next
243
+ return orig.apply(this, args)
244
+ }
245
+ }
246
+
247
+ /** 96 SVM-505: appName = the pmx app name unless the options or the URL name one. */
248
+ function withAppName (args, opts) {
249
+ const t = c.dbTracer()
250
+ const name = t && dbc.connectionAppName(t)
251
+ if (!name) return opts
252
+ if (opts && opts.appName !== undefined && opts.appName !== null) return opts
253
+ if (typeof args[0] === 'string' && /[?&]appname=/i.test(args[0])) return opts
254
+ return Object.assign({}, opts, { appName: name })
255
+ }
256
+
257
+ function proxyClient (MongoClient) {
258
+ return new Proxy(MongoClient, {
259
+ construct (target, args, newTarget) {
260
+ let a = args
261
+ try {
262
+ if (c.dbTracer()) {
263
+ const opts = args[1]
264
+ if (opts === undefined || opts === null || typeof opts === 'object') {
265
+ let next = opts || {}
266
+ if (next.monitorCommands === undefined) next = Object.assign({}, next, { monitorCommands: true })
267
+ next = withAppName(args, next)
268
+ if (next !== (opts || {})) {
269
+ a = args.slice()
270
+ a[1] = next
271
+ }
272
+ }
273
+ }
274
+ } catch (_) {
275
+ a = args
276
+ }
277
+ const client = Reflect.construct(target, a, newTarget)
278
+ try {
279
+ const o = client.options || {}
280
+ if (o.monitorCommands !== false) observe(client)
281
+ } catch (_) {}
282
+ return client
283
+ }
284
+ })
285
+ }
286
+
287
+ /** lib/mongo_client.js: exports.MongoClient. */
288
+ function patchClient (exports, info) {
289
+ const MongoClient = exports && exports.MongoClient
290
+ if (typeof MongoClient !== 'function') {
291
+ c.disabled('mongodb', 'MongoClient not found')
292
+ return
293
+ }
294
+ if (MongoClient[c.WRAPPED]) return
295
+ // 85 OTX-020: OpenTelemetry's MongoDB instrumentation wraps the connection's `command` (pmx
296
+ // observes events and wraps nothing there); resolved on the first command in bridge mode
297
+ const dir = info && info.dir
298
+ if (dir) {
299
+ c.watch('mongodb', () => {
300
+ const m = require(require('path').join(dir, 'lib', 'cmap', 'connection.js'))
301
+ return m && m.Connection && m.Connection.prototype ? [m.Connection.prototype, 'command'] : null
302
+ })
303
+ }
304
+ if (dir) {
305
+ try {
306
+ const m = require(require('path').join(dir, 'lib', 'cmap', 'connection.js'))
307
+ if (m && m.Connection && m.Connection.prototype) c.wrap(m.Connection.prototype, 'command', wrapCommand)
308
+ } catch (err) {
309
+ c.disabled('mongodb', 'trace comment: ' + (err && err.message))
310
+ }
311
+ }
312
+ try {
313
+ const b = require(require('path').join(info.dir, 'lib', 'bson.js'))
314
+ if (b && b.EJSON && typeof b.EJSON.stringify === 'function') ejson = b.EJSON
315
+ } catch (_) {}
316
+ const P = proxyClient(MongoClient)
317
+ const d = Object.getOwnPropertyDescriptor(exports, 'MongoClient')
318
+ if (d && d.writable) exports.MongoClient = P
319
+ else Object.defineProperty(exports, 'MongoClient', { value: P, writable: true, enumerable: true, configurable: true })
320
+ }
321
+
322
+ module.exports = { patchClient, statement, wrapCommand, withAppName, planned }
@@ -0,0 +1,205 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // mysql2 (82 §7 ITG-133, ITG-134, ITG-031). `query` and `execute` of the connection class are one
5
+ // CLIENT span per call, in callback form, event form (`query(…).on(…)`, `.stream()`) and through
6
+ // the promise wrapper (`mysql2/promise` calls the callback API, so it adds no span). The span ends
7
+ // at the command's `end` event, observed through the command's own `emit` (no listener is added,
8
+ // so an unhandled `error` event still behaves as without pmx). The statement is the text the app
9
+ // passed — before mysql2 interpolates the values client-side — sanitised per ITG-031. Pools bind
10
+ // the `getConnection` callback to the caller's context (the query a pool issues after a checkout
11
+ // belongs to the right request) and, with `db.poolSpans`, make a `pool.acquire` span when the
12
+ // checkout has to wait.
13
+
14
+ const c = require('./common')
15
+ const sql = require('./sql')
16
+ const dbc = require('./dbcomment')
17
+
18
+ const TAGS = Symbol('pmx-mysql2-tags')
19
+
20
+ function connTags (conn) {
21
+ let tags = conn[TAGS]
22
+ if (tags) return tags
23
+ tags = { 'db.system': 'mysql' }
24
+ const cfg = conn.config || {}
25
+ if (cfg.database) tags['db.name'] = String(cfg.database)
26
+ if (cfg.user) tags['db.user'] = String(cfg.user)
27
+ if (cfg.socketPath) tags['net.peer.name'] = String(cfg.socketPath)
28
+ else {
29
+ tags['net.peer.name'] = String(cfg.host || 'localhost')
30
+ if (cfg.port) tags['net.peer.port'] = String(cfg.port)
31
+ }
32
+ try {
33
+ Object.defineProperty(conn, TAGS, { value: tags, configurable: true })
34
+ } catch (_) {}
35
+ return tags
36
+ }
37
+
38
+ function textOf (s) {
39
+ if (typeof s === 'string') return s
40
+ if (s && typeof s === 'object' && typeof s.sql === 'string') return s.sql
41
+ return ''
42
+ }
43
+
44
+ function rowsOf (rows) {
45
+ if (Array.isArray(rows)) return rows.length
46
+ if (rows && typeof rows === 'object' && typeof rows.affectedRows === 'number') return rows.affectedRows
47
+ return undefined
48
+ }
49
+
50
+ /** Observe the command's completion through its own emit/onResult (instance properties). */
51
+ function observe (cmd, span) {
52
+ let err
53
+ let rows
54
+ let streamed = 0
55
+ const finish = () => {
56
+ if (span.ended) return
57
+ if (!err) {
58
+ const n = rows !== undefined ? rows : (streamed || undefined)
59
+ if (n !== undefined) span.setAttribute('db.rows', String(n))
60
+ }
61
+ c.end(span, err, err && err.errno !== undefined ? err.errno : undefined)
62
+ }
63
+ // callback mode: the result is delivered after `end` (on the next tick); the span ends then
64
+ const byCallback = typeof cmd.onResult === 'function'
65
+ const emit = cmd.emit
66
+ Object.defineProperty(cmd, 'emit', {
67
+ configurable: true,
68
+ writable: true,
69
+ enumerable: false,
70
+ value: function (ev, a) {
71
+ try {
72
+ if (ev === 'error') err = err || a
73
+ else if (ev === 'result') {
74
+ if (a && typeof a === 'object' && !Array.isArray(a) && typeof a.affectedRows === 'number') rows = a.affectedRows
75
+ else streamed++
76
+ } else if (ev === 'end' && !byCallback) finish()
77
+ } catch (_) {}
78
+ return emit.apply(this, arguments)
79
+ }
80
+ })
81
+ const onResult = cmd.onResult
82
+ if (typeof onResult === 'function') {
83
+ cmd.onResult = function (e, r) {
84
+ try {
85
+ if (e) err = e
86
+ else rows = rowsOf(r)
87
+ finish()
88
+ } catch (_) {}
89
+ return onResult.apply(this, arguments)
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * 96 SVM-501/502: prepend the trace comment (args[0] replaced by a copy). `execute()` uses
96
+ * mysql2's prepared-statement cache keyed by the text: skipped with `on`, commented with `force`.
97
+ */
98
+ function comment (t, span, s, args, prepared) {
99
+ const text = textOf(s)
100
+ if (!dbc.wants(t, span, text, prepared)) return
101
+ const prefix = dbc.sqlPrefix(t, span)
102
+ if (typeof s === 'string') args[0] = prefix + s
103
+ else {
104
+ const proto = Object.getPrototypeOf(s)
105
+ if (proto === Object.prototype || proto === null) args[0] = Object.assign({}, s, { sql: prefix + s.sql })
106
+ }
107
+ }
108
+
109
+ function wrapCall (op) {
110
+ return (orig) => function (s, values, cb) {
111
+ const t = c.dbTracer()
112
+ if (!t || c.nested() || s === null || s === undefined) return orig.apply(this, arguments)
113
+ let span = null
114
+ try {
115
+ const text = textOf(s)
116
+ const base = connTags(this)
117
+ const operation = sql.operation(text)
118
+ const tags = Object.assign({}, base)
119
+ if (operation) tags['db.operation'] = operation
120
+ const st = sql.statementTag(text, t.db.statement, true)
121
+ if (st !== undefined) tags['db.statement'] = st
122
+ const name = operation ? (base['db.name'] ? operation + ' ' + base['db.name'] : operation) : (base['db.name'] || 'mysql')
123
+ span = c.clientSpan(t, name, tags)
124
+ } catch (_) {
125
+ span = null
126
+ }
127
+ if (!span) return orig.apply(this, arguments)
128
+ const args = Array.prototype.slice.call(arguments)
129
+ try {
130
+ comment(t, span, s, args, op === 'execute')
131
+ } catch (_) {}
132
+ // callbacks run later from the socket: give them the caller's context
133
+ for (let i = 1; i < args.length; i++) if (typeof args[i] === 'function') args[i] = c.bind(args[i])
134
+ let cmd
135
+ try {
136
+ cmd = c.outer(orig, this, args)
137
+ } catch (err) {
138
+ c.end(span, err)
139
+ throw err
140
+ }
141
+ try {
142
+ if (cmd && typeof cmd.emit === 'function') observe(cmd, span)
143
+ else c.end(span)
144
+ } catch (_) {
145
+ c.end(span)
146
+ }
147
+ return cmd
148
+ }
149
+ }
150
+
151
+ function wrapGetConnection (orig) {
152
+ return function getConnection (cb) {
153
+ const t = c.tracer()
154
+ if (!t || typeof cb !== 'function') return orig.apply(this, arguments)
155
+ let span = null
156
+ try {
157
+ const cfg = this.config || {}
158
+ const limit = cfg.connectionLimit
159
+ const free = this._freeConnections
160
+ const all = this._allConnections
161
+ const freeCount = free ? (typeof free.length === 'number' ? free.length : 0) : 0
162
+ const allCount = all ? (typeof all.length === 'number' ? all.length : 0) : 0
163
+ if (t.db && t.db.poolSpans && c.currentSpan() && freeCount === 0 && limit > 0 && allCount >= limit) {
164
+ const q = this._connectionQueue
165
+ span = c.clientSpan(t, 'pool.acquire', {
166
+ 'db.system': 'mysql',
167
+ 'db.pool.size': String(allCount),
168
+ 'db.pool.max_size': String(limit),
169
+ 'db.pool.waiting': String(q && typeof q.length === 'number' ? q.length : 0)
170
+ }, 'INTERNAL')
171
+ }
172
+ } catch (_) {
173
+ span = null
174
+ }
175
+ const bound = c.bind(cb)
176
+ return orig.call(this, function (err) {
177
+ c.end(span, err || undefined)
178
+ return bound.apply(this, arguments)
179
+ })
180
+ }
181
+ }
182
+
183
+ /** lib/base/connection.js (≥ 3.10) or lib/connection.js (older 3.x): query and execute. */
184
+ function patchConnection (exports) {
185
+ const Conn = exports && (exports.default || exports)
186
+ const proto = Conn && Conn.prototype
187
+ if (!proto || typeof proto.query !== 'function') {
188
+ c.disabled('mysql2', 'Connection.prototype.query not found')
189
+ return
190
+ }
191
+ if (!Object.prototype.hasOwnProperty.call(proto, 'query')) return // inherited from base/connection.js
192
+ c.wrap(proto, 'query', wrapCall('query'))
193
+ if (typeof proto.execute === 'function') c.wrap(proto, 'execute', wrapCall('execute'))
194
+ }
195
+
196
+ /** lib/base/pool.js or lib/pool.js: getConnection. */
197
+ function patchPool (exports) {
198
+ const Pool = exports && (exports.default || exports)
199
+ const proto = Pool && Pool.prototype
200
+ if (!proto || typeof proto.getConnection !== 'function') return
201
+ if (!Object.prototype.hasOwnProperty.call(proto, 'getConnection')) return
202
+ c.wrap(proto, 'getConnection', wrapGetConnection)
203
+ }
204
+
205
+ module.exports = { patchConnection, patchPool }