@depup/elastic-apm-node 4.15.0-depup.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 (154) hide show
  1. package/LICENSE +26 -0
  2. package/NOTICE.md +442 -0
  3. package/README.md +48 -0
  4. package/changes.json +78 -0
  5. package/index.d.ts +398 -0
  6. package/index.js +11 -0
  7. package/lib/InflightEventSet.js +53 -0
  8. package/lib/activation-method.js +119 -0
  9. package/lib/agent.js +941 -0
  10. package/lib/apm-client/apm-client.js +313 -0
  11. package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
  12. package/lib/apm-client/http-apm-client/README.md +485 -0
  13. package/lib/apm-client/http-apm-client/central-config.js +41 -0
  14. package/lib/apm-client/http-apm-client/container-info.js +111 -0
  15. package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
  16. package/lib/apm-client/http-apm-client/index.js +1975 -0
  17. package/lib/apm-client/http-apm-client/logging.js +31 -0
  18. package/lib/apm-client/http-apm-client/ndjson.js +20 -0
  19. package/lib/apm-client/http-apm-client/truncate.js +434 -0
  20. package/lib/apm-client/noop-apm-client.js +73 -0
  21. package/lib/async-hooks-polyfill.js +58 -0
  22. package/lib/cloud-metadata/aws.js +175 -0
  23. package/lib/cloud-metadata/azure.js +123 -0
  24. package/lib/cloud-metadata/callback-coordination.js +159 -0
  25. package/lib/cloud-metadata/gcp.js +133 -0
  26. package/lib/cloud-metadata/index.js +175 -0
  27. package/lib/config/config.js +458 -0
  28. package/lib/config/normalizers.js +701 -0
  29. package/lib/config/schema.js +1007 -0
  30. package/lib/constants.js +35 -0
  31. package/lib/errors.js +303 -0
  32. package/lib/filters/sanitize-field-names.js +69 -0
  33. package/lib/http-request.js +249 -0
  34. package/lib/instrumentation/azure-functions.js +519 -0
  35. package/lib/instrumentation/context.js +56 -0
  36. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  37. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  38. package/lib/instrumentation/express-utils.js +91 -0
  39. package/lib/instrumentation/generic-span.js +322 -0
  40. package/lib/instrumentation/http-shared.js +424 -0
  41. package/lib/instrumentation/ids.js +39 -0
  42. package/lib/instrumentation/index.js +1127 -0
  43. package/lib/instrumentation/modules/@apollo/server.js +30 -0
  44. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  45. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  46. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  47. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  48. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  49. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  50. package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
  51. package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
  52. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  53. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  54. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  55. package/lib/instrumentation/modules/_lambda-handler.js +40 -0
  56. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  57. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  58. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  59. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  60. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  61. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  62. package/lib/instrumentation/modules/bluebird.js +93 -0
  63. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  64. package/lib/instrumentation/modules/elasticsearch.js +191 -0
  65. package/lib/instrumentation/modules/express-graphql.js +66 -0
  66. package/lib/instrumentation/modules/express-queue.js +28 -0
  67. package/lib/instrumentation/modules/express.js +162 -0
  68. package/lib/instrumentation/modules/fastify.js +172 -0
  69. package/lib/instrumentation/modules/finalhandler.js +41 -0
  70. package/lib/instrumentation/modules/generic-pool.js +85 -0
  71. package/lib/instrumentation/modules/graphql.js +256 -0
  72. package/lib/instrumentation/modules/handlebars.js +22 -0
  73. package/lib/instrumentation/modules/http.js +112 -0
  74. package/lib/instrumentation/modules/http2.js +320 -0
  75. package/lib/instrumentation/modules/https.js +68 -0
  76. package/lib/instrumentation/modules/ioredis.js +94 -0
  77. package/lib/instrumentation/modules/jade.js +18 -0
  78. package/lib/instrumentation/modules/kafkajs.js +476 -0
  79. package/lib/instrumentation/modules/knex.js +91 -0
  80. package/lib/instrumentation/modules/koa-router.js +74 -0
  81. package/lib/instrumentation/modules/koa.js +15 -0
  82. package/lib/instrumentation/modules/memcached.js +99 -0
  83. package/lib/instrumentation/modules/mimic-response.js +45 -0
  84. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  85. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  86. package/lib/instrumentation/modules/mongodb.js +259 -0
  87. package/lib/instrumentation/modules/mysql.js +200 -0
  88. package/lib/instrumentation/modules/mysql2.js +140 -0
  89. package/lib/instrumentation/modules/pg.js +148 -0
  90. package/lib/instrumentation/modules/pug.js +18 -0
  91. package/lib/instrumentation/modules/redis.js +176 -0
  92. package/lib/instrumentation/modules/restify.js +52 -0
  93. package/lib/instrumentation/modules/tedious.js +159 -0
  94. package/lib/instrumentation/modules/undici.js +270 -0
  95. package/lib/instrumentation/modules/ws.js +59 -0
  96. package/lib/instrumentation/noop-transaction.js +81 -0
  97. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  98. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  99. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  100. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  101. package/lib/instrumentation/run-context/RunContext.js +151 -0
  102. package/lib/instrumentation/run-context/index.js +23 -0
  103. package/lib/instrumentation/shimmer.js +123 -0
  104. package/lib/instrumentation/span-compression.js +239 -0
  105. package/lib/instrumentation/span.js +621 -0
  106. package/lib/instrumentation/template-shared.js +43 -0
  107. package/lib/instrumentation/timer.js +84 -0
  108. package/lib/instrumentation/transaction.js +571 -0
  109. package/lib/lambda.js +992 -0
  110. package/lib/load-source-map.js +100 -0
  111. package/lib/logging.js +212 -0
  112. package/lib/metrics/index.js +92 -0
  113. package/lib/metrics/platforms/generic/index.js +40 -0
  114. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  115. package/lib/metrics/platforms/generic/process-top.js +157 -0
  116. package/lib/metrics/platforms/generic/stats.js +34 -0
  117. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  118. package/lib/metrics/platforms/linux/index.js +19 -0
  119. package/lib/metrics/platforms/linux/stats.js +213 -0
  120. package/lib/metrics/queue.js +90 -0
  121. package/lib/metrics/registry.js +52 -0
  122. package/lib/metrics/reporter.js +119 -0
  123. package/lib/metrics/runtime.js +77 -0
  124. package/lib/middleware/connect.js +16 -0
  125. package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
  126. package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
  127. package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
  128. package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
  129. package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
  130. package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
  131. package/lib/opentelemetry-bridge/README.md +244 -0
  132. package/lib/opentelemetry-bridge/index.js +15 -0
  133. package/lib/opentelemetry-bridge/oblog.js +23 -0
  134. package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
  135. package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
  136. package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
  137. package/lib/opentelemetry-bridge/otelutils.js +99 -0
  138. package/lib/opentelemetry-bridge/setup.js +76 -0
  139. package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
  140. package/lib/opentelemetry-metrics/index.js +50 -0
  141. package/lib/parsers.js +225 -0
  142. package/lib/propwrap.js +147 -0
  143. package/lib/stacktraces.js +537 -0
  144. package/lib/symbols.js +15 -0
  145. package/lib/tracecontext/index.js +118 -0
  146. package/lib/tracecontext/traceparent.js +185 -0
  147. package/lib/tracecontext/tracestate.js +388 -0
  148. package/lib/wildcard-matcher.js +52 -0
  149. package/loader.mjs +7 -0
  150. package/package.json +299 -0
  151. package/start.d.ts +8 -0
  152. package/start.js +29 -0
  153. package/types/aws-lambda.d.ts +98 -0
  154. package/types/connect.d.ts +23 -0
