@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,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
+ };
@@ -0,0 +1,106 @@
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 asyncHooks = require('async_hooks');
10
+
11
+ const { BasicRunContextManager } = require('./BasicRunContextManager');
12
+
13
+ // A run context manager that uses an async hook to automatically track
14
+ // run context across async tasks.
15
+ //
16
+ // (Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts)
17
+ class AsyncHooksRunContextManager extends BasicRunContextManager {
18
+ constructor(log, runContextClass) {
19
+ super(log, runContextClass);
20
+ this._runContextFromAsyncId = new Map();
21
+ this._asyncHook = asyncHooks.createHook({
22
+ init: this._init.bind(this),
23
+ before: this._before.bind(this),
24
+ after: this._after.bind(this),
25
+ destroy: this._destroy.bind(this),
26
+ promiseResolve: this._destroy.bind(this),
27
+ });
28
+ }
29
+
30
+ enable() {
31
+ super.enable();
32
+ this._asyncHook.enable();
33
+ return this;
34
+ }
35
+
36
+ disable() {
37
+ super.disable();
38
+ this._asyncHook.disable();
39
+ this._runContextFromAsyncId.clear();
40
+ return this;
41
+ }
42
+
43
+ // Reset state for re-use of this context manager by tests in the same process.
44
+ testReset() {
45
+ // Absent a core node async_hooks bug, the easy way to implement this method
46
+ // would be: `this.disable(); this.enable()`.
47
+ // However there is a bug in Node.js v12.0.0 - v12.2.0 (inclusive) where
48
+ // disabling the async hook could result in it never getting re-enabled.
49
+ // https://github.com/nodejs/node/issues/27585
50
+ // https://github.com/nodejs/node/pull/27590 (included in node v12.3.0)
51
+ this._runContextFromAsyncId.clear();
52
+ this._stack = [];
53
+ }
54
+
55
+ /**
56
+ * Init hook will be called when userland create a async context, setting the
57
+ * context as the current one if it exist.
58
+ * @param asyncId id of the async context
59
+ * @param type the resource type
60
+ */
61
+ _init(asyncId, type, triggerAsyncId) {
62
+ // ignore TIMERWRAP as they combine timers with same timeout which can lead to
63
+ // false context propagation. TIMERWRAP has been removed in node 11
64
+ // every timer has it's own `Timeout` resource anyway which is used to propagete
65
+ // context.
66
+ if (type === 'TIMERWRAP') {
67
+ return;
68
+ }
69
+
70
+ const context = this._stack[this._stack.length - 1];
71
+ if (context !== undefined) {
72
+ this._runContextFromAsyncId.set(asyncId, context);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Destroy hook will be called when a given context is no longer used so we can
78
+ * remove its attached context.
79
+ * @param asyncId id of the async context
80
+ */
81
+ _destroy(asyncId) {
82
+ this._runContextFromAsyncId.delete(asyncId);
83
+ }
84
+
85
+ /**
86
+ * Before hook is called just before executing a async context.
87
+ * @param asyncId id of the async context
88
+ */
89
+ _before(asyncId) {
90
+ const context = this._runContextFromAsyncId.get(asyncId);
91
+ if (context !== undefined) {
92
+ this._enterRunContext(context);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * After hook is called just after completing the execution of a async context.
98
+ */
99
+ _after() {
100
+ this._exitRunContext();
101
+ }
102
+ }
103
+
104
+ module.exports = {
105
+ AsyncHooksRunContextManager,
106
+ };
@@ -0,0 +1,73 @@
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 { AsyncLocalStorage } = require('async_hooks');
10
+
11
+ const { AbstractRunContextManager } = require('./AbstractRunContextManager');
12
+
13
+ /**
14
+ * A RunContextManager that uses core node `AsyncLocalStorage` as the mechanism
15
+ * for run-context tracking.
16
+ *
17
+ * (Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-context-async-hooks/src/AsyncLocalStorageContextManager.ts)
18
+ */
19
+ class AsyncLocalStorageRunContextManager extends AbstractRunContextManager {
20
+ constructor(log, runContextClass) {
21
+ super(log, runContextClass);
22
+ this._asyncLocalStorage = new AsyncLocalStorage();
23
+ }
24
+
25
+ // A string representation useful for debug logging. For example,
26
+ // AsyncLocalStorageRunContextManager( RC(Trans(685ead, manual), [Span(9dd31c, GET httpstat.us, ended)]) )
27
+ toString() {
28
+ return `${this.constructor.name}( ${this.active().toString()} )`;
29
+ }
30
+
31
+ enable() {
32
+ return this;
33
+ }
34
+
35
+ disable() {
36
+ this._asyncLocalStorage.disable();
37
+ return this;
38
+ }
39
+
40
+ // Reset state for re-use of this context manager by tests in the same process.
41
+ testReset() {
42
+ this.disable();
43
+ this.enable();
44
+ }
45
+
46
+ active() {
47
+ const store = this._asyncLocalStorage.getStore();
48
+ if (store == null) {
49
+ return this.root();
50
+ } else {
51
+ return store;
52
+ }
53
+ }
54
+
55
+ with(runContext, fn, thisArg, ...args) {
56
+ const cb = thisArg == null ? fn : fn.bind(thisArg);
57
+ return this._asyncLocalStorage.run(runContext, cb, ...args);
58
+ }
59
+
60
+ // This public method is needed to support the semantics of
61
+ // apm.startTransaction() and apm.startSpan() that impact the current run
62
+ // context.
63
+ //
64
+ // Otherwise, all run context changes are via `.with()` -- scoped to a
65
+ // function call -- or via the "before" async hook -- scoped to an async task.
66
+ supersedeRunContext(runContext) {
67
+ this._asyncLocalStorage.enterWith(runContext);
68
+ }
69
+ }
70
+
71
+ module.exports = {
72
+ AsyncLocalStorageRunContextManager,
73
+ };
@@ -0,0 +1,82 @@
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 { AbstractRunContextManager } = require('./AbstractRunContextManager');
10
+
11
+ // A basic manager for run context. It handles a stack of run contexts, but does
12
+ // no automatic tracking (via async_hooks or otherwise). Use one of the
13
+ // subclasses.
14
+ //
15
+ // (Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-context-async-hooks/src/AsyncHooksContextManager.ts)
16
+ class BasicRunContextManager extends AbstractRunContextManager {
17
+ constructor(log, runContextClass) {
18
+ super(log, runContextClass);
19
+ this._stack = []; // Top of stack is the current run context.
20
+ }
21
+
22
+ // A string representation useful for debug logging. For example,
23
+ // BasicRunContextManager(
24
+ // RC(Trans(685ead, manual), [Span(9dd31c, GET httpstat.us, ended)]),
25
+ // RC(Trans(685ead, manual)) )
26
+ toString() {
27
+ return `${this.constructor.name}( ${this._stack
28
+ .map((rc) => rc.toString())
29
+ .join(', ')} )`;
30
+ }
31
+
32
+ enable() {
33
+ return this;
34
+ }
35
+
36
+ disable() {
37
+ this._stack = [];
38
+ return this;
39
+ }
40
+
41
+ // Reset state for re-use of this context manager by tests in the same process.
42
+ testReset() {
43
+ this.disable();
44
+ this.enable();
45
+ }
46
+
47
+ active() {
48
+ return this._stack[this._stack.length - 1] || this.root();
49
+ }
50
+
51
+ with(runContext, fn, thisArg, ...args) {
52
+ this._enterRunContext(runContext);
53
+ try {
54
+ return fn.call(thisArg, ...args);
55
+ } finally {
56
+ this._exitRunContext();
57
+ }
58
+ }
59
+
60
+ // This public method is needed to support the semantics of
61
+ // apm.startTransaction() and apm.startSpan() that impact the current run
62
+ // context.
63
+ //
64
+ // Otherwise, all run context changes are via `.with()` -- scoped to a
65
+ // function call -- or via the "before" async hook -- scoped to an async task.
66
+ supersedeRunContext(runContext) {
67
+ this._exitRunContext();
68
+ this._enterRunContext(runContext);
69
+ }
70
+
71
+ _enterRunContext(runContext) {
72
+ this._stack.push(runContext);
73
+ }
74
+
75
+ _exitRunContext() {
76
+ this._stack.pop();
77
+ }
78
+ }
79
+
80
+ module.exports = {
81
+ BasicRunContextManager,
82
+ };
@@ -0,0 +1,151 @@
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
+ // A RunContext is the immutable structure that holds which transaction and span
10
+ // are currently active, if any, for the running JavaScript code.
11
+ //
12
+ // Module instrumentation code interacts with run contexts via a number of
13
+ // methods on the `Instrumentation` instance at `agent._instrumentation`.
14
+ //
15
+ // User code using the agent's API (the Agent API, Transaction API, and Span API)
16
+ // are not exposes to RunContext instances. However users of the OpenTelemetry
17
+ // API, provided by the OpenTelemetry Bridge, *are* exposed to OpenTelemetry
18
+ // `Context` instances -- which RunContext implements.
19
+ //
20
+ // A RunContext holds:
21
+ // - a current Transaction, which can be null; and
22
+ // - a *stack* of Spans, where the top-of-stack span is the "current" one.
23
+ // A stack is necessary to support the semantics of multiple started and ended
24
+ // spans in the same async task. E.g.:
25
+ // apm.startTransaction('t')
26
+ // var s1 = apm.startSpan('s1')
27
+ // var s2 = apm.startSpan('s2')
28
+ // s2.end()
29
+ // assert(apm.currentSpan === s1, 's1 is now the current span')
30
+ // - a mapping of "values". This is an arbitrary key-value mapping, but exists
31
+ // primarily to implement OpenTelemetry `interface Context`
32
+ // https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.1.0/src/context/types.ts#L17-L41
33
+ //
34
+ // A RunContext is immutable. This means that `runContext.enterSpan(span)` and
35
+ // other similar methods return a new/separate RunContext instance. This is
36
+ // done so that a run-context change in the current code does not change
37
+ // anything for other code bound to the original RunContext (e.g. via
38
+ // `ins.bindFunction` or `ins.bindEmitter`).
39
+ //
40
+ // Warning: Agent code should never use the `RunContext` class directly
41
+ // because a subclass can be provided for the `Instrumentation` to use.
42
+ // Instead new instances should be built from an existing one, typically the
43
+ // active one (`_runCtxMgr.active()`) or the root one (`_runCtxMgr.root()`).
44
+ class RunContext {
45
+ constructor(trans, spans, parentValues) {
46
+ this._trans = trans || null;
47
+ this._spans = spans || [];
48
+ this._values = parentValues ? new Map(parentValues) : new Map();
49
+ }
50
+
51
+ currTransaction() {
52
+ return this._trans;
53
+ }
54
+
55
+ // Returns the currently active span, if any, otherwise null.
56
+ currSpan() {
57
+ if (this._spans.length > 0) {
58
+ return this._spans[this._spans.length - 1];
59
+ } else {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ // Return a new RunContext for a newly active/current Transaction.
65
+ enterTrans(trans) {
66
+ return new this.constructor(trans, null, this._values);
67
+ }
68
+
69
+ // Return a new RunContext with the given span added to the top of the spans
70
+ // stack.
71
+ enterSpan(span) {
72
+ const newSpans = this._spans.slice();
73
+ newSpans.push(span);
74
+ return new this.constructor(this._trans, newSpans, this._values);
75
+ }
76
+
77
+ // Return a new RunContext with the given transaction (and hence all of its
78
+ // spans) removed.
79
+ leaveTrans() {
80
+ return new this.constructor(null, null, this._values);
81
+ }
82
+
83
+ // Return a new RunContext with the given span removed, or null if there is
84
+ // no change (the given span isn't part of the run context).
85
+ //
86
+ // Typically this span is the top of stack (i.e. it is the current span).
87
+ // However, it is possible to have out-of-order span.end() or even end a span
88
+ // that isn't part of the current run context stack at all. (See
89
+ // test/instrumentation/run-context/fixtures/end-non-current-spans.js for
90
+ // examples.)
91
+ leaveSpan(span) {
92
+ let newRc = null;
93
+ let newSpans;
94
+ const lastSpan = this._spans[this._spans.length - 1];
95
+ if (lastSpan && lastSpan.id === span.id) {
96
+ // Fast path for common case: `span` is top of stack.
97
+ newSpans = this._spans.slice(0, this._spans.length - 1);
98
+ newRc = new this.constructor(this._trans, newSpans, this._values);
99
+ } else {
100
+ const stackIdx = this._spans.findIndex((s) => s.id === span.id);
101
+ if (stackIdx !== -1) {
102
+ newSpans = this._spans
103
+ .slice(0, stackIdx)
104
+ .concat(this._spans.slice(stackIdx + 1));
105
+ newRc = new this.constructor(this._trans, newSpans, this._values);
106
+ }
107
+ }
108
+ return newRc;
109
+ }
110
+
111
+ // A string representation useful for debug logging.
112
+ // For example:
113
+ // RunContext(Transaction(abc123, 'trans name'), [Span(def456, 'span name', ended)])
114
+ // ^^^^^^^-- if the span has ended
115
+ // ^^^^^^ ^^^^^^-- id
116
+ // ^^^^^^^^^^-- the class name, typically "RunContext", but can be overriden
117
+ toString() {
118
+ const bits = [];
119
+ if (this._trans) {
120
+ bits.push(this._trans.toString());
121
+ }
122
+ if (this._spans.length > 0) {
123
+ const spanStrs = this._spans.map((s) => s.toString());
124
+ bits.push('[' + spanStrs + ']');
125
+ }
126
+ return `${this.constructor.name}(${bits.join(', ')})`;
127
+ }
128
+
129
+ // ---- The following implements the OTel Context interface.
130
+ // https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.0.4/src/context/types.ts#L17
131
+
132
+ getValue(key) {
133
+ return this._values.get(key);
134
+ }
135
+
136
+ setValue(key, value) {
137
+ const rc = new this.constructor(this._trans, this._spans, this._values);
138
+ rc._values.set(key, value);
139
+ return rc;
140
+ }
141
+
142
+ deleteValue(key) {
143
+ const rc = new this.constructor(this._trans, this._spans, this._values);
144
+ rc._values.delete(key);
145
+ return rc;
146
+ }
147
+ }
148
+
149
+ module.exports = {
150
+ RunContext,
151
+ };
@@ -0,0 +1,23 @@
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 {
10
+ AsyncHooksRunContextManager,
11
+ } = require('./AsyncHooksRunContextManager');
12
+ const {
13
+ AsyncLocalStorageRunContextManager,
14
+ } = require('./AsyncLocalStorageRunContextManager');
15
+ const { BasicRunContextManager } = require('./BasicRunContextManager');
16
+ const { RunContext } = require('./RunContext');
17
+
18
+ module.exports = {
19
+ AsyncHooksRunContextManager,
20
+ AsyncLocalStorageRunContextManager,
21
+ BasicRunContextManager,
22
+ RunContext,
23
+ };