@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,270 @@
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
+ // Instrument the undici module.
10
+ //
11
+ // This uses undici's diagnostics_channel support for instrumentation.
12
+ // https://github.com/nodejs/undici/blob/main/docs/api/DiagnosticsChannel.md
13
+ // Undici is also used for Node >=v18.0.0's `fetch()` implementation, via
14
+ // an esbuild bundle. This instrumentation is enabled if either `global.fetch`
15
+ // is present or `require('undici')`.
16
+ //
17
+ // Limitations:
18
+ // - Currently this isn't subscribing to 'undici:client:...' messages.
19
+ // With typical undici usage a connection will only be initiated for a
20
+ // request. However, if a user manually does `client.connect(...)` then it is
21
+ // possible for this instrumentation to miss a connection error from
22
+ // 'undici:client:connectError'. It would eventually be nice to heuristically
23
+ // add 'connect' spans as children of request spans.
24
+ // - This doesn't instrument HTTP CONNECT, as exposed by `undici.connect(...)`.
25
+ // I don't think the current undici diagnostics_channel messages provide a
26
+ // way to watch the completion of the CONNECT request.
27
+ // - This hasn't been tested with `undici.upgrade()`.
28
+ //
29
+ // Some notes on if/when we want to collect some HTTP client metrics:
30
+ // - The time between 'undici:client:connected' and 'undici:client:sendHeaders'
31
+ // could be a measure of client-side latency. I'm not sure if client-side
32
+ // queueing of requests would show a time gap here.
33
+ // - The time between 'undici:client:sendHeaders' and 'undici:client:bodySent'
34
+ // might be interesting for large bodies, or perhaps for streaming requests.
35
+ // - The time between 'undici:client:bodySent' and 'undici:request:headers'
36
+ // could be a measure of response TTFB latency.
37
+
38
+ let diagch = null;
39
+ try {
40
+ diagch = require('diagnostics_channel');
41
+ } catch (_importErr) {
42
+ // pass
43
+ }
44
+
45
+ const semver = require('semver');
46
+
47
+ // Search an undici@5 request.headers string for a 'traceparent' header.
48
+ const headersStrHasTraceparentRe = /^traceparent:/im;
49
+
50
+ let isInstrumented = false;
51
+ let spanFromReq = null;
52
+ let chans = null;
53
+
54
+ // Get the content-length from undici response headers.
55
+ // `headers` is an Array of buffers: [k, v, k, v, ...].
56
+ // If the header is not present, or has an invalid value, this returns null.
57
+ function contentLengthFromResponseHeaders(headers) {
58
+ const name = 'content-length';
59
+ for (let i = 0; i < headers.length; i += 2) {
60
+ const k = headers[i];
61
+ if (k.length === name.length && k.toString().toLowerCase() === name) {
62
+ const v = Number(headers[i + 1]);
63
+ if (!isNaN(v)) {
64
+ return v;
65
+ } else {
66
+ return null;
67
+ }
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+
73
+ function uninstrumentUndici() {
74
+ if (!isInstrumented) {
75
+ return;
76
+ }
77
+ isInstrumented = false;
78
+
79
+ spanFromReq = null;
80
+ chans.forEach(({ chan, onMessage }) => {
81
+ chan.unsubscribe(onMessage);
82
+ });
83
+ chans = null;
84
+ }
85
+
86
+ /**
87
+ * Setup instrumentation for undici. The instrumentation is based entirely on
88
+ * diagnostics_channel usage, so no reference to the loaded undici module is
89
+ * required.
90
+ */
91
+ function instrumentUndici(agent) {
92
+ if (isInstrumented) {
93
+ return;
94
+ }
95
+ isInstrumented = true;
96
+
97
+ const ins = agent._instrumentation;
98
+ spanFromReq = new WeakMap();
99
+
100
+ // Keep ref to avoid https://github.com/nodejs/node/issues/42170 bug and for
101
+ // unsubscribing.
102
+ chans = [];
103
+ function diagchSub(name, onMessage) {
104
+ const chan = diagch.channel(name);
105
+ chan.subscribe(onMessage);
106
+ chans.push({
107
+ name,
108
+ chan,
109
+ onMessage,
110
+ });
111
+ }
112
+
113
+ diagchSub('undici:request:create', ({ request }) => {
114
+ // We do not handle instrumenting HTTP CONNECT. See limitation notes above.
115
+ if (request.method === 'CONNECT') {
116
+ return;
117
+ }
118
+
119
+ const url = new URL(request.origin);
120
+ const span = ins.createSpan(
121
+ `${request.method} ${url.host}`,
122
+ 'external',
123
+ 'http',
124
+ request.method,
125
+ { exitSpan: true },
126
+ );
127
+
128
+ // W3C trace-context propagation.
129
+ // If the span is null (e.g. hit `transactionMaxSpans`, unsampled
130
+ // transaction), then fallback to the current run context's span or
131
+ // transaction, if any.
132
+ const parentRunContext = ins.currRunContext();
133
+ const propSpan =
134
+ span || parentRunContext.currSpan() || parentRunContext.currTransaction();
135
+ if (propSpan) {
136
+ // Guard against adding a duplicate 'traceparent' header, because that
137
+ // breaks ES. https://github.com/elastic/apm-agent-nodejs/issues/3964
138
+ // Dev Note: This cheats a little and assumes the header names to add
139
+ // will include 'traceparent'.
140
+ let alreadyHasTp = false;
141
+ if (Array.isArray(request.headers)) {
142
+ // undici@6
143
+ for (let i = 0; i < request.headers.length; i += 2) {
144
+ if (request.headers[i].toLowerCase() === 'traceparent') {
145
+ alreadyHasTp = true;
146
+ break;
147
+ }
148
+ }
149
+ } else if (typeof request.headers === 'string') {
150
+ // undici@5
151
+ alreadyHasTp = headersStrHasTraceparentRe.test(request.headers);
152
+ }
153
+
154
+ if (!alreadyHasTp) {
155
+ propSpan.propagateTraceContextHeaders(
156
+ request,
157
+ function (req, name, value) {
158
+ if (typeof request.addHeader === 'function') {
159
+ req.addHeader(name, value);
160
+ } else if (Array.isArray(request.headers)) {
161
+ // undici@6.11.0 accidentally, briefly removed `request.addHeader()`.
162
+ req.headers.push(name, value);
163
+ }
164
+ },
165
+ );
166
+ }
167
+ }
168
+
169
+ if (span) {
170
+ spanFromReq.set(request, span);
171
+
172
+ // Set some initial HTTP context, in case the request errors out before a response.
173
+ span.setHttpContext({
174
+ method: request.method,
175
+ url: request.origin + request.path,
176
+ });
177
+
178
+ const destContext = {
179
+ address: url.hostname,
180
+ };
181
+ const port =
182
+ Number(url.port) ||
183
+ (url.protocol === 'https:' && 443) ||
184
+ (url.protocol === 'http:' && 80);
185
+ if (port) {
186
+ destContext.port = port;
187
+ }
188
+ span._setDestinationContext(destContext);
189
+ }
190
+ });
191
+
192
+ diagchSub('undici:request:headers', ({ request, response }) => {
193
+ const span = spanFromReq.get(request);
194
+ if (span !== undefined) {
195
+ // We are currently *not* capturing response headers, even though the
196
+ // intake API does allow it, because none of the other `setHttpContext`
197
+ // uses currently do.
198
+
199
+ const httpContext = {
200
+ method: request.method,
201
+ status_code: response.statusCode,
202
+ url: request.origin + request.path,
203
+ };
204
+ const cLen = contentLengthFromResponseHeaders(response.headers);
205
+ if (cLen !== null) {
206
+ httpContext.response = { encoded_body_size: cLen };
207
+ }
208
+ span.setHttpContext(httpContext);
209
+
210
+ span._setOutcomeFromHttpStatusCode(response.statusCode);
211
+ }
212
+ });
213
+
214
+ diagchSub('undici:request:trailers', ({ request }) => {
215
+ const span = spanFromReq.get(request);
216
+ if (span !== undefined) {
217
+ span.end();
218
+ spanFromReq.delete(request);
219
+ }
220
+ });
221
+
222
+ diagchSub('undici:request:error', ({ request, error }) => {
223
+ const span = spanFromReq.get(request);
224
+ const errOpts = {};
225
+ if (span !== undefined) {
226
+ errOpts.parent = span;
227
+ // Cases where we won't have an undici parent span:
228
+ // - We've hit transactionMaxSpans.
229
+ // - The undici HTTP span was suppressed because it is a child of an
230
+ // exit span (e.g. when used as the transport for the Elasticsearch
231
+ // client).
232
+ // It might be debatable whether we want to capture the error in the
233
+ // latter case. This could be revisited later.
234
+ }
235
+ agent.captureError(error, errOpts);
236
+ if (span !== undefined) {
237
+ span.end();
238
+ spanFromReq.delete(request);
239
+ }
240
+ });
241
+ }
242
+
243
+ function shimUndici(undici, agent, { version, enabled }) {
244
+ if (!enabled) {
245
+ return undici;
246
+ }
247
+ if (semver.lt(version, '4.7.1')) {
248
+ // Undici added its diagnostics_channel messages in v4.7.0. In v4.7.1 the
249
+ // `request.origin` property, that we need, was added.
250
+ agent.logger.debug(
251
+ 'cannot instrument undici: undici version %s is not supported',
252
+ version,
253
+ );
254
+ return undici;
255
+ }
256
+ if (!diagch) {
257
+ agent.logger.debug(
258
+ 'cannot instrument undici: there is no "diagnostics_channel" module',
259
+ process.version,
260
+ );
261
+ return undici;
262
+ }
263
+
264
+ instrumentUndici(agent);
265
+ return undici;
266
+ }
267
+
268
+ module.exports = shimUndici;
269
+ module.exports.instrumentUndici = instrumentUndici;
270
+ module.exports.uninstrumentUndici = uninstrumentUndici;
@@ -0,0 +1,59 @@
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 (ws, agent, { version, enabled }) {
14
+ if (!enabled) return ws;
15
+ if (!semver.satisfies(version, '>=1 <8')) {
16
+ agent.logger.debug('ws version %s not supported - aborting...', version);
17
+ return ws;
18
+ }
19
+
20
+ const ins = agent._instrumentation;
21
+
22
+ agent.logger.debug('shimming ws.prototype.send function');
23
+ shimmer.wrap(ws.prototype, 'send', wrapSend);
24
+
25
+ return ws;
26
+
27
+ function wrapSend(orig) {
28
+ return function wrappedSend() {
29
+ agent.logger.debug('intercepted call to ws.prototype.send');
30
+ const span = ins.createSpan(
31
+ 'Send WebSocket Message',
32
+ 'websocket',
33
+ 'send',
34
+ { exitSpan: true },
35
+ );
36
+ if (!span) {
37
+ return orig.apply(this, arguments);
38
+ }
39
+
40
+ const args = Array.prototype.slice.call(arguments);
41
+ let cb = args[args.length - 1];
42
+ const onDone = function () {
43
+ span.end();
44
+ if (cb) {
45
+ cb.apply(this, arguments);
46
+ }
47
+ };
48
+ if (typeof cb === 'function') {
49
+ args[args.length - 1] = ins.bindFunction(onDone);
50
+ } else {
51
+ cb = null;
52
+ args.push(ins.bindFunction(onDone));
53
+ }
54
+
55
+ const spanRunContext = ins.currRunContext().enterSpan(span);
56
+ return ins.withRunContext(spanRunContext, orig, this, ...args);
57
+ };
58
+ }
59
+ };
@@ -0,0 +1,81 @@
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 constants = require('../constants');
10
+
11
+ const NOOP_TRANSACTION_ID = '0000000000000000';
12
+ const NOOP_TRACEID = '00000000000000000000000000000000';
13
+ const NOOP_TRACEPARENT = '00-00000000000000000000000000000-0000000000000000-00';
14
+
15
+ /**
16
+ * A do-nothing Transaction object.
17
+ * https://www.elastic.co/guide/en/apm/agent/nodejs/current/transaction-api.html
18
+ */
19
+ class NoopTransaction {
20
+ constructor() {
21
+ this.name = 'unnamed';
22
+ this.type = 'noop';
23
+ this.traceparent = NOOP_TRACEPARENT;
24
+ this.result = constants.RESULT_SUCCESS;
25
+ this.outcome = constants.OUTCOME_UNKNOWN;
26
+ this.ids = {
27
+ 'trace.id': NOOP_TRACEID,
28
+ 'transaction.id': NOOP_TRANSACTION_ID,
29
+ };
30
+
31
+ // Unofficial properties mentioned in a comment in index.d.ts.
32
+ this.timestamp = Date.now();
33
+ this.id = NOOP_TRANSACTION_ID;
34
+ this.traceId = NOOP_TRACEID;
35
+ this.sampled = false;
36
+ this.ended = false;
37
+ }
38
+
39
+ // Public methods.
40
+ setType() {}
41
+ setLabel() {
42
+ return true;
43
+ }
44
+ addLabels() {
45
+ return true;
46
+ }
47
+ setOutcome() {}
48
+ startSpan() {
49
+ return null;
50
+ }
51
+ end() {}
52
+ ensureParentId() {
53
+ return NOOP_TRANSACTION_ID;
54
+ }
55
+ toString() {
56
+ return `Transaction(${this.id}, '${this.name}'${
57
+ this.ended ? ', ended' : ''
58
+ })`;
59
+ }
60
+
61
+ // Non-public methods mentioned in a comment in index.d.ts.
62
+ setUserContext() {}
63
+ setCustomContext() {}
64
+ setDefaultName() {}
65
+ setDefaultNameFromRequest() {}
66
+ toJSON() {
67
+ return {};
68
+ }
69
+ duration() {
70
+ return 0;
71
+ }
72
+ setFaas() {}
73
+ setMessageContext() {}
74
+ setServiceContext() {}
75
+ setCloudContext() {}
76
+ _setOutcomeFromHttpStatusCode() {}
77
+ }
78
+
79
+ module.exports = {
80
+ NoopTransaction,
81
+ };
@@ -0,0 +1,215 @@
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 { RunContext } = require('./RunContext');
10
+
11
+ const ADD_LISTENER_METHODS = [
12
+ 'addListener',
13
+ 'on',
14
+ 'once',
15
+ 'prependListener',
16
+ 'prependOnceListener',
17
+ ];
18
+
19
+ // An abstract base RunContextManager class that implements the following
20
+ // methods that all run context manager implementations can share:
21
+ // root()
22
+ // bindFn(runContext, target)
23
+ // bindEE(runContext, eventEmitter)
24
+ // isEEBound(eventEmitter)
25
+ // and stubs out the remaining public methods of the RunContextManager
26
+ // interface.
27
+ //
28
+ // (This class has largerly the same API as @opentelemetry/api `ContextManager`.
29
+ // The implementation is adapted from
30
+ // https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-context-async-hooks/src/AbstractAsyncHooksContextManager.ts)
31
+ class AbstractRunContextManager {
32
+ constructor(log, runContextClass = RunContext) {
33
+ this._log = log;
34
+ this._kListeners = Symbol('ElasticListeners');
35
+ // eslint-disable-next-line new-cap
36
+ this._root = new runContextClass();
37
+ }
38
+
39
+ // Get the root run context. This is always empty (no current trans or span).
40
+ //
41
+ // This is the equivalent of OTel JS API's `ROOT_CONTEXT` constant. Ours
42
+ // is not a top-level constant, because the RunContext class can be
43
+ // overriden.
44
+ root() {
45
+ return this._root;
46
+ }
47
+
48
+ enable() {
49
+ throw new Error('abstract method not implemented');
50
+ }
51
+
52
+ disable() {
53
+ throw new Error('abstract method not implemented');
54
+ }
55
+
56
+ // Reset state for re-use of this context manager by tests in the same process.
57
+ testReset() {
58
+ this.disable();
59
+ this.enable();
60
+ }
61
+
62
+ active() {
63
+ throw new Error('abstract method not implemented');
64
+ }
65
+
66
+ with(runContext, fn, thisArg, ...args) {
67
+ throw new Error('abstract method not implemented');
68
+ }
69
+
70
+ supersedeRunContext(runContext) {
71
+ throw new Error('abstract method not implemented');
72
+ }
73
+
74
+ // The OTel ContextManager API has a single .bind() like this:
75
+ //
76
+ // bind (runContext, target) {
77
+ // if (target instanceof EventEmitter) {
78
+ // return this._bindEventEmitter(runContext, target)
79
+ // }
80
+ // if (typeof target === 'function') {
81
+ // return this._bindFunction(runContext, target)
82
+ // }
83
+ // return target
84
+ // }
85
+ //
86
+ // Is there any value in this over our two separate `.bind*` methods?
87
+
88
+ bindFn(runContext, target) {
89
+ if (typeof target !== 'function') {
90
+ return target;
91
+ }
92
+ // this._log.trace('bind %s to fn "%s"', runContext, target.name)
93
+
94
+ const self = this;
95
+ const wrapper = function () {
96
+ return self.with(runContext, () => target.apply(this, arguments));
97
+ };
98
+ Object.defineProperty(wrapper, 'length', {
99
+ enumerable: false,
100
+ configurable: true,
101
+ writable: false,
102
+ value: target.length,
103
+ });
104
+
105
+ return wrapper;
106
+ }
107
+
108
+ // (This implementation is adapted from OTel's `_bindEventEmitter`.)
109
+ bindEE(runContext, ee) {
110
+ // Explicitly do *not* guard with `ee instanceof EventEmitter`. The
111
+ // `Request` object from the aws-sdk@2 module, for example, has an `on`
112
+ // with the EventEmitter API that we want to bind, but it is not otherwise
113
+ // an EventEmitter.
114
+
115
+ const map = this._getPatchMap(ee);
116
+ if (map !== undefined) {
117
+ // No double-binding.
118
+ return ee;
119
+ }
120
+ this._createPatchMap(ee);
121
+
122
+ // patch methods that add a listener to propagate context
123
+ ADD_LISTENER_METHODS.forEach((methodName) => {
124
+ if (ee[methodName] === undefined) return;
125
+ ee[methodName] = this._patchAddListener(ee, ee[methodName], runContext);
126
+ });
127
+ // patch methods that remove a listener
128
+ if (typeof ee.removeListener === 'function') {
129
+ ee.removeListener = this._patchRemoveListener(ee, ee.removeListener);
130
+ }
131
+ if (typeof ee.off === 'function') {
132
+ ee.off = this._patchRemoveListener(ee, ee.off);
133
+ }
134
+ // patch method that remove all listeners
135
+ if (typeof ee.removeAllListeners === 'function') {
136
+ ee.removeAllListeners = this._patchRemoveAllListeners(
137
+ ee,
138
+ ee.removeAllListeners,
139
+ );
140
+ }
141
+ return ee;
142
+ }
143
+
144
+ // Return true iff the given EventEmitter is already bound to a run context.
145
+ isEEBound(ee) {
146
+ return this._getPatchMap(ee) !== undefined;
147
+ }
148
+
149
+ // Patch methods that remove a given listener so that we match the "patched"
150
+ // version of that listener (the one that propagate context).
151
+ _patchRemoveListener(ee, original) {
152
+ const contextManager = this;
153
+ return function (event, listener) {
154
+ const map = contextManager._getPatchMap(ee);
155
+ const listeners = map && map[event];
156
+ if (listeners === undefined) {
157
+ return original.call(this, event, listener);
158
+ }
159
+ const patchedListener = listeners.get(listener);
160
+ return original.call(this, event, patchedListener || listener);
161
+ };
162
+ }
163
+
164
+ // Patch methods that remove all listeners so we remove our internal
165
+ // references for a given event.
166
+ _patchRemoveAllListeners(ee, original) {
167
+ const contextManager = this;
168
+ return function (event) {
169
+ const map = contextManager._getPatchMap(ee);
170
+ if (map !== undefined) {
171
+ if (arguments.length === 0) {
172
+ contextManager._createPatchMap(ee);
173
+ } else if (map[event] !== undefined) {
174
+ delete map[event];
175
+ }
176
+ }
177
+ return original.apply(this, arguments);
178
+ };
179
+ }
180
+
181
+ // Patch methods on an event emitter instance that can add listeners so we
182
+ // can force them to propagate a given context.
183
+ _patchAddListener(ee, original, runContext) {
184
+ const contextManager = this;
185
+ return function (event, listener) {
186
+ let map = contextManager._getPatchMap(ee);
187
+ if (map === undefined) {
188
+ map = contextManager._createPatchMap(ee);
189
+ }
190
+ let listeners = map[event];
191
+ if (listeners === undefined) {
192
+ listeners = new WeakMap();
193
+ map[event] = listeners;
194
+ }
195
+ const patchedListener = contextManager.bindFn(runContext, listener);
196
+ // store a weak reference of the user listener to ours
197
+ listeners.set(listener, patchedListener);
198
+ return original.call(this, event, patchedListener);
199
+ };
200
+ }
201
+
202
+ _createPatchMap(ee) {
203
+ const map = Object.create(null);
204
+ ee[this._kListeners] = map;
205
+ return map;
206
+ }
207
+
208
+ _getPatchMap(ee) {
209
+ return ee[this._kListeners];
210
+ }
211
+ }
212
+
213
+ module.exports = {
214
+ AbstractRunContextManager,
215
+ };