@@ -0,0 +1,259 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const semver = require('semver');
10
+
11
+ const { OUTCOME_SUCCESS, OUTCOME_FAILURE } = require('../../constants');
12
+ const { getDBDestination } = require('../context');
13
+ const shimmer = require('../shimmer');
14
+ const kListenersAdded = Symbol('kListenersAdded');
15
+
16
+ // Match expected `<hostname>:<port>`, e.g. "mongo:27017", "::1:27017",
17
+ // "127.0.0.1:27017".
18
+ const HOSTNAME_PORT_RE = /^(.+):(\d+)$/;
19
+
20
+ module.exports = (mongodb, agent, { version, enabled }) => {
21
+ if (!enabled) return mongodb;
22
+ if (!semver.satisfies(version, '>=3.3 <7')) {
23
+ agent.logger.debug(
24
+ 'mongodb version %s not instrumented (mongodb <3.3 is instrumented via mongodb-core)',
25
+ version,
26
+ );
27
+ return mongodb;
28
+ }
29
+
30
+ const ins = agent._instrumentation;
31
+
32
+ const activeSpans = new Map();
33
+ if (mongodb.instrument) {
34
+ const listener = mongodb.instrument();
35
+ listener.on('started', onStart);
36
+ listener.on('succeeded', onSuccess);
37
+ listener.on('failed', onFailure);
38
+ } else if (mongodb.MongoClient) {
39
+ // mongodb 4.0+ removed the instrument() method in favor of
40
+ // listeners on the instantiated client objects. There are two mechanisms
41
+ // to get a client:
42
+ // 1. const client = new mongodb.MongoClient(...)
43
+ // 2. const client = await MongoClient.connect(...)
44
+ class MongoClientTraced extends mongodb.MongoClient {
45
+ constructor() {
46
+ // The `command*` events are only emitted if `options.monitorCommands: true`.
47
+ const args = Array.prototype.slice.call(arguments);
48
+ if (!args[1]) {
49
+ args[1] = { monitorCommands: true };
50
+ } else if (args[1].monitorCommands !== true) {
51
+ args[1] = Object.assign({}, args[1], { monitorCommands: true });
52
+ }
53
+ super(...args);
54
+ this.on('commandStarted', onStart);
55
+ this.on('commandSucceeded', onSuccess);
56
+ this.on('commandFailed', onFailure);
57
+ this[kListenersAdded] = true;
58
+ }
59
+ }
60
+ Object.defineProperty(mongodb, 'MongoClient', {
61
+ enumerable: true,
62
+ get: function () {
63
+ return MongoClientTraced;
64
+ },
65
+ });
66
+
67
+ shimmer.wrap(mongodb.MongoClient, 'connect', wrapConnect);
68
+ } else {
69
+ agent.logger.warn('could not instrument mongodb@%s', version);
70
+ }
71
+ return mongodb;
72
+
73
+ // Wrap the MongoClient.connect(url, options?, callback?) static method.
74
+ // It calls back with `function (err, client)` or returns a Promise that
75
+ // resolves to the client.
76
+ // https://github.com/mongodb/node-mongodb-native/blob/v4.2.1/src/mongo_client.ts#L503-L511
77
+ //
78
+ // From versions >=4.11.0 the method uses `new this` to create the client instance. Hence
79
+ // our `MongoClientTraced`'s constructor is used and the command listeners are already
80
+ // registered. We should check if the client has already added the listeners to avoid handling
81
+ // the same events twice
82
+ // NOTE: prefering to use a Symbol over version check since internal implementation may
83
+ // change in future versions
84
+ // https://github.com/mongodb/node-mongodb-native/blob/v4.11.0/src/mongo_client.ts#L618
85
+ function wrapConnect(origConnect) {
86
+ return function wrappedConnect(url, options, callback) {
87
+ if (typeof options === 'function') {
88
+ callback = options;
89
+ options = {};
90
+ }
91
+ options = options || {};
92
+ if (!options.monitorCommands) {
93
+ options.monitorCommands = true;
94
+ }
95
+ if (typeof callback === 'function') {
96
+ return origConnect.call(
97
+ this,
98
+ url,
99
+ options,
100
+ function wrappedCallback(err, client) {
101
+ if (err) {
102
+ callback(err);
103
+ } else {
104
+ if (!client[kListenersAdded]) {
105
+ client.on('commandStarted', onStart);
106
+ client.on('commandSucceeded', onSuccess);
107
+ client.on('commandFailed', onFailure);
108
+ client[kListenersAdded] = true;
109
+ }
110
+ callback(err, client);
111
+ }
112
+ },
113
+ );
114
+ } else {
115
+ const p = origConnect.call(this, url, options, callback);
116
+ p.then((client) => {
117
+ if (!client[kListenersAdded]) {
118
+ client.on('commandStarted', onStart);
119
+ client.on('commandSucceeded', onSuccess);
120
+ client.on('commandFailed', onFailure);
121
+ client[kListenersAdded] = true;
122
+ }
123
+ });
124
+ return p;
125
+ }
126
+ };
127
+ }
128
+
129
+ function onStart(event) {
130
+ // `event` is a `CommandStartedEvent`
131
+ // https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.rst#command-started-message
132
+ // E.g. with mongodb@6.2.0:
133
+ // CommandStartedEvent {
134
+ // connectionId: '127.0.0.1:27017',
135
+ // requestId: 1,
136
+ // databaseName: 'test',
137
+ // commandName: 'insert',
138
+ // command:
139
+ // { ... } }
140
+ const name = [
141
+ event.databaseName,
142
+ collectionFor(event),
143
+ event.commandName,
144
+ ].join('.');
145
+
146
+ const span = ins.createSpan(name, 'db', 'mongodb', event.commandName, {
147
+ exitSpan: true,
148
+ });
149
+ if (span) {
150
+ activeSpans.set(event.requestId, span);
151
+
152
+ // Destination context.
153
+ // Per the following code it looks like "<hostname>:<port>" should be
154
+ // available via the `address` or `connectionId` field.
155
+ // https://github.com/mongodb/node-mongodb-native/blob/dd356f0ede/lib/core/connection/apm.js#L155-L169
156
+ const address = event.address || event.connectionId;
157
+ let match;
158
+ if (
159
+ address &&
160
+ typeof address === 'string' &&
161
+ (match = HOSTNAME_PORT_RE.exec(address))
162
+ ) {
163
+ span._setDestinationContext(getDBDestination(match[1], match[2]));
164
+ } else {
165
+ agent.logger.trace(
166
+ 'could not set destination context on mongodb span from address=%j',
167
+ address,
168
+ );
169
+ }
170
+
171
+ const dbContext = { type: 'mongodb', instance: event.databaseName };
172
+ span.setDbContext(dbContext);
173
+ }
174
+ }
175
+
176
+ function onSuccess(event) {
177
+ // `event` is a `CommandSucceededEvent`
178
+ // https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.rst#command-succeeded-message
179
+ // E.g. with mongodb@6.2.0:
180
+ // CommandSucceededEvent {
181
+ // connectionId: '127.0.0.1:27017',
182
+ // requestId: 1,
183
+ // duration: 1,
184
+ // reply: { ... }
185
+ // }
186
+ if (!activeSpans.has(event.requestId)) return;
187
+
188
+ const span = activeSpans.get(event.requestId);
189
+ activeSpans.delete(event.requestId);
190
+
191
+ // From mongodb@3.6.0 and up some commands like `deleteOne` may contain
192
+ // error data inside the `reply` property. It makes sense to capture it.
193
+ const writeErrors = event?.reply?.writeErrors;
194
+
195
+ if (writeErrors && writeErrors.length) {
196
+ agent.captureError(writeErrors[0].errmsg, {
197
+ skipOutcome: true,
198
+ parent: span,
199
+ });
200
+ }
201
+ span.setOutcome(OUTCOME_SUCCESS);
202
+ span.end(span._timer.start / 1000 + event.duration);
203
+ }
204
+
205
+ function onFailure(event) {
206
+ // `event` is a `CommandFailedEvent`
207
+ // https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.rst#command-failed-message
208
+ // E.g. with mongodb@6.2.0:
209
+ // CommandFailedEvent {
210
+ // connectionId: '127.0.0.1:27017',
211
+ // requestId: 6,
212
+ // commandName: "find",
213
+ // duration: 1,
214
+ // "failure": { ... }
215
+ // }
216
+ if (!activeSpans.has(event.requestId)) return;
217
+
218
+ const span = activeSpans.get(event.requestId);
219
+ activeSpans.delete(event.requestId);
220
+ span.setOutcome(OUTCOME_FAILURE);
221
+ span.end(span._timer.start / 1000 + event.duration);
222
+ }
223
+
224
+ function collectionFor(event) {
225
+ // `getMore` command is a special case where the collection name is
226
+ // placed in a property named `collection`. The types exported
227
+ // do not ensure the property is there so we are defensive on its
228
+ // retrieval.
229
+ //
230
+ // Example of `getMore` payload:
231
+ // {
232
+ // event: CommandStartedEvent {
233
+ // name: 'commandStarted',
234
+ // address: '172.20.0.2:27017',
235
+ // connectionId: 12,
236
+ // requestId: 686,
237
+ // databaseName: 'mydatabase',
238
+ // commandName: 'getMore',
239
+ // command: {
240
+ // getMore: new Long('1769182660590360229'),
241
+ // collection: 'Interaction',
242
+ // ...
243
+ // }
244
+ // },
245
+ // commandName: 'getMore',
246
+ // collection: new Long('1769182660590360229')
247
+ // }
248
+ // ref: https://github.com/elastic/apm-agent-nodejs/issues/3834
249
+ if (
250
+ event.commandName === 'getMore' &&
251
+ typeof event.command.collection === 'string'
252
+ ) {
253
+ return event.command.collection;
254
+ }
255
+
256
+ const collection = event.command[event.commandName];
257
+ return typeof collection === 'string' ? collection : '$cmd';
258
+ }
259
+ };
@@ -0,0 +1,200 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const EventEmitter = require('events');
10
+
11
+ var semver = require('semver');
12
+ var sqlSummary = require('sql-summary');
13
+
14
+ var shimmer = require('../shimmer');
15
+ var symbols = require('../../symbols');
16
+ var { getDBDestination } = require('../context');
17
+
18
+ module.exports = function (mysql, agent, { version, enabled }) {
19
+ if (!enabled) {
20
+ return mysql;
21
+ }
22
+ if (!semver.satisfies(version, '^2.0.0')) {
23
+ agent.logger.debug('mysql version %s not supported - aborting...', version);
24
+ return mysql;
25
+ }
26
+
27
+ agent.logger.debug('shimming mysql.createPool');
28
+ shimmer.wrap(mysql, 'createPool', wrapCreatePool);
29
+
30
+ agent.logger.debug('shimming mysql.createPoolCluster');
31
+ shimmer.wrap(mysql, 'createPoolCluster', wrapCreatePoolCluster);
32
+
33
+ agent.logger.debug('shimming mysql.createConnection');
34
+ shimmer.wrap(mysql, 'createConnection', wrapCreateConnection);
35
+
36
+ return mysql;
37
+
38
+ function wrapCreateConnection(original) {
39
+ return function wrappedCreateConnection() {
40
+ var connection = original.apply(this, arguments);
41
+
42
+ wrapQueryable(connection, 'connection', agent);
43
+
44
+ return connection;
45
+ };
46
+ }
47
+
48
+ function wrapCreatePool(original) {
49
+ return function wrappedCreatePool() {
50
+ var pool = original.apply(this, arguments);
51
+
52
+ agent.logger.debug('shimming mysql pool.getConnection');
53
+ shimmer.wrap(pool, 'getConnection', wrapGetConnection);
54
+
55
+ return pool;
56
+ };
57
+ }
58
+
59
+ function wrapCreatePoolCluster(original) {
60
+ return function wrappedCreatePoolCluster() {
61
+ var cluster = original.apply(this, arguments);
62
+
63
+ agent.logger.debug('shimming mysql cluster.of');
64
+ shimmer.wrap(cluster, 'of', function wrapOf(original) {
65
+ return function wrappedOf() {
66
+ var ofCluster = original.apply(this, arguments);
67
+
68
+ agent.logger.debug('shimming mysql cluster of.getConnection');
69
+ shimmer.wrap(ofCluster, 'getConnection', wrapGetConnection);
70
+
71
+ return ofCluster;
72
+ };
73
+ });
74
+
75
+ return cluster;
76
+ };
77
+ }
78
+
79
+ function wrapGetConnection(original) {
80
+ return function wrappedGetConnection() {
81
+ var cb = arguments[0];
82
+
83
+ if (typeof cb === 'function') {
84
+ arguments[0] = agent._instrumentation.bindFunction(
85
+ function wrapedCallback(_err, connection) {
86
+ if (connection)
87
+ wrapQueryable(connection, 'getConnection() > connection', agent);
88
+ return cb.apply(this, arguments);
89
+ },
90
+ );
91
+ }
92
+
93
+ return original.apply(this, arguments);
94
+ };
95
+ }
96
+ };
97
+
98
+ function wrapQueryable(connection, objType, agent) {
99
+ const ins = agent._instrumentation;
100
+
101
+ agent.logger.debug('shimming mysql %s.query', objType);
102
+ shimmer.wrap(connection, 'query', wrapQuery);
103
+
104
+ let host, port, user, database;
105
+ if (typeof connection.config === 'object') {
106
+ ({ host, port, user, database } = connection.config);
107
+ }
108
+
109
+ function wrapQuery(original) {
110
+ return function wrappedQuery(sql, values, cb) {
111
+ agent.logger.debug('intercepted call to mysql %s.query', objType);
112
+
113
+ var span = ins.createSpan(null, 'db', 'mysql', 'query', {
114
+ exitSpan: true,
115
+ });
116
+ if (!span) {
117
+ return original.apply(this, arguments);
118
+ }
119
+
120
+ var hasCallback = false;
121
+ var sqlStr;
122
+
123
+ if (this[symbols.knexStackObj]) {
124
+ span.customStackTrace(this[symbols.knexStackObj]);
125
+ this[symbols.knexStackObj] = null;
126
+ }
127
+
128
+ const wrapCallback = function (origCallback) {
129
+ hasCallback = true;
130
+ return ins.bindFunction(function wrappedCallback(_err) {
131
+ span.end();
132
+ return origCallback.apply(this, arguments);
133
+ });
134
+ };
135
+
136
+ switch (typeof sql) {
137
+ case 'string':
138
+ sqlStr = sql;
139
+ break;
140
+ case 'object':
141
+ if (typeof sql._callback === 'function') {
142
+ sql._callback = wrapCallback(sql._callback);
143
+ }
144
+ sqlStr = sql.sql;
145
+ break;
146
+ case 'function':
147
+ arguments[0] = wrapCallback(sql);
148
+ break;
149
+ }
150
+
151
+ if (sqlStr) {
152
+ agent.logger.debug({ sql: sqlStr }, 'extracted sql from mysql query');
153
+ span.setDbContext({
154
+ statement: sqlStr,
155
+ type: 'sql',
156
+ user,
157
+ instance: database,
158
+ });
159
+ span.name = sqlSummary(sqlStr);
160
+ }
161
+ span._setDestinationContext(getDBDestination(host, port));
162
+
163
+ if (typeof values === 'function') {
164
+ arguments[1] = wrapCallback(values);
165
+ } else if (typeof cb === 'function') {
166
+ arguments[2] = wrapCallback(cb);
167
+ }
168
+
169
+ const spanRunContext = ins.currRunContext().enterSpan(span);
170
+ const result = ins.withRunContext(
171
+ spanRunContext,
172
+ original,
173
+ this,
174
+ ...arguments,
175
+ );
176
+
177
+ if (!hasCallback && result instanceof EventEmitter) {
178
+ // Wrap `result.emit` instead of `result.once('error', ...)` to avoid
179
+ // changing app behaviour by possibly setting the only 'error' handler.
180
+ shimmer.wrap(result, 'emit', function (origEmit) {
181
+ return function wrappedEmit(event, data) {
182
+ // The 'mysql' module emits 'end' even after an 'error' event.
183
+ switch (event) {
184
+ case 'error':
185
+ break;
186
+ case 'end':
187
+ span.end();
188
+ break;
189
+ }
190
+ return origEmit.apply(this, arguments);
191
+ };
192
+ });
193
+ // Ensure event handlers execute in the caller run context.
194
+ ins.bindEmitter(result);
195
+ }
196
+
197
+ return result;
198
+ };
199
+ }
200
+ }
@@ -0,0 +1,140 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ var semver = require('semver');
10
+ var sqlSummary = require('sql-summary');
11
+
12
+ var shimmer = require('../shimmer');
13
+ var symbols = require('../../symbols');
14
+ var { getDBDestination } = require('../context');
15
+
16
+ module.exports = function (mysql2, agent, { version, enabled }) {
17
+ if (!enabled) {
18
+ return mysql2;
19
+ }
20
+ if (!semver.satisfies(version, '>=1 <4')) {
21
+ agent.logger.debug(
22
+ 'mysql2 version %s not supported - aborting...',
23
+ version,
24
+ );
25
+ return mysql2;
26
+ }
27
+
28
+ var ins = agent._instrumentation;
29
+
30
+ // mysql2@3.11.5 added BaseConnection class which is extended by Connection
31
+ // but is not in the public API so we need to extract it via prototype chain
32
+ // ref: https://github.com/sidorares/node-mysql2/pull/3081
33
+ const baseClass = Object.getPrototypeOf(mysql2.Connection);
34
+ const baseProto = baseClass.prototype;
35
+ const hasQuery = typeof baseProto?.query === 'function';
36
+ const hasExec = typeof baseProto?.execute === 'function';
37
+ const shouldPatchBase = hasQuery && hasExec;
38
+
39
+ if (shouldPatchBase) {
40
+ shimmer.wrap(baseProto, 'query', wrapQuery);
41
+ shimmer.wrap(baseProto, 'execute', wrapQuery);
42
+ } else {
43
+ shimmer.wrap(mysql2.Connection.prototype, 'query', wrapQuery);
44
+ shimmer.wrap(mysql2.Connection.prototype, 'execute', wrapQuery);
45
+ }
46
+
47
+ return mysql2;
48
+
49
+ function wrapQuery(original) {
50
+ return function wrappedQuery(sql, values, cb) {
51
+ agent.logger.debug('intercepted call to mysql2.%s', original.name);
52
+
53
+ var span = ins.createSpan(null, 'db', 'mysql', 'query', {
54
+ exitSpan: true,
55
+ });
56
+ if (!span) {
57
+ return original.apply(this, arguments);
58
+ }
59
+
60
+ if (this[symbols.knexStackObj]) {
61
+ span.customStackTrace(this[symbols.knexStackObj]);
62
+ this[symbols.knexStackObj] = null;
63
+ }
64
+
65
+ let hasCallback = false;
66
+ const wrapCallback = function (origCallback) {
67
+ hasCallback = true;
68
+ return ins.bindFunction(function wrappedCallback(_err) {
69
+ span.end();
70
+ return origCallback.apply(this, arguments);
71
+ });
72
+ };
73
+
74
+ let host, port, user, database;
75
+ if (typeof this.config === 'object') {
76
+ ({ host, port, user, database } = this.config);
77
+ }
78
+ span._setDestinationContext(getDBDestination(host, port));
79
+
80
+ let sqlStr;
81
+ switch (typeof sql) {
82
+ case 'string':
83
+ sqlStr = sql;
84
+ break;
85
+ case 'object':
86
+ // `mysql2.{query,execute}` support the only arg being an instance
87
+ // of the internal mysql2 `Command` object.
88
+ if (typeof sql.onResult === 'function') {
89
+ sql.onResult = wrapCallback(sql.onResult);
90
+ }
91
+ sqlStr = sql.sql;
92
+ break;
93
+ case 'function':
94
+ arguments[0] = wrapCallback(sql);
95
+ break;
96
+ }
97
+ if (sqlStr) {
98
+ span.setDbContext({
99
+ type: 'sql',
100
+ instance: database,
101
+ user,
102
+ statement: sqlStr,
103
+ });
104
+ span.name = sqlSummary(sqlStr);
105
+ } else {
106
+ span.setDbContext({ type: 'sql', instance: database, user });
107
+ }
108
+
109
+ if (typeof values === 'function') {
110
+ arguments[1] = wrapCallback(values);
111
+ } else if (typeof cb === 'function') {
112
+ arguments[2] = wrapCallback(cb);
113
+ }
114
+ const spanRunContext = ins.currRunContext().enterSpan(span);
115
+ const result = ins.withRunContext(
116
+ spanRunContext,
117
+ original,
118
+ this,
119
+ ...arguments,
120
+ );
121
+
122
+ if (result && !hasCallback) {
123
+ ins.bindEmitter(result);
124
+ shimmer.wrap(result, 'emit', function (origEmit) {
125
+ return function (event) {
126
+ switch (event) {
127
+ case 'error':
128
+ case 'close':
129
+ case 'end':
130
+ span.end();
131
+ }
132
+ return origEmit.apply(this, arguments);
133
+ };
134
+ });
135
+ }
136
+
137
+ return result;
138
+ };
139
+ }
140
+ };