@redthreadlabs/tracelog 1.4.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 (127) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +126 -0
  3. package/index.d.ts +464 -0
  4. package/index.js +11 -0
  5. package/lib/InflightEventSet.js +53 -0
  6. package/lib/activation-method.js +97 -0
  7. package/lib/agent.js +1226 -0
  8. package/lib/apm-client/apm-client.js +107 -0
  9. package/lib/apm-client/channel-writer.js +334 -0
  10. package/lib/apm-client/jsonl-file-client.js +241 -0
  11. package/lib/apm-client/ndjson.js +20 -0
  12. package/lib/apm-client/noop-apm-client.js +79 -0
  13. package/lib/apm-client/s3-uploader.js +308 -0
  14. package/lib/apm-client/truncate.js +507 -0
  15. package/lib/async-hooks-polyfill.js +58 -0
  16. package/lib/cloud-metadata/aws.js +175 -0
  17. package/lib/cloud-metadata/azure.js +123 -0
  18. package/lib/cloud-metadata/callback-coordination.js +159 -0
  19. package/lib/cloud-metadata/gcp.js +133 -0
  20. package/lib/cloud-metadata/index.js +175 -0
  21. package/lib/config/config.js +431 -0
  22. package/lib/config/normalizers.js +649 -0
  23. package/lib/config/schema.js +946 -0
  24. package/lib/constants.js +35 -0
  25. package/lib/errors.js +303 -0
  26. package/lib/filters/sanitize-field-names.js +69 -0
  27. package/lib/http-request.js +249 -0
  28. package/lib/instrumentation/context.js +56 -0
  29. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  30. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  31. package/lib/instrumentation/express-utils.js +91 -0
  32. package/lib/instrumentation/generic-span.js +322 -0
  33. package/lib/instrumentation/http-shared.js +424 -0
  34. package/lib/instrumentation/ids.js +39 -0
  35. package/lib/instrumentation/index.js +1078 -0
  36. package/lib/instrumentation/modules/@apollo/server.js +39 -0
  37. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  38. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  39. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  40. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  41. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  42. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  43. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  44. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  45. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  46. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  47. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  48. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  49. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  50. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  51. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  52. package/lib/instrumentation/modules/bluebird.js +93 -0
  53. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  54. package/lib/instrumentation/modules/elasticsearch.js +200 -0
  55. package/lib/instrumentation/modules/express-graphql.js +66 -0
  56. package/lib/instrumentation/modules/express-queue.js +28 -0
  57. package/lib/instrumentation/modules/express.js +162 -0
  58. package/lib/instrumentation/modules/fastify.js +179 -0
  59. package/lib/instrumentation/modules/finalhandler.js +41 -0
  60. package/lib/instrumentation/modules/generic-pool.js +85 -0
  61. package/lib/instrumentation/modules/graphql.js +256 -0
  62. package/lib/instrumentation/modules/handlebars.js +33 -0
  63. package/lib/instrumentation/modules/http.js +112 -0
  64. package/lib/instrumentation/modules/http2.js +320 -0
  65. package/lib/instrumentation/modules/https.js +68 -0
  66. package/lib/instrumentation/modules/ioredis.js +94 -0
  67. package/lib/instrumentation/modules/jade.js +29 -0
  68. package/lib/instrumentation/modules/kafkajs.js +476 -0
  69. package/lib/instrumentation/modules/knex.js +91 -0
  70. package/lib/instrumentation/modules/koa-router.js +74 -0
  71. package/lib/instrumentation/modules/koa.js +15 -0
  72. package/lib/instrumentation/modules/memcached.js +100 -0
  73. package/lib/instrumentation/modules/mimic-response.js +45 -0
  74. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  75. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  76. package/lib/instrumentation/modules/mongodb.js +259 -0
  77. package/lib/instrumentation/modules/mysql.js +200 -0
  78. package/lib/instrumentation/modules/mysql2.js +140 -0
  79. package/lib/instrumentation/modules/pg.js +148 -0
  80. package/lib/instrumentation/modules/pug.js +29 -0
  81. package/lib/instrumentation/modules/redis.js +176 -0
  82. package/lib/instrumentation/modules/restify.js +52 -0
  83. package/lib/instrumentation/modules/tedious.js +159 -0
  84. package/lib/instrumentation/modules/undici.js +270 -0
  85. package/lib/instrumentation/modules/ws.js +59 -0
  86. package/lib/instrumentation/noop-transaction.js +81 -0
  87. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  88. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  89. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  90. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  91. package/lib/instrumentation/run-context/RunContext.js +151 -0
  92. package/lib/instrumentation/run-context/index.js +23 -0
  93. package/lib/instrumentation/shimmer.js +123 -0
  94. package/lib/instrumentation/span-compression.js +239 -0
  95. package/lib/instrumentation/span.js +621 -0
  96. package/lib/instrumentation/template-shared.js +43 -0
  97. package/lib/instrumentation/timer.js +84 -0
  98. package/lib/instrumentation/transaction.js +571 -0
  99. package/lib/load-source-map.js +100 -0
  100. package/lib/logging.js +212 -0
  101. package/lib/metrics/index.js +92 -0
  102. package/lib/metrics/platforms/generic/index.js +40 -0
  103. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  104. package/lib/metrics/platforms/generic/process-top.js +157 -0
  105. package/lib/metrics/platforms/generic/stats.js +34 -0
  106. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  107. package/lib/metrics/platforms/linux/index.js +19 -0
  108. package/lib/metrics/platforms/linux/stats.js +213 -0
  109. package/lib/metrics/queue.js +90 -0
  110. package/lib/metrics/registry.js +52 -0
  111. package/lib/metrics/reporter.js +119 -0
  112. package/lib/metrics/runtime.js +77 -0
  113. package/lib/middleware/connect.js +16 -0
  114. package/lib/parsers.js +225 -0
  115. package/lib/propwrap.js +147 -0
  116. package/lib/stacktraces.js +537 -0
  117. package/lib/symbols.js +15 -0
  118. package/lib/tracecontext/index.js +115 -0
  119. package/lib/tracecontext/traceparent.js +185 -0
  120. package/lib/tracecontext/tracestate.js +388 -0
  121. package/lib/wildcard-matcher.js +52 -0
  122. package/loader.mjs +7 -0
  123. package/package.json +98 -0
  124. package/start.d.ts +8 -0
  125. package/start.js +29 -0
  126. package/types/aws-lambda.d.ts +98 -0
  127. package/types/connect.d.ts +23 -0
