@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.
- package/LICENSE +26 -0
- package/README.md +126 -0
- package/index.d.ts +464 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +97 -0
- package/lib/agent.js +1226 -0
- package/lib/apm-client/apm-client.js +107 -0
- package/lib/apm-client/channel-writer.js +334 -0
- package/lib/apm-client/jsonl-file-client.js +241 -0
- package/lib/apm-client/ndjson.js +20 -0
- package/lib/apm-client/noop-apm-client.js +79 -0
- package/lib/apm-client/s3-uploader.js +308 -0
- package/lib/apm-client/truncate.js +507 -0
- package/lib/async-hooks-polyfill.js +58 -0
- package/lib/cloud-metadata/aws.js +175 -0
- package/lib/cloud-metadata/azure.js +123 -0
- package/lib/cloud-metadata/callback-coordination.js +159 -0
- package/lib/cloud-metadata/gcp.js +133 -0
- package/lib/cloud-metadata/index.js +175 -0
- package/lib/config/config.js +431 -0
- package/lib/config/normalizers.js +649 -0
- package/lib/config/schema.js +946 -0
- package/lib/constants.js +35 -0
- package/lib/errors.js +303 -0
- package/lib/filters/sanitize-field-names.js +69 -0
- package/lib/http-request.js +249 -0
- package/lib/instrumentation/context.js +56 -0
- package/lib/instrumentation/dropped-spans-stats.js +112 -0
- package/lib/instrumentation/elasticsearch-shared.js +63 -0
- package/lib/instrumentation/express-utils.js +91 -0
- package/lib/instrumentation/generic-span.js +322 -0
- package/lib/instrumentation/http-shared.js +424 -0
- package/lib/instrumentation/ids.js +39 -0
- package/lib/instrumentation/index.js +1078 -0
- package/lib/instrumentation/modules/@apollo/server.js +39 -0
- package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
- package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
- package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
- package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
- package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
- package/lib/instrumentation/modules/apollo-server-core.js +49 -0
- package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
- package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
- package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
- package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
- package/lib/instrumentation/modules/aws-sdk.js +76 -0
- package/lib/instrumentation/modules/bluebird.js +93 -0
- package/lib/instrumentation/modules/cassandra-driver.js +280 -0
- package/lib/instrumentation/modules/elasticsearch.js +200 -0
- package/lib/instrumentation/modules/express-graphql.js +66 -0
- package/lib/instrumentation/modules/express-queue.js +28 -0
- package/lib/instrumentation/modules/express.js +162 -0
- package/lib/instrumentation/modules/fastify.js +179 -0
- package/lib/instrumentation/modules/finalhandler.js +41 -0
- package/lib/instrumentation/modules/generic-pool.js +85 -0
- package/lib/instrumentation/modules/graphql.js +256 -0
- package/lib/instrumentation/modules/handlebars.js +33 -0
- package/lib/instrumentation/modules/http.js +112 -0
- package/lib/instrumentation/modules/http2.js +320 -0
- package/lib/instrumentation/modules/https.js +68 -0
- package/lib/instrumentation/modules/ioredis.js +94 -0
- package/lib/instrumentation/modules/jade.js +29 -0
- package/lib/instrumentation/modules/kafkajs.js +476 -0
- package/lib/instrumentation/modules/knex.js +91 -0
- package/lib/instrumentation/modules/koa-router.js +74 -0
- package/lib/instrumentation/modules/koa.js +15 -0
- package/lib/instrumentation/modules/memcached.js +100 -0
- package/lib/instrumentation/modules/mimic-response.js +45 -0
- package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
- package/lib/instrumentation/modules/mongodb-core.js +206 -0
- package/lib/instrumentation/modules/mongodb.js +259 -0
- package/lib/instrumentation/modules/mysql.js +200 -0
- package/lib/instrumentation/modules/mysql2.js +140 -0
- package/lib/instrumentation/modules/pg.js +148 -0
- package/lib/instrumentation/modules/pug.js +29 -0
- package/lib/instrumentation/modules/redis.js +176 -0
- package/lib/instrumentation/modules/restify.js +52 -0
- package/lib/instrumentation/modules/tedious.js +159 -0
- package/lib/instrumentation/modules/undici.js +270 -0
- package/lib/instrumentation/modules/ws.js +59 -0
- package/lib/instrumentation/noop-transaction.js +81 -0
- package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
- package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
- package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
- package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
- package/lib/instrumentation/run-context/RunContext.js +151 -0
- package/lib/instrumentation/run-context/index.js +23 -0
- package/lib/instrumentation/shimmer.js +123 -0
- package/lib/instrumentation/span-compression.js +239 -0
- package/lib/instrumentation/span.js +621 -0
- package/lib/instrumentation/template-shared.js +43 -0
- package/lib/instrumentation/timer.js +84 -0
- package/lib/instrumentation/transaction.js +571 -0
- package/lib/load-source-map.js +100 -0
- package/lib/logging.js +212 -0
- package/lib/metrics/index.js +92 -0
- package/lib/metrics/platforms/generic/index.js +40 -0
- package/lib/metrics/platforms/generic/process-cpu.js +22 -0
- package/lib/metrics/platforms/generic/process-top.js +157 -0
- package/lib/metrics/platforms/generic/stats.js +34 -0
- package/lib/metrics/platforms/generic/system-cpu.js +51 -0
- package/lib/metrics/platforms/linux/index.js +19 -0
- package/lib/metrics/platforms/linux/stats.js +213 -0
- package/lib/metrics/queue.js +90 -0
- package/lib/metrics/registry.js +52 -0
- package/lib/metrics/reporter.js +119 -0
- package/lib/metrics/runtime.js +77 -0
- package/lib/middleware/connect.js +16 -0
- package/lib/parsers.js +225 -0
- package/lib/propwrap.js +147 -0
- package/lib/stacktraces.js +537 -0
- package/lib/symbols.js +15 -0
- package/lib/tracecontext/index.js +115 -0
- package/lib/tracecontext/traceparent.js +185 -0
- package/lib/tracecontext/tracestate.js +388 -0
- package/lib/wildcard-matcher.js +52 -0
- package/loader.mjs +7 -0
- package/package.json +98 -0
- package/start.d.ts +8 -0
- package/start.js +29 -0
- package/types/aws-lambda.d.ts +98 -0
- 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
|
+
};
|