@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,537 @@
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
+ // Handling for translating `<Error instance>.stack` to the
10
+ // `{error.exception,span}.stacktrace` object passed to APM server.
11
+ // https://github.com/elastic/apm-server/blob/master/docs/spec/v2/error.json#L633
12
+ //
13
+ // A lot of this derived from https://github.com/watson/stackman but was
14
+ // moved internal to allow breaking compat for perf work.
15
+
16
+ var fsPromises = require('fs/promises');
17
+ var path = require('path');
18
+ var { promisify } = require('util');
19
+ const { fileURLToPath } = require('url');
20
+
21
+ // avoid loading error-callsites until needed to avoid
22
+ // Error.prepareStackTrace side-effects
23
+ // https://github.com/elastic/apm-agent-nodejs/issues/2833
24
+ let errorCallsites;
25
+ function initStackTraceCollection() {
26
+ errorCallsites = require('error-callsites');
27
+ }
28
+ const errorStackParser = require('error-stack-parser');
29
+ const loadSourceMap = require('./load-source-map');
30
+ const { LRUCache } = require('lru-cache');
31
+
32
+ const fileCache = new LRUCache({
33
+ max: 500, // fileCacheMax
34
+ fetchMethod: async (file, _staleValue, { signal }) => {
35
+ // Note: We *could* pass `signal` to fsPromies.readFile options.
36
+ const data = await fsPromises.readFile(file, { encoding: 'utf8' });
37
+ if (signal.aborted) {
38
+ return;
39
+ }
40
+ return data.split(/\r?\n/);
41
+ },
42
+ });
43
+
44
+ const sourceMapCache = new LRUCache({
45
+ max: 500, // sourceMapCacheMax
46
+ fetchMethod: async (filename, _staleValue, { signal }) => {
47
+ if (!filename) {
48
+ return null;
49
+ }
50
+
51
+ const sourcemap = await promisify(loadSourceMap)(filename);
52
+ if (signal.aborted) {
53
+ return;
54
+ }
55
+ // Translate sourcemap===undefined to null, because lru-cache
56
+ // treats `undefined` as a cache miss. Without this there is no
57
+ // caching for files that have no sourcemap (the common case).
58
+ return sourcemap || null;
59
+ },
60
+ dispose: function (sourcemap, _filename, _reason) {
61
+ if (sourcemap) {
62
+ sourcemap.destroy();
63
+ }
64
+ },
65
+ });
66
+
67
+ const frameCache = new LRUCache({ max: 1000 });
68
+ const frameCacheStats = {
69
+ hits: 0,
70
+ misses: 0,
71
+ };
72
+
73
+ let lastCwd = process.cwd();
74
+
75
+ // ---- internal support functions
76
+
77
+ // process.cwd() can throw EMFILE if hitting the `ulimit -Hn`. A process's
78
+ // cwd should not change regularly, so falling back to the previous one is fine.
79
+ // At worst the stacktrace frame.filename values will be relative to an old
80
+ // directory.
81
+ function getCwd(log) {
82
+ let cwd;
83
+ try {
84
+ cwd = process.cwd();
85
+ } catch (ex) {
86
+ log.trace(ex, 'error getting cwd: fallback back to %s', lastCwd);
87
+ return lastCwd;
88
+ }
89
+ lastCwd = cwd;
90
+ return cwd;
91
+ }
92
+
93
+ // "filePath" refers to frame.fileName(), but with the possible "file://..."
94
+ // URL converted to a local path. An callsite in an ES module will have a
95
+ // file URL for the `fileName`.
96
+ //
97
+ // This just relies on `callsite.getFileName() -> <string | null | undefined>`
98
+ // so it works with CallSite or StackFrames (from `error-stack-parser`).
99
+ function filePathFromCallSite(callsite) {
100
+ let filePath = callsite.getFileName();
101
+ if (filePath && filePath.startsWith('file://')) {
102
+ filePath = fileURLToPath(filePath);
103
+ }
104
+ return filePath;
105
+ }
106
+
107
+ // If gathering a stacktrace from the structured CallSites fails, this is
108
+ // used as a fallback: parsing the `err.stack` *string*.
109
+ function stackTraceFromErrStackString(log, err) {
110
+ const stacktrace = [];
111
+
112
+ // graphqljs adds the `originalError` property which represents the original
113
+ // error thrown within the resolver
114
+ err = err.originalError || err;
115
+
116
+ if (err.stack === null) {
117
+ return [];
118
+ }
119
+
120
+ // frames is an array of StackFrame (https://github.com/stacktracejs/stackframe).
121
+ let frames = null;
122
+ try {
123
+ frames = errorStackParser.parse(err);
124
+ } catch (parseErr) {
125
+ log.debug('could not parse err.stack string: %s', parseErr);
126
+ }
127
+
128
+ if (frames) {
129
+ const cwd = getCwd(log);
130
+ for (var i = 0; i < frames.length; i++) {
131
+ const frame = frames[i];
132
+ const filename = filePathFromCallSite(frame) || '';
133
+ stacktrace.push({
134
+ filename: getRelativeFileName(filename, cwd),
135
+ function: frame.getFunctionName(),
136
+ lineno: frame.getLineNumber(),
137
+ library_frame: !isStackFrameApp(frame),
138
+ abs_path: filename,
139
+ });
140
+ }
141
+ }
142
+
143
+ return stacktrace;
144
+ }
145
+
146
+ const NODE_MODULES_PATH_SEG = 'node_modules' + path.sep;
147
+
148
+ // Return true iff the given StackFrame
149
+ // (https://github.com/stacktracejs/stackframe) is an application frame.
150
+ function isStackFrameApp(stackframe) {
151
+ if (isStackFrameNode(stackframe)) {
152
+ return false;
153
+ } else {
154
+ const fileName = filePathFromCallSite(stackframe);
155
+ if (!fileName) {
156
+ return true;
157
+ } else if (fileName.indexOf(NODE_MODULES_PATH_SEG) === -1) {
158
+ return true;
159
+ } else {
160
+ return false;
161
+ }
162
+ }
163
+ }
164
+
165
+ // Return true iff the given StackFrame
166
+ // (https://github.com/stacktracejs/stackframe) is a Node frame.
167
+ function isStackFrameNode(stackframe) {
168
+ if (stackframe.isNative) {
169
+ return true;
170
+ } else {
171
+ const fileName = filePathFromCallSite(stackframe);
172
+ if (!fileName) {
173
+ return true;
174
+ } else {
175
+ return !path.isAbsolute(fileName) && fileName[0] !== '.';
176
+ }
177
+ }
178
+ }
179
+
180
+ function isCallSiteApp(callsite) {
181
+ if (isCallSiteNode(callsite)) {
182
+ return false;
183
+ } else {
184
+ const fileName = filePathFromCallSite(callsite);
185
+ if (!fileName) {
186
+ return true;
187
+ } else if (fileName.indexOf(NODE_MODULES_PATH_SEG) === -1) {
188
+ return true;
189
+ } else {
190
+ return false;
191
+ }
192
+ }
193
+ }
194
+
195
+ function isCallSiteNode(callsite) {
196
+ if (callsite.isNative()) {
197
+ return true;
198
+ } else {
199
+ const fileName = filePathFromCallSite(callsite);
200
+ if (!fileName) {
201
+ return true;
202
+ } else {
203
+ return !path.isAbsolute(fileName) && fileName[0] !== '.';
204
+ }
205
+ }
206
+ }
207
+
208
+ function isValidCallsites(callsites) {
209
+ return (
210
+ Array.isArray(callsites) &&
211
+ callsites.length > 0 &&
212
+ typeof callsites[0] === 'object' &&
213
+ typeof callsites[0].getFileName === 'function'
214
+ );
215
+ }
216
+
217
+ // From stackman getTypeNameSafely().
218
+ function getCallSiteTypeNameSafely(callsite) {
219
+ try {
220
+ return callsite.getTypeName();
221
+ } catch (e) {
222
+ // This seems to happen sometimes when using 'use strict',
223
+ // stemming from `getTypeName`.
224
+ // [TypeError: Cannot read property 'constructor' of undefined]
225
+ return null;
226
+ }
227
+ }
228
+
229
+ // From stackman getFunctionNameSanitized().
230
+ function getCallSiteFunctionNameSanitized(callsite) {
231
+ var fnName = callsite.getFunctionName();
232
+ if (fnName) return fnName;
233
+ var typeName = getCallSiteTypeNameSafely(callsite);
234
+ if (typeName)
235
+ return typeName + '.' + (callsite.getMethodName() || '<anonymous>');
236
+ return '<anonymous>';
237
+ }
238
+
239
+ function addSourceContextToFrame(frame, lines, lineNum, n) {
240
+ var index = lineNum - 1; // lines 1-based -> index 0-based
241
+ var nBefore = Math.ceil((n - 1) / 2);
242
+ var nAfter = Math.floor((n - 1) / 2);
243
+ frame.pre_context = lines.slice(Math.max(0, index - nBefore), index);
244
+ frame.context_line = lines[index];
245
+ frame.post_context = lines.slice(index + 1, index + 1 + nAfter);
246
+ }
247
+
248
+ // Get the path of `filename` relative to `relTo` -- which should be a directory
249
+ // path *without* a trailing path separator.
250
+ function getRelativeFileName(filename, relTo) {
251
+ if (filename.startsWith(relTo + path.sep)) {
252
+ return filename.slice(relTo.length + 1);
253
+ } else {
254
+ return filename;
255
+ }
256
+ }
257
+
258
+ async function getSourceMapConsumer(callsite) {
259
+ if (isCallSiteNode(callsite)) {
260
+ return null;
261
+ } else {
262
+ var filename = filePathFromCallSite(callsite);
263
+ if (!filename) {
264
+ return null;
265
+ } else {
266
+ return sourceMapCache.fetch(filename);
267
+ }
268
+ }
269
+ }
270
+
271
+ // Put together an APM stacktrace frame object:
272
+ // {
273
+ // "filename": "...",
274
+ // "lineno": 65,
275
+ // "function": "...",
276
+ // "library_frame": <bool>,
277
+ // "abs_path": "...",
278
+ // "pre_context": [ ... ],
279
+ // "context_line": "...",
280
+ // "post_context": [ ... ],
281
+ // },
282
+ // from a v8 CallSite object
283
+ // (https://v8.dev/docs/stack-trace-api#customizing-stack-traces).
284
+ //
285
+ // This asynchronously returns the call frame object. Getting source context
286
+ // is best-effort, so the returned Promise never rejects.
287
+ async function frameFromCallSite(
288
+ log,
289
+ callsite,
290
+ cwd,
291
+ sourceLinesAppFrames,
292
+ sourceLinesLibraryFrames,
293
+ ) {
294
+ // getFileName can return null, e.g. with a `at Generator.next (<anonymous>)` frame.
295
+ const filename = filePathFromCallSite(callsite) || '';
296
+ const lineno = callsite.getLineNumber();
297
+ const colno = callsite.getColumnNumber();
298
+
299
+ // Caching
300
+ const cacheKey = [
301
+ filename,
302
+ lineno,
303
+ colno,
304
+ sourceLinesAppFrames,
305
+ sourceLinesLibraryFrames,
306
+ ].join(':');
307
+ const cachedFrame = frameCache.get(cacheKey);
308
+ if (cachedFrame !== undefined) {
309
+ frameCacheStats.hits++;
310
+
311
+ // Guard against later JSON serialization mistakenly changing duplicate
312
+ // frames in a stacktrace (a totally legal thing) into '[Circular]' as a
313
+ // guard against serializing an object with circular references.
314
+ const clonedFrame = Object.assign({}, cachedFrame);
315
+ if (clonedFrame.pre_context) {
316
+ clonedFrame.pre_context = clonedFrame.pre_context.slice();
317
+ }
318
+ if (clonedFrame.post_context) {
319
+ clonedFrame.post_context = clonedFrame.post_context.slice();
320
+ }
321
+
322
+ return clonedFrame;
323
+ }
324
+
325
+ function cacheIt(frame) {
326
+ frameCacheStats.misses++;
327
+ frameCache.set(cacheKey, frame);
328
+ }
329
+
330
+ let mappedFilename = null;
331
+ let absMappedFilename = null;
332
+ let mappedLineno = null;
333
+
334
+ // If the file has a sourcemap, we use that for: filename, lineno, source
335
+ // context.
336
+ let sourceMapConsumer = null;
337
+ try {
338
+ sourceMapConsumer = await getSourceMapConsumer(callsite);
339
+ } catch (sourceMapErr) {
340
+ log.debug(
341
+ { filename, err: sourceMapErr },
342
+ 'could not process file source map',
343
+ );
344
+ }
345
+ if (sourceMapConsumer) {
346
+ let pos;
347
+ try {
348
+ pos = sourceMapConsumer.originalPositionFor({
349
+ line: lineno,
350
+ column: colno,
351
+ });
352
+ } catch (posErr) {
353
+ log.debug(
354
+ { filename, line: lineno, err: posErr },
355
+ 'could not get position from sourcemap',
356
+ );
357
+ pos = {
358
+ source: null,
359
+ line: null,
360
+ column: null,
361
+ name: null,
362
+ };
363
+ }
364
+ if (pos.source !== null) {
365
+ mappedFilename = pos.source;
366
+ absMappedFilename = path.resolve(path.dirname(filename), mappedFilename);
367
+ }
368
+ if (pos.line !== null) {
369
+ mappedLineno = pos.line;
370
+ }
371
+ // TODO: Is `pos.name` relevant for `frame.function` if minifying?
372
+ }
373
+
374
+ const isApp = isCallSiteApp(callsite);
375
+ const frame = {
376
+ filename: getRelativeFileName(absMappedFilename || filename, cwd),
377
+ lineno: mappedLineno || lineno,
378
+ function: getCallSiteFunctionNameSanitized(callsite),
379
+ library_frame: !isApp,
380
+ };
381
+ if (!Number.isFinite(frame.lineno)) {
382
+ // An early comment in stackman suggested this is "sometimes not" an int.
383
+ frame.lineno = 0;
384
+ }
385
+ if (filename) {
386
+ frame.abs_path = absMappedFilename || filename;
387
+ }
388
+
389
+ // Finish early if we do not need to collect source lines of context.
390
+ var linesOfContext = isApp ? sourceLinesAppFrames : sourceLinesLibraryFrames;
391
+ if (linesOfContext === 0 || !filename || isCallSiteNode(callsite)) {
392
+ cacheIt(frame);
393
+ return frame;
394
+ }
395
+
396
+ // Attempt to use "sourcesContent" in a sourcemap, if available.
397
+ if (sourceMapConsumer && mappedFilename && mappedLineno) {
398
+ // To use `sourceMapConsumer.sourceContentFor` we need the filename as
399
+ // it is in the "sources" field of the source map. `mappedFilename`,
400
+ // from `sourceMapConsume.originalPositionFor` above, was made relative
401
+ // to "sourceRoot" -- sourceFilename undoes that.
402
+ const sourceFilename = sourceMapConsumer.sourceRoot
403
+ ? path.relative(sourceMapConsumer.sourceRoot, mappedFilename)
404
+ : mappedFilename;
405
+ var source = sourceMapConsumer.sourceContentFor(sourceFilename, true);
406
+ log.trace(
407
+ {
408
+ sourceRoot: sourceMapConsumer.sourceRoot,
409
+ mappedFilename,
410
+ sourceFilename,
411
+ haveSourceContent: !!source,
412
+ },
413
+ 'sourcemap sourceContent lookup',
414
+ );
415
+ if (source) {
416
+ addSourceContextToFrame(
417
+ frame,
418
+ source.split(/\r?\n/g),
419
+ mappedLineno,
420
+ linesOfContext,
421
+ );
422
+ cacheIt(frame);
423
+ return frame;
424
+ }
425
+ }
426
+
427
+ // If the file looks like it minimized (as we didn't have a source-map in
428
+ // the processing above), then skip adding source context because it
429
+ // is mostly useless and the typically 500-char lines result in over-large
430
+ // APM error objects.
431
+ if (filename.endsWith('.min.js')) {
432
+ cacheIt(frame);
433
+ return frame;
434
+ }
435
+
436
+ // Otherwise load the file from disk, if available.
437
+ let lines;
438
+ try {
439
+ lines = await fileCache.fetch(frame.abs_path);
440
+ } catch (fileErr) {
441
+ log.debug(
442
+ { filename: frame.abs_path, err: fileErr },
443
+ 'could not read file for source context',
444
+ );
445
+ }
446
+ if (lines) {
447
+ addSourceContextToFrame(frame, lines, frame.lineno, linesOfContext);
448
+ }
449
+
450
+ cacheIt(frame);
451
+ return frame;
452
+ }
453
+
454
+ // ---- exports
455
+
456
+ // Gather an APM `stacktrace` object from the given `err`.
457
+ // This stacktrace object is used for `error.exception.stacktrace`,
458
+ // `error.log.stacktrace`, or `span.stacktrace`.
459
+ //
460
+ // This is a best effort, so it never fails. It will log.debug any parsing
461
+ // failures.
462
+ //
463
+ // @param {Logger} log
464
+ // @param {Error|Object} err - Typically an Error instance, but can also be a
465
+ // plain object on which `Error.captureStackTrace(...)` has been called.
466
+ // @param {Integer} sourceLinesAppFrames - The number of source lines of
467
+ // context to include for frames in application code.
468
+ // @param {Integer} sourceLinesLibraryFrames - The number of source lines of
469
+ // context to include for frames in library code (i.e. under node_modules/)
470
+ // @param {Function} filterCallSite - Optional. A function to filter the
471
+ // CallSites to include in the stacktrace.
472
+ // @param {Function} cb - `cb(null, stacktrace)`
473
+ function gatherStackTrace(
474
+ log,
475
+ err,
476
+ sourceLinesAppFrames,
477
+ sourceLinesLibraryFrames,
478
+ filterCallSite,
479
+ cb,
480
+ ) {
481
+ // errorCallsites returns an array of v8 CallSite objects.
482
+ // https://v8.dev/docs/stack-trace-api#customizing-stack-traces
483
+ let callsites = errorCallsites ? errorCallsites(err) : null;
484
+
485
+ if (!isValidCallsites(callsites)) {
486
+ // When can this happen? Another competing Error.prepareStackTrace breaking
487
+ // error-callsites? Also initStackTraceCollection not having been called.
488
+ log.debug('could not get valid callsites from error "%s"', err);
489
+ // Fallback to parsing the err.stack string.
490
+ setImmediate(cb, null, stackTraceFromErrStackString(log, err));
491
+ return;
492
+ }
493
+
494
+ if (filterCallSite) {
495
+ callsites = callsites.filter(filterCallSite);
496
+ if (callsites.length === 0) {
497
+ // Note: I'm not sure using the fallback here is the intended behaviour,
498
+ // because it defeats the purpose of a `filterCallSite` that filters out
499
+ // all callsites. In an earlier implementation this fallback handling was
500
+ // lumped in with the `!isValidCallsites(...)` case. `filterCallSite` is
501
+ // only used for *span* stacktraces (off by default), so the priority here
502
+ // is low.
503
+ setImmediate(cb, null, stackTraceFromErrStackString(log, err));
504
+ return;
505
+ }
506
+ }
507
+
508
+ const cwd = getCwd(log);
509
+ const promises = callsites.map((callsite) =>
510
+ frameFromCallSite(
511
+ log,
512
+ callsite,
513
+ cwd,
514
+ sourceLinesAppFrames,
515
+ sourceLinesLibraryFrames,
516
+ ),
517
+ );
518
+
519
+ Promise.all(promises)
520
+ .then((stacktrace) => cb(null, stacktrace))
521
+ .catch((frameErr) => {
522
+ log.debug(
523
+ { err, frameErr },
524
+ 'error getting a stack frame from one or more callsites',
525
+ );
526
+ cb(null, stackTraceFromErrStackString(log, err)); // fallback
527
+ });
528
+ }
529
+
530
+ module.exports = {
531
+ gatherStackTrace,
532
+ frameCacheStats,
533
+ initStackTraceCollection,
534
+
535
+ // Exported for testing only.
536
+ stackTraceFromErrStackString,
537
+ };
package/lib/symbols.js ADDED
@@ -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
+ exports.unwrap = Symbol('ElasticAPMUnwrap');
10
+ exports.agentInitialized = Symbol('ElasticAPMAgentInitialized');
11
+ exports.knexStackObj = Symbol('ElasticAPMKnexStackObj');
12
+ exports.staticFile = Symbol('ElasticAPMStaticFile');
13
+ exports.expressMountStack = Symbol('ElasticAPMExpressMountStack');
14
+ exports.errorReportedSymbol = Symbol('ElasticAPMErrorReported');
15
+ exports.redisClientOptions = Symbol('ElasticAPMRedisClientOptions');
@@ -0,0 +1,118 @@
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
+ const { TraceParent } = require('./traceparent');
9
+ const TraceState = require('./tracestate');
10
+
11
+ class TraceContext {
12
+ constructor(traceparent, tracestate, conf = {}) {
13
+ this.traceparent = traceparent;
14
+ this.tracestate = tracestate;
15
+ this._conf = conf;
16
+ }
17
+
18
+ // Resume (a.k.a. continue) a trace if `childOf` includes trace-context.
19
+ // Otherwise, make a sampling decision and start new trace-context.
20
+ //
21
+ // @param { Transaction | Span | TraceParent | string | undefined } childOf
22
+ // If this is a Transaction, Span, or TraceParent instance, or a valid
23
+ // W3C trace-context 'traceparent' string, then the trace is continued.
24
+ // Otherwise a sampling decision made based on
25
+ // `conf.transactionSampleRate` and a new trace is started.
26
+ // @param { Object } conf - The agent configuration.
27
+ // @param { TraceState | string } tracestateArg - A TraceState instance or
28
+ // a W3C trace-context 'tracestate' string.
29
+ static startOrResume(childOf, conf, tracestateArg) {
30
+ if (childOf && childOf._context instanceof TraceContext)
31
+ return childOf._context.child();
32
+ const traceparent = TraceParent.startOrResume(childOf, conf);
33
+ const tracestate =
34
+ tracestateArg instanceof TraceState
35
+ ? tracestateArg
36
+ : TraceState.fromStringFormatString(tracestateArg);
37
+
38
+ // if a root transaction/span, set the initial sample rate in the tracestate
39
+
40
+ if (!childOf && traceparent.recorded) {
41
+ // if this is a sampled/recorded transaction, set the rate
42
+ tracestate.setValue('s', conf.transactionSampleRate);
43
+ } else if (!childOf) {
44
+ // if this is a un-sampled/unreocrded transaction, set the
45
+ // rate to zero, per the spec
46
+ //
47
+ // https://github.com/elastic/apm/blob/main/specs/agents/tracing-sampling.md
48
+ tracestate.setValue('s', 0);
49
+ }
50
+
51
+ return new TraceContext(traceparent, tracestate, conf);
52
+ }
53
+
54
+ static fromString(header) {
55
+ return TraceParent.fromString(header);
56
+ }
57
+
58
+ ensureParentId() {
59
+ return this.traceparent.ensureParentId();
60
+ }
61
+
62
+ child() {
63
+ const childTraceParent = this.traceparent.child();
64
+ const childTraceContext = new TraceContext(
65
+ childTraceParent,
66
+ this.tracestate,
67
+ this._conf,
68
+ );
69
+ return childTraceContext;
70
+ }
71
+
72
+ /**
73
+ * Returns traceparent string only
74
+ *
75
+ * @todo legacy -- can we remove to avoid confusion?
76
+ */
77
+ toString() {
78
+ return this.traceparent.toString();
79
+ }
80
+
81
+ toTraceStateString() {
82
+ return this.tracestate.toW3cString();
83
+ }
84
+
85
+ toTraceParentString() {
86
+ return this.traceparent.toString();
87
+ }
88
+
89
+ propagateTraceContextHeaders(carrier, setter) {
90
+ if (!carrier || !setter) {
91
+ return;
92
+ }
93
+ const traceparentStr = this.toTraceParentString();
94
+ const tracestateStr = this.toTraceStateString();
95
+ if (traceparentStr) {
96
+ setter(carrier, 'traceparent', traceparentStr);
97
+ if (this._conf.useElasticTraceparentHeader) {
98
+ setter(carrier, 'elastic-apm-traceparent', traceparentStr);
99
+ }
100
+ }
101
+
102
+ if (tracestateStr) {
103
+ setter(carrier, 'tracestate', tracestateStr);
104
+ }
105
+ }
106
+
107
+ setRecorded() {
108
+ return this.traceparent.setRecorded();
109
+ }
110
+
111
+ isRecorded() {
112
+ return this.traceparent.recorded;
113
+ }
114
+ }
115
+
116
+ TraceContext.FLAGS = TraceParent.FLAGS;
117
+
118
+ module.exports = TraceContext;