@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,52 @@
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 shimmer = require('../shimmer');
12
+
13
+ module.exports = function (restify, agent, { version, enabled }) {
14
+ if (!enabled) {
15
+ return restify;
16
+ }
17
+ if (!semver.satisfies(version, '>=5.2.0 <12.0.0')) {
18
+ agent.logger.debug('restify version %s not supported, skipping', version);
19
+ return restify;
20
+ }
21
+
22
+ agent.setFramework({ name: 'restify', version, overwrite: false });
23
+
24
+ function patchServer(server) {
25
+ if (semver.gte(version, '7.0.0')) {
26
+ shimmer.wrap(server, '_onHandlerError', function (orig) {
27
+ return function _wrappedOnHandlerError(err, req, res, isUncaught) {
28
+ if (err)
29
+ agent.captureError(err, { request: req, handled: !isUncaught });
30
+ return orig.apply(this, arguments);
31
+ };
32
+ });
33
+ } else {
34
+ shimmer.wrap(server, '_emitErrorEvents', function (orig) {
35
+ return function _wrappedOnHandlerError(req, res, route, err, cb) {
36
+ if (err) agent.captureError(err, { request: req });
37
+ return orig.apply(this, arguments);
38
+ };
39
+ });
40
+ }
41
+ }
42
+
43
+ shimmer.wrap(restify, 'createServer', function (fn) {
44
+ return function wrappedCreateServer() {
45
+ const server = fn.apply(this, arguments);
46
+ patchServer(server);
47
+ return server;
48
+ };
49
+ });
50
+
51
+ return restify;
52
+ };
@@ -0,0 +1,159 @@
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 clone = require('shallow-clone-shim');
11
+ var sqlSummary = require('sql-summary');
12
+
13
+ var { getDBDestination } = require('../context');
14
+
15
+ module.exports = function (tedious, agent, { version, enabled }) {
16
+ if (!enabled) return tedious;
17
+ if (
18
+ semver.satisfies(version, '>=19') &&
19
+ !semver.satisfies(process.version, '>=18.17')
20
+ ) {
21
+ agent.logger.debug(
22
+ 'tedious version %s not supported for node %s - aborting...',
23
+ version,
24
+ process.version,
25
+ );
26
+ return tedious;
27
+ }
28
+ if (version === '4.0.0' || !semver.satisfies(version, '>=1.9.0 <20')) {
29
+ agent.logger.debug(
30
+ 'tedious version %s not supported - aborting...',
31
+ version,
32
+ );
33
+ return tedious;
34
+ }
35
+
36
+ const ins = agent._instrumentation;
37
+
38
+ return clone({}, tedious, {
39
+ Connection(descriptor) {
40
+ const getter = descriptor.get;
41
+ if (getter) {
42
+ // tedious v6.5.0+
43
+ descriptor.get = function get() {
44
+ return wrapConnection(getter());
45
+ };
46
+ } else if (typeof descriptor.value === 'function') {
47
+ descriptor.value = wrapConnection(descriptor.value);
48
+ } else {
49
+ agent.logger.debug(
50
+ 'could not patch `tedious.Connection` property for tedious version %s - aborting...',
51
+ version,
52
+ );
53
+ }
54
+ return descriptor;
55
+ },
56
+ Request(descriptor) {
57
+ const getter = descriptor.get;
58
+ if (getter) {
59
+ // tedious v6.5.0+
60
+ descriptor.get = function get() {
61
+ return wrapRequest(getter());
62
+ };
63
+ } else if (typeof descriptor.value === 'function') {
64
+ descriptor.value = wrapRequest(descriptor.value);
65
+ } else {
66
+ agent.logger.debug(
67
+ 'could not patch `tedious.Request` property for tedious version %s - aborting...',
68
+ version,
69
+ );
70
+ }
71
+ return descriptor;
72
+ },
73
+ });
74
+
75
+ function wrapRequest(OriginalRequest) {
76
+ class Request extends OriginalRequest {
77
+ constructor() {
78
+ super(...arguments);
79
+ ins.bindEmitter(this);
80
+ }
81
+ }
82
+
83
+ return Request;
84
+ }
85
+
86
+ function wrapConnection(OriginalConnection) {
87
+ class Connection extends OriginalConnection {
88
+ constructor() {
89
+ super(...arguments);
90
+ ins.bindEmitter(this);
91
+ }
92
+
93
+ makeRequest(request, _packetType, payload) {
94
+ // if not a Request object (i.e. a BulkLoad), then bail
95
+ if (!request.parametersByName) {
96
+ return super.makeRequest(...arguments);
97
+ }
98
+ const span = ins.createSpan(null, 'db', 'mssql', 'query', {
99
+ exitSpan: true,
100
+ });
101
+ if (!span) {
102
+ return super.makeRequest(...arguments);
103
+ }
104
+
105
+ let host, port, instanceName;
106
+ if (typeof this.config === 'object') {
107
+ // http://tediousjs.github.io/tedious/api-connection.html#function_newConnection
108
+ host = this.config.server;
109
+ if (this.config.options) {
110
+ port = this.config.options.port;
111
+ instanceName = this.config.options.instanceName;
112
+ }
113
+ }
114
+ span._setDestinationContext(getDBDestination(host, port));
115
+
116
+ let sql;
117
+ let preparing;
118
+ if (payload.parameters !== undefined) {
119
+ // This looks for tedious instance with `RpcRequestPayload` started
120
+ // since version >=v11.0.10, when RPC parameter handling was refactored
121
+ // (https://github.com/tediousjs/tedious/pull/1275).
122
+ preparing =
123
+ typeof payload.procedure === 'number'
124
+ ? // tedious@16.2.0 starts using stored procedure *IDs*
125
+ // (https://github.com/tediousjs/tedious/pull/1327)
126
+ payload.procedure === 11
127
+ : payload.procedure === 'sp_prepare';
128
+ const stmtParam =
129
+ payload.parameters.find(({ name }) => name === 'statement') ||
130
+ payload.parameters.find(({ name }) => name === 'stmt');
131
+ sql = stmtParam ? stmtParam.value : request.sqlTextOrProcedure;
132
+ } else {
133
+ preparing = request.sqlTextOrProcedure === 'sp_prepare';
134
+ const params = request.parametersByName;
135
+ sql = (params.statement || params.stmt || {}).value;
136
+ }
137
+ span.name = sqlSummary(sql) + (preparing ? ' (prepare)' : '');
138
+ const dbContext = { type: 'sql', statement: sql };
139
+ if (instanceName) {
140
+ dbContext.instance = instanceName;
141
+ }
142
+ span.setDbContext(dbContext);
143
+
144
+ const origCallback = request.userCallback;
145
+ request.userCallback = ins.bindFunction(function tracedCallback() {
146
+ // TODO: captureError and setOutcome on err first arg here
147
+ span.end();
148
+ if (origCallback) {
149
+ return origCallback.apply(this, arguments);
150
+ }
151
+ });
152
+
153
+ return super.makeRequest(...arguments);
154
+ }
155
+ }
156
+
157
+ return Connection;
158
+ }
159
+ };
@@ -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
+ };