@@ -0,0 +1,476 @@
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 { Buffer } = require('buffer');
10
+
11
+ const semver = require('semver');
12
+
13
+ const constants = require('../../constants');
14
+ const shimmer = require('../shimmer');
15
+ const { redactKeysFromObject } = require('../../filters/sanitize-field-names');
16
+
17
+ const NAME = 'Kafka';
18
+ const TYPE = 'messaging';
19
+ const SUBTYPE = 'kafka';
20
+
21
+ /**
22
+ * @typedef {{ Kafka: import('kafkajs').Kafka}} KafkaModule
23
+ * @typedef {(config: any) => Consumer} ConsumerFactory
24
+ * @typedef {import('kafkajs').Consumer} Consumer
25
+ * @typedef {import('kafkajs').ConsumerRunConfig} ConsumerRunConfig
26
+ * @typedef {(config: any) => Producer} ProducerFactory
27
+ * @typedef {import('kafkajs').Producer} Producer
28
+ * @typedef {import('kafkajs').ProducerRecord} ProducerRecord
29
+ */
30
+
31
+ /**
32
+ * @param {KafkaModule} mod
33
+ * @param {any} agent
34
+ * @param {Object} options
35
+ * @param {string} options.version
36
+ * @param {boolean} options.enabled
37
+ */
38
+ module.exports = function (mod, agent, { version, enabled }) {
39
+ if (!enabled || !semver.satisfies(version, '>=2 <3')) {
40
+ return mod;
41
+ }
42
+
43
+ const config = agent._conf;
44
+ const ins = agent._instrumentation;
45
+
46
+ agent.logger.debug('shimming Kafka.prototype.consumer');
47
+ shimmer.wrap(mod.Kafka.prototype, 'consumer', wrapConsumer);
48
+ agent.logger.debug('shimming Kafka.prototype.producer');
49
+ shimmer.wrap(mod.Kafka.prototype, 'producer', wrapProducer);
50
+ return mod;
51
+
52
+ /**
53
+ * Returns the patched version of `Kafka.consumer` which creates a new
54
+ * consumer with its `run` method patched to instrument message handling
55
+ *
56
+ * @param {ConsumerFactory} origConsumer
57
+ * @returns {ConsumerFactory}
58
+ */
59
+ function wrapConsumer(origConsumer) {
60
+ return function wrappedConsumer() {
61
+ const consumer = origConsumer.apply(this, arguments);
62
+
63
+ shimmer.wrap(consumer, 'run', wrapConsumerRun);
64
+ return consumer;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Return the patched version of `run` which instruments the
70
+ * `eachMessage` & `eachBatch` callbacks.
71
+ *
72
+ * @param {Consumer['run']} origRun
73
+ * @returns {Consumer['run']}
74
+ */
75
+ function wrapConsumerRun(origRun) {
76
+ return function wrappedConsumerRun() {
77
+ const runConfig = arguments[0];
78
+
79
+ if (typeof runConfig.eachMessage === 'function') {
80
+ shimmer.wrap(runConfig, 'eachMessage', wrapEachMessage);
81
+ }
82
+
83
+ if (typeof runConfig.eachBatch === 'function') {
84
+ shimmer.wrap(runConfig, 'eachBatch', wrapEachBatch);
85
+ }
86
+
87
+ return origRun.apply(this, arguments);
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Returns the instrumented version of `eachMessage` which
93
+ * - creates a transaction each time is called
94
+ * - add trace context into the transaction if present in message headers
95
+ *
96
+ * @param {ConsumerRunConfig['eachMessage']} origEachMessage
97
+ * @returns {ConsumerRunConfig['eachMessage']}
98
+ */
99
+ function wrapEachMessage(origEachMessage) {
100
+ return async function (payload) {
101
+ const { topic, message } = payload;
102
+
103
+ if (shouldIgnoreTopic(topic, config)) {
104
+ return origEachMessage.apply(this, arguments);
105
+ }
106
+
107
+ // For distributed tracing this instrumentation is going to check
108
+ // the headers defined by opentelemetry and ignore the propietary
109
+ // `elasticaapmtraceparent` header
110
+ // https://github.com/elastic/apm/blob/main/specs/agents/tracing-distributed-tracing.md#binary-fields
111
+ const traceparent = message.headers && message.headers.traceparent;
112
+ const tracestate = message.headers && message.headers.tracestate;
113
+ const opts = {};
114
+
115
+ // According to `kafkajs` types a header value might be
116
+ // a string or Buffer
117
+ // https://github.com/tulios/kafkajs/blob/ff3b1117f316d527ae170b550bc0f772614338e9/types/index.d.ts#L148
118
+ if (typeof traceparent === 'string') {
119
+ opts.childOf = traceparent;
120
+ } else if (traceparent instanceof Buffer) {
121
+ opts.childOf = traceparent.toString('utf-8');
122
+ }
123
+
124
+ if (typeof tracestate === 'string') {
125
+ opts.tracestate = tracestate;
126
+ } else if (tracestate instanceof Buffer) {
127
+ opts.tracestate = tracestate.toString('utf-8');
128
+ }
129
+
130
+ const trans = ins.startTransaction(
131
+ `${NAME} RECEIVE from ${topic}`,
132
+ TYPE,
133
+ opts,
134
+ );
135
+
136
+ const messageCtx = { queue: { name: topic } };
137
+ if (
138
+ config.captureBody === 'all' ||
139
+ config.captureBody === 'transactions'
140
+ ) {
141
+ messageCtx.body = message.value?.toString();
142
+ }
143
+
144
+ if (message.headers && config.captureHeaders) {
145
+ // Make sure there is no sensitive data
146
+ // and transform non-redacted buffers
147
+ messageCtx.headers = redactKeysFromObject(
148
+ message.headers,
149
+ config.sanitizeFieldNamesRegExp,
150
+ );
151
+ Object.keys(messageCtx.headers).forEach((key) => {
152
+ const value = messageCtx.headers[key];
153
+ if (value instanceof Buffer) {
154
+ messageCtx.headers[key] = value.toString('utf-8');
155
+ }
156
+ });
157
+ }
158
+
159
+ if (message.timestamp) {
160
+ messageCtx.age = {
161
+ ms: Date.now() - Number(message.timestamp),
162
+ };
163
+ }
164
+
165
+ trans.setMessageContext(messageCtx);
166
+
167
+ let result, err;
168
+ try {
169
+ result = await origEachMessage.apply(this, arguments);
170
+ } catch (ex) {
171
+ // Save the error for use in `finally` below, but re-throw it to
172
+ // not impact code flow.
173
+ err = ex;
174
+ throw ex;
175
+ } finally {
176
+ trans.setOutcome(
177
+ err ? constants.OUTCOME_FAILURE : constants.OUTCOME_SUCCESS,
178
+ );
179
+ trans.end();
180
+ }
181
+
182
+ return result;
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Returns the instrumented version of `eachBatch` which
188
+ * - creates a transaction each time is called
189
+ * - if trace context present in messages inks them to the transaction
190
+ *
191
+ * @param {ConsumerRunConfig['eachBatch']} origEachBatch
192
+ * @returns {ConsumerRunConfig['eachBatch']}
193
+ */
194
+ function wrapEachBatch(origEachBatch) {
195
+ return async function ({ batch }) {
196
+ if (shouldIgnoreTopic(batch.topic, config)) {
197
+ return origEachBatch.apply(this, arguments);
198
+ }
199
+
200
+ const trans = ins.startTransaction(
201
+ `${NAME} RECEIVE from ${batch.topic}`,
202
+ TYPE,
203
+ );
204
+ const messageCtx = { queue: { name: batch.topic } };
205
+ trans.setMessageContext(messageCtx);
206
+
207
+ const serviceContext = {
208
+ framework: { name: 'Kafka' },
209
+ };
210
+ trans.setServiceContext(serviceContext);
211
+
212
+ // Extract span links from up to 1000 messages in this batch.
213
+ // https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-messaging.md#receiving-trace-context
214
+ // A span link is created from a `traceparent` header in a message.
215
+ const messages = batch && batch.messages;
216
+
217
+ if (messages) {
218
+ const traceparentsSeen = new Set();
219
+ const links = [];
220
+ const limit = Math.min(
221
+ messages.length,
222
+ constants.MAX_MESSAGES_PROCESSED_FOR_TRACE_CONTEXT,
223
+ );
224
+
225
+ for (let i = 0; i < messages.length; i++) {
226
+ const msg = messages[i];
227
+ const traceparent =
228
+ msg.headers &&
229
+ msg.headers.traceparent &&
230
+ msg.headers.traceparent.toString();
231
+
232
+ if (traceparent && !traceparentsSeen.has(traceparent)) {
233
+ links.push({ context: traceparent });
234
+ traceparentsSeen.add(traceparent);
235
+
236
+ if (links.length >= limit) {
237
+ break;
238
+ }
239
+ }
240
+ }
241
+ trans.addLinks(links);
242
+ }
243
+
244
+ let result, err;
245
+ try {
246
+ result = await origEachBatch.apply(this, arguments);
247
+ } catch (ex) {
248
+ // Save the error for use in `finally` below, but re-throw it to
249
+ // not impact code flow.
250
+ err = ex;
251
+ throw ex;
252
+ } finally {
253
+ trans.setOutcome(
254
+ err ? constants.OUTCOME_FAILURE : constants.OUTCOME_SUCCESS,
255
+ );
256
+ trans.end();
257
+ }
258
+
259
+ return result;
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Returns the patched version of `Kafka.producer` which creates a new
265
+ * producer with `send` & `sendBatch` methods patched to instrument message sending
266
+ *
267
+ * @param {ProducerFactory} origProducer
268
+ * @returns {ProducerFactory}
269
+ */
270
+ function wrapProducer(origProducer) {
271
+ return function wrappedProducer() {
272
+ const producer = origProducer.apply(this, arguments);
273
+
274
+ shimmer.wrap(producer, 'send', wrapProducerSend);
275
+ shimmer.wrap(producer, 'sendBatch', wrapProducerSendBatch);
276
+ return producer;
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Returns the instrumented version of `send` which
282
+ * - creates an exit span each time is called
283
+ * - propagates trace context through message headers
284
+ *
285
+ * @param {Producer['send']} origSend
286
+ * @returns {Producer['send']}
287
+ */
288
+ function wrapProducerSend(origSend) {
289
+ return async function (record) {
290
+ const { topic } = record;
291
+ let span;
292
+
293
+ if (!shouldIgnoreTopic(topic, config)) {
294
+ span = ins.createSpan(
295
+ `${NAME} SEND to ${topic}`,
296
+ TYPE,
297
+ SUBTYPE,
298
+ 'send',
299
+ { exitSpan: true },
300
+ );
301
+ }
302
+
303
+ // W3C trace-context propagation.
304
+ const runContext = ins.currRunContext();
305
+ const parentSpan =
306
+ span || runContext.currSpan() || runContext.currTransaction();
307
+
308
+ if (parentSpan) {
309
+ record.messages.forEach((msg) => {
310
+ const newHeaders = Object.assign({}, msg.headers);
311
+ parentSpan.propagateTraceContextHeaders(
312
+ newHeaders,
313
+ function (carrier, name, value) {
314
+ if (name.startsWith('elastic-')) {
315
+ return;
316
+ }
317
+ carrier[name] = value;
318
+ },
319
+ );
320
+ msg.headers = newHeaders;
321
+ });
322
+ }
323
+
324
+ if (!span) {
325
+ return origSend.apply(this, arguments);
326
+ }
327
+
328
+ // We do not add headers or body because:
329
+ // - `record.messages` is a list
330
+ // - spec says is for transactions (https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-messaging.md#transaction-context-fields)
331
+ span.setMessageContext({ queue: { name: topic } });
332
+
333
+ const service = {
334
+ resource: `${SUBTYPE}/${topic}`,
335
+ type: SUBTYPE,
336
+ name: topic,
337
+ };
338
+
339
+ span._setDestinationContext({ service });
340
+
341
+ let result, err;
342
+ try {
343
+ result = await origSend.apply(this, arguments);
344
+ } catch (ex) {
345
+ // Save the error for use in `finally` below, but re-throw it to
346
+ // not impact code flow.
347
+ err = ex;
348
+ throw ex;
349
+ } finally {
350
+ span.setOutcome(
351
+ err ? constants.OUTCOME_FAILURE : constants.OUTCOME_SUCCESS,
352
+ );
353
+ span.end();
354
+ }
355
+
356
+ return result;
357
+ };
358
+ }
359
+
360
+ /**
361
+ * Returns the patched version of `Producer.sendBatch` which
362
+ * - creates an exit span for the operation
363
+ * - propagates trace context via message headers
364
+ *
365
+ * @param {Producer['sendBatch']} origSendBatch
366
+ * @returns {Producer['sendBatch']}
367
+ */
368
+ function wrapProducerSendBatch(origSendBatch) {
369
+ return async function (batch) {
370
+ let span;
371
+ let topicForContext;
372
+ let shouldIgnoreBatch = true;
373
+ const messages = batch.topicMessages || [];
374
+ const topics = new Set();
375
+
376
+ // Remove possible topic duplications
377
+ for (const msg of messages) {
378
+ topics.add(msg.topic);
379
+ }
380
+
381
+ for (const t of topics) {
382
+ const topicIgnored = shouldIgnoreTopic(t, config);
383
+
384
+ shouldIgnoreBatch = shouldIgnoreBatch && topicIgnored;
385
+
386
+ // When a topic is not ignored we keep a copy for context unless
387
+ // we find a 2nd topic also not ignored.
388
+ if (!topicIgnored) {
389
+ if (topicForContext) {
390
+ topicForContext = undefined;
391
+ break;
392
+ }
393
+ topicForContext = t;
394
+ }
395
+ }
396
+
397
+ if (!shouldIgnoreBatch) {
398
+ const suffix = topicForContext ? ` to ${topicForContext}` : '';
399
+ span = ins.createSpan(`${NAME} SEND${suffix}`, TYPE, SUBTYPE, 'send', {
400
+ exitSpan: true,
401
+ });
402
+ }
403
+
404
+ // W3C trace-context propagation.
405
+ const runContext = ins.currRunContext();
406
+ const parentSpan =
407
+ span || runContext.currSpan() || runContext.currTransaction();
408
+
409
+ if (parentSpan && batch.topicMessages) {
410
+ batch.topicMessages.forEach((topicMessage) => {
411
+ topicMessage.messages.forEach((msg) => {
412
+ const newHeaders = Object.assign({}, msg.headers);
413
+ parentSpan.propagateTraceContextHeaders(
414
+ newHeaders,
415
+ function (carrier, name, value) {
416
+ if (name.startsWith('elastic-')) {
417
+ return;
418
+ }
419
+ carrier[name] = value;
420
+ },
421
+ );
422
+ msg.headers = newHeaders;
423
+ });
424
+ });
425
+ }
426
+
427
+ if (!span) {
428
+ return origSendBatch.apply(this, arguments);
429
+ }
430
+
431
+ if (topicForContext) {
432
+ // We do not add headers or body because:
433
+ // - `record.messages` is a list
434
+ // - spec says is for transactions (https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-messaging.md#transaction-context-fields)
435
+ span.setMessageContext({ queue: { name: topicForContext } });
436
+ }
437
+ span.setServiceTarget(SUBTYPE, topicForContext);
438
+
439
+ let result, err;
440
+ try {
441
+ result = await origSendBatch.apply(this, arguments);
442
+ } catch (ex) {
443
+ // Save the error for use in `finally` below, but re-throw it to
444
+ // not impact code flow.
445
+ err = ex;
446
+ throw ex;
447
+ } finally {
448
+ span.setOutcome(
449
+ err ? constants.OUTCOME_FAILURE : constants.OUTCOME_SUCCESS,
450
+ );
451
+ span.end();
452
+ }
453
+
454
+ return result;
455
+ };
456
+ }
457
+ };
458
+
459
+ /**
460
+ * Returns true if we have to ignore messages on the given topic
461
+ *
462
+ * @param {string} topic the topic where client is publishing/subscribing
463
+ * @param {{ ignoreMessageQueuesRegExp: RegExp[] }} config the agent's configuration object
464
+ * @returns {boolean}
465
+ */
466
+ function shouldIgnoreTopic(topic, config) {
467
+ if (config.ignoreMessageQueuesRegExp) {
468
+ for (const rule of config.ignoreMessageQueuesRegExp) {
469
+ if (rule.test(topic)) {
470
+ return true;
471
+ }
472
+ }
473
+ }
474
+
475
+ return false;
476
+ }
@@ -0,0 +1,91 @@
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
+ // Knex instrumentation exists to capture a more useful span stacktrace at
8
+ // the start of a Knex query, and use that stacktrace on the 'pg' or 'mysql'
9
+ // span.
10
+
11
+ 'use strict';
12
+
13
+ var semver = require('semver');
14
+
15
+ var shimmer = require('../shimmer');
16
+ var symbols = require('../../symbols');
17
+
18
+ module.exports = function (Knex, agent, { version, enabled }) {
19
+ if (!enabled) {
20
+ return Knex;
21
+ }
22
+ if (!semver.satisfies(version, '>=0.10.0 <4.0.0')) {
23
+ agent.logger.debug(
24
+ `knex@${version} is not supported, skipping knex instrumentation`,
25
+ );
26
+ return Knex;
27
+ }
28
+
29
+ if (agent._conf.spanStackTraceMinDuration < 0) {
30
+ agent.logger.trace(
31
+ 'not instrumenting knex because not capturing span stack traces (spanStackTraceMinDuration=%s)',
32
+ agent._conf.spanStackTraceMinDuration,
33
+ );
34
+ return Knex;
35
+ }
36
+
37
+ function wrapQueryStartPoint(original) {
38
+ return function wrappedQueryStartPoint() {
39
+ var builder = original.apply(this, arguments);
40
+
41
+ agent.logger.debug('capturing custom stack trace for knex');
42
+ var obj = {};
43
+ Error.captureStackTrace(obj);
44
+ builder[symbols.knexStackObj] = obj;
45
+
46
+ return builder;
47
+ };
48
+ }
49
+
50
+ function wrapRunner(original) {
51
+ return function wrappedRunner() {
52
+ var runner = original.apply(this, arguments);
53
+
54
+ agent.logger.debug('shimming knex runner.query');
55
+ shimmer.wrap(runner, 'query', wrapQuery);
56
+
57
+ return runner;
58
+ };
59
+ }
60
+
61
+ function wrapQuery(original) {
62
+ return function wrappedQuery() {
63
+ agent.logger.debug('intercepted call to knex runner.query');
64
+ if (this.connection) {
65
+ this.connection[symbols.knexStackObj] = this.builder
66
+ ? this.builder[symbols.knexStackObj]
67
+ : null;
68
+ }
69
+ return original.apply(this, arguments);
70
+ };
71
+ }
72
+
73
+ return function wrappedKnex() {
74
+ const knexInstance = Knex.apply(null, arguments);
75
+
76
+ if (knexInstance && knexInstance.client) {
77
+ const QUERY_FNS = ['queryBuilder', 'raw'];
78
+ agent.logger.debug('shimming knexInstance.client.runner');
79
+ shimmer.wrap(knexInstance.client, 'runner', wrapRunner);
80
+ agent.logger.debug(
81
+ 'shimming Knex.Client.prototype functions: %j',
82
+ QUERY_FNS,
83
+ );
84
+ shimmer.massWrap(knexInstance.client, QUERY_FNS, wrapQueryStartPoint);
85
+ } else {
86
+ agent.logger.debug('could not shim Knex');
87
+ }
88
+
89
+ return knexInstance;
90
+ };
91
+ };
@@ -0,0 +1,74 @@
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
+
11
+ var shimmer = require('../shimmer');
12
+
13
+ module.exports = function (Router, agent, { version, enabled }) {
14
+ if (!enabled) return Router;
15
+ if (!semver.satisfies(version, '>=5.2.0 <14')) {
16
+ agent.logger.debug(
17
+ 'koa-router version %s not supported - aborting...',
18
+ version,
19
+ );
20
+ return Router;
21
+ }
22
+ if (
23
+ semver.satisfies(version, '>=13') &&
24
+ semver.satisfies(process.version, '<18')
25
+ ) {
26
+ agent.logger.debug(
27
+ 'koa-router version %s not supported for node %s - aborting...',
28
+ version,
29
+ process.version,
30
+ );
31
+ return Router;
32
+ }
33
+
34
+ agent.logger.debug('shimming koa-router prototype.match function');
35
+ shimmer.wrap(Router.prototype, 'match', function (orig) {
36
+ return function (_, method) {
37
+ var matched = orig.apply(this, arguments);
38
+
39
+ if (typeof method !== 'string') {
40
+ agent.logger.debug(
41
+ 'unexpected method type in koa-router prototype.match: %s',
42
+ typeof method,
43
+ );
44
+ return matched;
45
+ }
46
+
47
+ if (Array.isArray(matched && matched.pathAndMethod)) {
48
+ const layer = matched.pathAndMethod.find(function (layer) {
49
+ return layer && layer.opts && layer.opts.end === true;
50
+ });
51
+
52
+ var path = layer && layer.path;
53
+ if (typeof path === 'string') {
54
+ var name = method + ' ' + path;
55
+ agent._instrumentation.setDefaultTransactionName(name);
56
+ } else {
57
+ agent.logger.debug(
58
+ 'unexpected path type in koa-router prototype.match: %s',
59
+ typeof path,
60
+ );
61
+ }
62
+ } else {
63
+ agent.logger.debug(
64
+ 'unexpected match result in koa-router prototype.match: %s',
65
+ typeof matched,
66
+ );
67
+ }
68
+
69
+ return matched;
70
+ };
71
+ });
72
+
73
+ return Router;
74
+ };
@@ -0,0 +1,15 @@
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
+ module.exports = function (koa, agent, { version, enabled }) {
10
+ if (!enabled) return koa;
11
+
12
+ agent.setFramework({ name: 'koa', version, overwrite: false });
13
+
14
+ return koa;
15
+ };