@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.
- package/LICENSE +26 -0
- package/NOTICE.md +442 -0
- package/README.md +48 -0
- package/changes.json +78 -0
- package/index.d.ts +398 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +119 -0
- package/lib/agent.js +941 -0
- package/lib/apm-client/apm-client.js +313 -0
- package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
- package/lib/apm-client/http-apm-client/README.md +485 -0
- package/lib/apm-client/http-apm-client/central-config.js +41 -0
- package/lib/apm-client/http-apm-client/container-info.js +111 -0
- package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
- package/lib/apm-client/http-apm-client/index.js +1975 -0
- package/lib/apm-client/http-apm-client/logging.js +31 -0
- package/lib/apm-client/http-apm-client/ndjson.js +20 -0
- package/lib/apm-client/http-apm-client/truncate.js +434 -0
- package/lib/apm-client/noop-apm-client.js +73 -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 +458 -0
- package/lib/config/normalizers.js +701 -0
- package/lib/config/schema.js +1007 -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/azure-functions.js +519 -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 +1127 -0
- package/lib/instrumentation/modules/@apollo/server.js +30 -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/@opentelemetry/api.js +86 -0
- package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -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/_lambda-handler.js +40 -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 +191 -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 +172 -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 +22 -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 +18 -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 +99 -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 +18 -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/lambda.js +992 -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/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
- package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
- package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
- package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
- package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
- package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
- package/lib/opentelemetry-bridge/README.md +244 -0
- package/lib/opentelemetry-bridge/index.js +15 -0
- package/lib/opentelemetry-bridge/oblog.js +23 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
- package/lib/opentelemetry-bridge/otelutils.js +99 -0
- package/lib/opentelemetry-bridge/setup.js +76 -0
- package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
- package/lib/opentelemetry-metrics/index.js +50 -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 +118 -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 +299 -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,201 @@
|
|
|
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 otel = require('@opentelemetry/api');
|
|
10
|
+
|
|
11
|
+
const oblog = require('./oblog');
|
|
12
|
+
const { OTelBridgeRunContext } = require('./OTelBridgeRunContext');
|
|
13
|
+
const { OTelSpan } = require('./OTelSpan');
|
|
14
|
+
const Transaction = require('../instrumentation/transaction');
|
|
15
|
+
const { OTelBridgeNonRecordingSpan } = require('./OTelBridgeNonRecordingSpan');
|
|
16
|
+
const {
|
|
17
|
+
epochMsFromOTelTimeInput,
|
|
18
|
+
otelSpanContextFromTraceContext,
|
|
19
|
+
traceparentStrFromOTelSpanContext,
|
|
20
|
+
} = require('./otelutils');
|
|
21
|
+
const { OUTCOME_UNKNOWN } = require('../constants');
|
|
22
|
+
|
|
23
|
+
// Implements interface Tracer from:
|
|
24
|
+
// https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.0.4/src/trace/tracer.ts
|
|
25
|
+
class OTelTracer {
|
|
26
|
+
constructor(agent) {
|
|
27
|
+
this._agent = agent;
|
|
28
|
+
this._ins = agent._instrumentation;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Starts a new {@link Span}. Start the span without setting it on context.
|
|
33
|
+
*
|
|
34
|
+
* This method do NOT modify the current Context.
|
|
35
|
+
*
|
|
36
|
+
* @param name The name of the span
|
|
37
|
+
* @param [options] SpanOptions used for span creation
|
|
38
|
+
* @param [context] Context to use to extract parent
|
|
39
|
+
* @returns Span The newly created span
|
|
40
|
+
* @example
|
|
41
|
+
* const span = tracer.startSpan('op');
|
|
42
|
+
* span.setAttribute('key', 'value');
|
|
43
|
+
* span.end();
|
|
44
|
+
*/
|
|
45
|
+
startSpan(name, otelSpanOptions = {}, otelContext = otel.context.active()) {
|
|
46
|
+
oblog.apicall(
|
|
47
|
+
'OTelTracer.startSpan(name=%s, options=%j, context=%s)',
|
|
48
|
+
name,
|
|
49
|
+
otelSpanOptions,
|
|
50
|
+
otelContext,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Get the parent info for the new span.
|
|
54
|
+
// We want to get a core Transaction or Span as a parent, when possible,
|
|
55
|
+
// because that is required to support the span compression feature.
|
|
56
|
+
let parentGenericSpan;
|
|
57
|
+
let parentOTelSpanContext;
|
|
58
|
+
if (otelSpanOptions.root) {
|
|
59
|
+
// Pass through: explicitly want no parent.
|
|
60
|
+
} else if (otelContext instanceof OTelBridgeRunContext) {
|
|
61
|
+
parentGenericSpan =
|
|
62
|
+
otelContext.currSpan() || otelContext.currTransaction();
|
|
63
|
+
if (parentGenericSpan instanceof OTelBridgeNonRecordingSpan) {
|
|
64
|
+
// This isn't a real Transaction we can use. It is a placeholder
|
|
65
|
+
// to propagate its SpanContext. Grab just that.
|
|
66
|
+
parentOTelSpanContext = parentGenericSpan.spanContext();
|
|
67
|
+
parentGenericSpan = null;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
// `otelContext` is any object that is meant to satisfy `interface
|
|
71
|
+
// Context`. This may hold an OTel `SpanContext` that should be
|
|
72
|
+
// propagated.
|
|
73
|
+
parentOTelSpanContext = otel.trace.getSpanContext(otelContext);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const createOpts = {};
|
|
77
|
+
if (otelSpanOptions.links) {
|
|
78
|
+
// Span link *attributes* are not currently supported, they are silently dropped.
|
|
79
|
+
createOpts.links = otelSpanOptions.links
|
|
80
|
+
.filter(
|
|
81
|
+
(otelLink) =>
|
|
82
|
+
otelLink &&
|
|
83
|
+
otelLink.context &&
|
|
84
|
+
otel.isSpanContextValid(otelLink.context),
|
|
85
|
+
)
|
|
86
|
+
.map((otelLink) => {
|
|
87
|
+
return {
|
|
88
|
+
context: traceparentStrFromOTelSpanContext(otelLink.context),
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Create the new Span/Transaction.
|
|
94
|
+
let newTransOrSpan = null;
|
|
95
|
+
if (parentGenericSpan) {
|
|
96
|
+
// New child span.
|
|
97
|
+
const trans =
|
|
98
|
+
parentGenericSpan instanceof Transaction
|
|
99
|
+
? parentGenericSpan
|
|
100
|
+
: parentGenericSpan.transaction;
|
|
101
|
+
createOpts.childOf = parentGenericSpan;
|
|
102
|
+
if (otelSpanOptions.startTime) {
|
|
103
|
+
createOpts.startTime = epochMsFromOTelTimeInput(
|
|
104
|
+
otelSpanOptions.startTime,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (
|
|
108
|
+
otelSpanOptions.kind === otel.SpanKind.CLIENT ||
|
|
109
|
+
otelSpanOptions.kind === otel.SpanKind.PRODUCER
|
|
110
|
+
) {
|
|
111
|
+
createOpts.exitSpan = true;
|
|
112
|
+
}
|
|
113
|
+
newTransOrSpan = trans.createSpan(name, createOpts);
|
|
114
|
+
|
|
115
|
+
// There might be no span, e.g. if the span is a child of an exit span. We
|
|
116
|
+
// have to return some OTelSpan, and we also want to propagate the
|
|
117
|
+
// parent's trace-context, if any.
|
|
118
|
+
if (!newTransOrSpan) {
|
|
119
|
+
return otel.trace.wrapSpanContext(
|
|
120
|
+
otelSpanContextFromTraceContext(parentGenericSpan._context),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
} else if (
|
|
124
|
+
parentOTelSpanContext &&
|
|
125
|
+
otel.isSpanContextValid(parentOTelSpanContext)
|
|
126
|
+
) {
|
|
127
|
+
// New continuing transaction.
|
|
128
|
+
// Note: This is *not* using `SpanContext.isRemote`. I am not sure if it
|
|
129
|
+
// is relevant unless using something, like @opentelemetry/core's
|
|
130
|
+
// `W3CTraceContextPropagator`, that sets `isRemote = true`. Nothing in
|
|
131
|
+
// @opentelemetry/api itself sets isRemote.
|
|
132
|
+
createOpts.childOf = traceparentStrFromOTelSpanContext(
|
|
133
|
+
parentOTelSpanContext,
|
|
134
|
+
);
|
|
135
|
+
if (parentOTelSpanContext.traceState) {
|
|
136
|
+
createOpts.tracestate = parentOTelSpanContext.traceState.serialize();
|
|
137
|
+
}
|
|
138
|
+
if (otelSpanOptions.startTime !== undefined) {
|
|
139
|
+
createOpts.startTime = epochMsFromOTelTimeInput(
|
|
140
|
+
otelSpanOptions.startTime,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
newTransOrSpan = this._ins.createTransaction(name, createOpts);
|
|
144
|
+
} else {
|
|
145
|
+
// New root transaction.
|
|
146
|
+
if (otelSpanOptions.startTime !== undefined) {
|
|
147
|
+
createOpts.startTime = epochMsFromOTelTimeInput(
|
|
148
|
+
otelSpanOptions.startTime,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
newTransOrSpan = this._ins.createTransaction(name, createOpts);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
newTransOrSpan._setOTelKind(
|
|
155
|
+
otel.SpanKind[otelSpanOptions.kind || otel.SpanKind.INTERNAL],
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Explicitly use the higher-priority user outcome API to prevent the agent
|
|
159
|
+
// inferring the outcome from any reported errors or HTTP status code.
|
|
160
|
+
newTransOrSpan.setOutcome(OUTCOME_UNKNOWN);
|
|
161
|
+
|
|
162
|
+
const otelSpan = new OTelSpan(newTransOrSpan);
|
|
163
|
+
otelSpan.setAttributes(otelSpanOptions.attributes);
|
|
164
|
+
|
|
165
|
+
return otelSpan;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// startActiveSpan(name[, options[, context]], fn)
|
|
169
|
+
//
|
|
170
|
+
// Interface: https://github.com/open-telemetry/opentelemetry-js-api/blob/main/src/trace/tracer.ts#L41
|
|
171
|
+
// Adapted from: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-sdk-trace-base/src/Tracer.ts
|
|
172
|
+
startActiveSpan(name, otelSpanOptions, otelContext, fn) {
|
|
173
|
+
oblog.apicall(
|
|
174
|
+
'OTelTracer.startActiveSpan(name=%s, options=%j, context=%s, fn)',
|
|
175
|
+
name,
|
|
176
|
+
otelSpanOptions,
|
|
177
|
+
otelContext,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (arguments.length < 2) {
|
|
181
|
+
return;
|
|
182
|
+
} else if (arguments.length === 2) {
|
|
183
|
+
fn = otelSpanOptions;
|
|
184
|
+
otelSpanOptions = undefined;
|
|
185
|
+
otelContext = undefined;
|
|
186
|
+
} else if (arguments.length === 3) {
|
|
187
|
+
fn = otelContext;
|
|
188
|
+
otelContext = undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const parentContext = otelContext || otel.context.active();
|
|
192
|
+
const span = this.startSpan(name, otelSpanOptions, parentContext);
|
|
193
|
+
const contextWithSpanSet = otel.trace.setSpan(parentContext, span);
|
|
194
|
+
|
|
195
|
+
return otel.context.with(contextWithSpanSet, fn, undefined, span);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
OTelTracer,
|
|
201
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
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 oblog = require('./oblog');
|
|
10
|
+
|
|
11
|
+
class OTelTracerProvider {
|
|
12
|
+
// @param {OTelTracer} tracer
|
|
13
|
+
constructor(tracer) {
|
|
14
|
+
this._tracer = tracer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getTracer(_name, _version, _options) {
|
|
18
|
+
oblog.apicall('OTelTracerProvider.getTracer(...)');
|
|
19
|
+
return this._tracer;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
OTelTracerProvider,
|
|
25
|
+
};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# OpenTelemetry Bridge
|
|
2
|
+
|
|
3
|
+
This document includes design / developer / maintenance notes for the
|
|
4
|
+
Node.js APM Agent *OpenTelemetry Bridge*.
|
|
5
|
+
|
|
6
|
+
Spec: https://github.com/elastic/apm/blob/main/specs/agents/tracing-api-otel.md
|
|
7
|
+
|
|
8
|
+
## Maintenance
|
|
9
|
+
|
|
10
|
+
- We should release a new agent version with an updated "@opentelemetry/api"
|
|
11
|
+
dependency relatively soon after any new *minor* release. Otherwise a user
|
|
12
|
+
upgrading their "@opentelemetry/api" dep to "1.x+1", e.g. "1.2.0", will find
|
|
13
|
+
that the OTel Bridge which uses version "1.x", e.g. "1.1.0" or lower, does
|
|
14
|
+
not work.
|
|
15
|
+
|
|
16
|
+
The reason is that the OTel Bridge registers global providers (e.g.
|
|
17
|
+
`otel.trace.setGlobalTracerProvider`) with its version of the OTel API. When
|
|
18
|
+
user code attempts to *get* a tracer with **its version** of the OTel API, the
|
|
19
|
+
[OTel API compatibility logic](https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.1.0/src/internal/semver.ts#L24-L33)
|
|
20
|
+
decides that using a v1.1.x Tracer with a v1.2.0 Tracer API is not compatible
|
|
21
|
+
and falls back to a noop implementation.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Development / Debugging
|
|
25
|
+
|
|
26
|
+
When doing development on, or debugging the OTel Bridge, it might be helpful to
|
|
27
|
+
enable logging of (almost) every `@opentelemetry/api` call into the bridge.
|
|
28
|
+
This is done by setting this in `lib/opentelemetry-bridge/setup.js`.
|
|
29
|
+
|
|
30
|
+
const LOG_OTEL_API_CALLS = true
|
|
31
|
+
|
|
32
|
+
It looks like this:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
% cd test/opentelemetry-bridge/fixtures
|
|
36
|
+
% ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true node -r ../../../start.js start-span.js
|
|
37
|
+
otelapi: OTelTracerProvider.getTracer(...)
|
|
38
|
+
otelapi: OTelContextManager.active()
|
|
39
|
+
otelapi: OTelTracer.startSpan(name=mySpan, options={}, context=OTelBridgeRunContext<>)
|
|
40
|
+
otelapi: OTelContextManager.active()
|
|
41
|
+
otelapi: OTelBridgeRunContext.getValue(Symbol(OpenTelemetry Context Key SPAN))
|
|
42
|
+
otelapi: OTelSpan<Transaction<52260136515317aa, "mySpan">>.end(endTime=undefined)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Together with the agent's usual debug logging, this can help show how the bridge
|
|
46
|
+
is working.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
% ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true \
|
|
50
|
+
ELASTIC_APM_LOG_LEVEL=debug \
|
|
51
|
+
node -r ../../../start.js start-span.js | ecslog
|
|
52
|
+
...
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Naming
|
|
57
|
+
|
|
58
|
+
In general, the following variable/class/file naming is used:
|
|
59
|
+
|
|
60
|
+
- A class that implements an OTel interface is prefixed with "OTel". For
|
|
61
|
+
example `class OTelSpan` implements OTel `interface Span`.
|
|
62
|
+
- A class that bridges between an OTel interface and an object in the APM
|
|
63
|
+
agent is prefixed with `OTelBridge`. For example `OTelBridgeRunContext`
|
|
64
|
+
bridges between an OTel `interface Context` and the APM agent's `RunContext`,
|
|
65
|
+
i.e. it implements both interfaces/APIs.
|
|
66
|
+
- A variable that holds an OpenTelemetry object is prefixed with `otel`, or
|
|
67
|
+
`...OTel...` if it in the middle of the var name. Some examples:
|
|
68
|
+
- `otelSpanOptions` holds an OTel `SpanOptions` instance
|
|
69
|
+
- `parentOTelSpanContext`
|
|
70
|
+
- `epochMsFromOTelTimeInput()` converts from an OTel `TimeInput` to a number
|
|
71
|
+
of milliseconds since the Unix epoch
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Design Overview
|
|
75
|
+
|
|
76
|
+
The OpenTelemetry API is, currently, [these four interfaces](https://github.com/open-telemetry/opentelemetry-js-api/tree/main/src/api/):
|
|
77
|
+
|
|
78
|
+
- `otel.context.*` - API for managing Context, i.e. what the APM agent calls
|
|
79
|
+
"run context". More below.
|
|
80
|
+
- `otel.trace.*` - API for manipulating spans, and getting a `Tracer` to
|
|
81
|
+
create spans. More below.
|
|
82
|
+
- `otel.diag.*` - This is used to hook into internal OpenTelemetry diagnostics,
|
|
83
|
+
i.e. internal logging. There is very little `otel.diag` usage in
|
|
84
|
+
`@opentelemetry/api`, more in the SDK. The APM agent hooks up `otel.diag`
|
|
85
|
+
logging to its own logger **if `logLevel=trace`**.
|
|
86
|
+
- `otel.propagation.*` - Used for abstracting trace-context propagation
|
|
87
|
+
(reading/writing "traceparent" et al headers) and Baggage handling. This
|
|
88
|
+
isn't touched by the OTel Bridge, and shouldn't be necessary until either
|
|
89
|
+
the bridge supports Baggage or TextMapPropagator implementations like
|
|
90
|
+
`W3CTraceContextPropagator`. The APM agent implements its own internally.
|
|
91
|
+
|
|
92
|
+
In `Agent#start()`, if the `opentelemetryBridgeEnabled` config is true, then
|
|
93
|
+
a global [`ContextManager`](./OTelContextManager.js) and a global [`TracerProvider`](./OTelTracerProvider.js) are registered, which "enables" the bridge.
|
|
94
|
+
|
|
95
|
+
From the OTel Bridge spec:
|
|
96
|
+
|
|
97
|
+
> In order to avoid potentially complex and tedious synchronization issues
|
|
98
|
+
> between OTel and our existing agent implementations, the bridge implementation
|
|
99
|
+
> SHOULD provide an abstraction to have a single "active context" storage.
|
|
100
|
+
|
|
101
|
+
For this bridge, the agent's `RunContext` class was extended to support the
|
|
102
|
+
small [`interface Context`](https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.1.0/src/context/types.ts#L17-L41)
|
|
103
|
+
API and the agent's run context managers were updated to allow passing in a
|
|
104
|
+
subclass of `RunContext` to use. So the "single active context storage" is
|
|
105
|
+
instances of [`OTelBridgeRunContext`](./OTelBridgeRunContext.js) in the agent's
|
|
106
|
+
usual run context managers.
|
|
107
|
+
|
|
108
|
+
The way the "active span" is tracked by the OTel API is to call
|
|
109
|
+
`context.setValue(SPAN_KEY, span)`. The `OTelBridgeRunContext` class translates
|
|
110
|
+
calls using `SPAN_KEY` into the API that the agent's RunContext class uses.
|
|
111
|
+
Roughly this:
|
|
112
|
+
|
|
113
|
+
- `context.setValue(SPAN_KEY, span)` -> `this.enterSpan(span)`
|
|
114
|
+
- `context.getValue(SPAN_KEY)` -> `return new OTelSpan(this.currSpan())`
|
|
115
|
+
|
|
116
|
+
Otherwise the `*RunContextManager` classes in the agent map very well to the
|
|
117
|
+
OpenTelemetry `ContextManager` interface: the [`OTelContextManager`](./OTelContextManager.js)
|
|
118
|
+
implementation is very straightforward.
|
|
119
|
+
|
|
120
|
+
The `@opentelemetry/api` supports two ways to create objects that are internally
|
|
121
|
+
implemented and do not call the registered global providers.
|
|
122
|
+
|
|
123
|
+
1. `otel.trace.wrapSpanContext(...)` supports creating a `NonRecordingSpan` (a
|
|
124
|
+
class that isn't exported) instance that implements `interface Span`. [This
|
|
125
|
+
test fixture](../../test/opentelemetry-bridge/fixtures/nonrecordingspan-parent.js)
|
|
126
|
+
shows a use case. The bridge wraps this in an `OTelBridgeNonRecordingSpan`
|
|
127
|
+
that implements both OTel `interface Span` and the agent's Transaction API.
|
|
128
|
+
2. `otel.ROOT_CONTEXT` is a singleton object (an internal `BaseContext` class
|
|
129
|
+
instance) that implements `interface Context` but is not created via any
|
|
130
|
+
bridge API. That means bridge code cannot rely on a given `context` argument
|
|
131
|
+
being an instance of its `OTelBridgeRunContext` class.
|
|
132
|
+
[This test fixtures](../../test/opentelemetry-bridge/fixtures/using-root-context.js)
|
|
133
|
+
shows an example.
|
|
134
|
+
|
|
135
|
+
The trickiest part of the bridge is handling these two cases, especially at
|
|
136
|
+
the top of `startSpan` in [`OTelTracer`](./OTelTracer.js)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## Limitations / Differences with OpenTelemetry SDK
|
|
140
|
+
|
|
141
|
+
- The OpenTelemetry SDK defines [SpanLimits](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-limits).
|
|
142
|
+
This OpenTelemetry Bridge differs as follows:
|
|
143
|
+
- Attribute count is not limited. The OTel SDK defaults to a limit of 128.
|
|
144
|
+
(To implement this, start at `maybeSetOTelAttr` in "OTelSpan.js".)
|
|
145
|
+
- Attribute value strings are truncated at 1024 bytes. The OpenTelemetry SDK
|
|
146
|
+
uses `AttributeValueLengthLimit (Default=Infinity)`.
|
|
147
|
+
(We could consider using the configurable `longFieldMaxLength` for the
|
|
148
|
+
attribute value truncation limit, if there is a need.)
|
|
149
|
+
- Span events are not currently supported by this bridge.
|
|
150
|
+
- Span link *attributes* are not supported by the bridge (Elastic APM
|
|
151
|
+
supports span links, but not span link attributes).
|
|
152
|
+
|
|
153
|
+
- The OpenTelemetry Bridge spec says APM agents
|
|
154
|
+
["MAY"](https://github.com/elastic/apm/blob/main/specs/agents/tracing-api-otel.md#attributes-mapping)
|
|
155
|
+
report OTel span attributes as spad and transaction *labels* if the upstream
|
|
156
|
+
APM Server is less than version 7.16. This implementation opts *not* to do
|
|
157
|
+
that. The OTel spec allows a larger range of types for span attributes values
|
|
158
|
+
than is allowed for "tags" (aka labels) in the APM Server intake API, so some
|
|
159
|
+
further filtering of attributes would be required.
|
|
160
|
+
|
|
161
|
+
- There is a semantic difference between this OTel Bridge and the OpenTelemetry
|
|
162
|
+
SDK with `span.end()` that could impact parent/child relationships of spans.
|
|
163
|
+
This demonstrates the different:
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
const otel = require('@opentelemetry/api')
|
|
167
|
+
const tracer = otel.trace.getTracer()
|
|
168
|
+
tracer.startActiveSpan('s1', s1 => {
|
|
169
|
+
tracer.startActiveSpan('s2', s2 => {
|
|
170
|
+
s2.end()
|
|
171
|
+
})
|
|
172
|
+
s1.end()
|
|
173
|
+
tracer.startActiveSpan('s3', s3 => {
|
|
174
|
+
s3.end()
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
With the OTel SDK that will yield:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
span s1
|
|
183
|
+
`- span s2
|
|
184
|
+
`- span s3
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
With the Elastic APM agent:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
transaction s1
|
|
191
|
+
`- span s2
|
|
192
|
+
transaction s3
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
In current Elastic APM semantics, when a span is ended (e.g. `s1` above) it is
|
|
196
|
+
*no longer the current/active span in that async context*. This is historical
|
|
197
|
+
and allows a stack of current spans in sync code, e.g.:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
const t1 = apm.startTransaction('t1')
|
|
201
|
+
const s2 = apm.startSpan('s2')
|
|
202
|
+
const s3 = apm.startSpan('s3') // s3 is a child of s2
|
|
203
|
+
s3.end() // s3 is no longer active (popped off the stack)
|
|
204
|
+
const s4 = apm.startSpan('s4') // s4 is a child of s2
|
|
205
|
+
s4.end()
|
|
206
|
+
s2.end()
|
|
207
|
+
t1.end()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
This semantic difference is not expected to be common, because it is expected
|
|
211
|
+
that typically OTel API user code will end a span only at the end of its
|
|
212
|
+
function:
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
tracer.startActiveSpan('mySpan', mySpan => {
|
|
216
|
+
// ...
|
|
217
|
+
mySpan.end() // .end() only at end of function block
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Note that active span context *is* properly maintained when a new async task
|
|
222
|
+
is created (e.g. with `setTimeout`, etc.), so the following code produces
|
|
223
|
+
the expected trace:
|
|
224
|
+
|
|
225
|
+
```js
|
|
226
|
+
tracer.startActiveSpan('s1', s1 => {
|
|
227
|
+
setImmediate(() => {
|
|
228
|
+
tracer.startActiveSpan('s2', s2 => {
|
|
229
|
+
s2.end()
|
|
230
|
+
})
|
|
231
|
+
setTimeout(() => { // s1 is bound as the active span in this async task.
|
|
232
|
+
tracer.startActiveSpan('s3', s3 => {
|
|
233
|
+
s3.end()
|
|
234
|
+
})
|
|
235
|
+
}, 100)
|
|
236
|
+
s1.end()
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
If this *does* turn out to be a common issue, the OTel semantics for span.end()
|
|
241
|
+
can likely be accommodated.
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
@@ -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
|
+
const { OTelBridgeRunContext } = require('./OTelBridgeRunContext');
|
|
10
|
+
const { setupOTelBridge } = require('./setup');
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
OTelBridgeRunContext,
|
|
14
|
+
setupOTelBridge,
|
|
15
|
+
};
|
|
@@ -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
|
+
// This is the (O)penTelemetry (B)ridge (LOG) facility.
|
|
10
|
+
//
|
|
11
|
+
// It is used for development/debugging of the OTel Bridge to emit a log line
|
|
12
|
+
// for (almost) every OTel API call. OTel Bridge implementations typically
|
|
13
|
+
// call `oblog.apicall(...)`. During development/debugging there is a block in
|
|
14
|
+
// "setup.js" that is enabled to turn this logging on. This should always be
|
|
15
|
+
// disabled for any release code.
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
setApiCallLogFn(logFn) {
|
|
19
|
+
module.exports.apicall = logFn;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
apicall() {},
|
|
23
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and other contributors where applicable.
|
|
3
|
+
* Licensed under the BSD 2-Clause License; you may not use this file except in
|
|
4
|
+
* compliance with the BSD 2-Clause License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Adapted from @opentelemetry/core.
|
|
8
|
+
|
|
9
|
+
"use strict";
|
|
10
|
+
/*
|
|
11
|
+
* Copyright The OpenTelemetry Authors
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License.
|
|
15
|
+
* You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*/
|
|
25
|
+
const VALID_KEY_CHAR_RANGE = '[_0-9a-z-*/]';
|
|
26
|
+
const VALID_KEY = `[a-z]${VALID_KEY_CHAR_RANGE}{0,255}`;
|
|
27
|
+
const VALID_VENDOR_KEY = `[a-z0-9]${VALID_KEY_CHAR_RANGE}{0,240}@[a-z]${VALID_KEY_CHAR_RANGE}{0,13}`;
|
|
28
|
+
const VALID_KEY_REGEX = new RegExp(`^(?:${VALID_KEY}|${VALID_VENDOR_KEY})$`);
|
|
29
|
+
const VALID_VALUE_BASE_REGEX = /^[ -~]{0,255}[!-~]$/;
|
|
30
|
+
const INVALID_VALUE_COMMA_EQUAL_REGEX = /,|=/;
|
|
31
|
+
/**
|
|
32
|
+
* Key is opaque string up to 256 characters printable. It MUST begin with a
|
|
33
|
+
* lowercase letter, and can only contain lowercase letters a-z, digits 0-9,
|
|
34
|
+
* underscores _, dashes -, asterisks *, and forward slashes /.
|
|
35
|
+
* For multi-tenant vendor scenarios, an at sign (@) can be used to prefix the
|
|
36
|
+
* vendor name. Vendors SHOULD set the tenant ID at the beginning of the key.
|
|
37
|
+
* see https://www.w3.org/TR/trace-context/#key
|
|
38
|
+
*/
|
|
39
|
+
function validateKey(key) {
|
|
40
|
+
return VALID_KEY_REGEX.test(key);
|
|
41
|
+
}
|
|
42
|
+
exports.validateKey = validateKey;
|
|
43
|
+
/**
|
|
44
|
+
* Value is opaque string up to 256 characters printable ASCII RFC0020
|
|
45
|
+
* characters (i.e., the range 0x20 to 0x7E) except comma , and =.
|
|
46
|
+
*/
|
|
47
|
+
function validateValue(value) {
|
|
48
|
+
return (VALID_VALUE_BASE_REGEX.test(value) &&
|
|
49
|
+
!INVALID_VALUE_COMMA_EQUAL_REGEX.test(value));
|
|
50
|
+
}
|
|
51
|
+
exports.validateValue = validateValue;
|
|
52
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
// Adapted from @opentelemetry/core.
|
|
8
|
+
|
|
9
|
+
"use strict";
|
|
10
|
+
/*
|
|
11
|
+
* Copyright The OpenTelemetry Authors
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License.
|
|
15
|
+
* You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*/
|
|
25
|
+
const validators_1 = require("../internal/validators");
|
|
26
|
+
const MAX_TRACE_STATE_ITEMS = 32;
|
|
27
|
+
const MAX_TRACE_STATE_LEN = 512;
|
|
28
|
+
const LIST_MEMBERS_SEPARATOR = ',';
|
|
29
|
+
const LIST_MEMBER_KEY_VALUE_SPLITTER = '=';
|
|
30
|
+
/**
|
|
31
|
+
* TraceState must be a class and not a simple object type because of the spec
|
|
32
|
+
* requirement (https://www.w3.org/TR/trace-context/#tracestate-field).
|
|
33
|
+
*
|
|
34
|
+
* Here is the list of allowed mutations:
|
|
35
|
+
* - New key-value pair should be added into the beginning of the list
|
|
36
|
+
* - The value of any key can be updated. Modified keys MUST be moved to the
|
|
37
|
+
* beginning of the list.
|
|
38
|
+
*/
|
|
39
|
+
class TraceState {
|
|
40
|
+
constructor(rawTraceState) {
|
|
41
|
+
this._internalState = new Map();
|
|
42
|
+
if (rawTraceState)
|
|
43
|
+
this._parse(rawTraceState);
|
|
44
|
+
}
|
|
45
|
+
set(key, value) {
|
|
46
|
+
// TODO: Benchmark the different approaches(map vs list) and
|
|
47
|
+
// use the faster one.
|
|
48
|
+
const traceState = this._clone();
|
|
49
|
+
if (traceState._internalState.has(key)) {
|
|
50
|
+
traceState._internalState.delete(key);
|
|
51
|
+
}
|
|
52
|
+
traceState._internalState.set(key, value);
|
|
53
|
+
return traceState;
|
|
54
|
+
}
|
|
55
|
+
unset(key) {
|
|
56
|
+
const traceState = this._clone();
|
|
57
|
+
traceState._internalState.delete(key);
|
|
58
|
+
return traceState;
|
|
59
|
+
}
|
|
60
|
+
get(key) {
|
|
61
|
+
return this._internalState.get(key);
|
|
62
|
+
}
|
|
63
|
+
serialize() {
|
|
64
|
+
return this._keys()
|
|
65
|
+
.reduce((agg, key) => {
|
|
66
|
+
agg.push(key + LIST_MEMBER_KEY_VALUE_SPLITTER + this.get(key));
|
|
67
|
+
return agg;
|
|
68
|
+
}, [])
|
|
69
|
+
.join(LIST_MEMBERS_SEPARATOR);
|
|
70
|
+
}
|
|
71
|
+
_parse(rawTraceState) {
|
|
72
|
+
if (rawTraceState.length > MAX_TRACE_STATE_LEN)
|
|
73
|
+
return;
|
|
74
|
+
this._internalState = rawTraceState
|
|
75
|
+
.split(LIST_MEMBERS_SEPARATOR)
|
|
76
|
+
.reverse() // Store in reverse so new keys (.set(...)) will be placed at the beginning
|
|
77
|
+
.reduce((agg, part) => {
|
|
78
|
+
const listMember = part.trim(); // Optional Whitespace (OWS) handling
|
|
79
|
+
const i = listMember.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER);
|
|
80
|
+
if (i !== -1) {
|
|
81
|
+
const key = listMember.slice(0, i);
|
|
82
|
+
const value = listMember.slice(i + 1, part.length);
|
|
83
|
+
if ((0, validators_1.validateKey)(key) && (0, validators_1.validateValue)(value)) {
|
|
84
|
+
agg.set(key, value);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// TODO: Consider to add warning log
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return agg;
|
|
91
|
+
}, new Map());
|
|
92
|
+
// Because of the reverse() requirement, trunc must be done after map is created
|
|
93
|
+
if (this._internalState.size > MAX_TRACE_STATE_ITEMS) {
|
|
94
|
+
this._internalState = new Map(Array.from(this._internalState.entries())
|
|
95
|
+
.reverse() // Use reverse same as original tracestate parse chain
|
|
96
|
+
.slice(0, MAX_TRACE_STATE_ITEMS));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
_keys() {
|
|
100
|
+
return Array.from(this._internalState.keys()).reverse();
|
|
101
|
+
}
|
|
102
|
+
_clone() {
|
|
103
|
+
const traceState = new TraceState();
|
|
104
|
+
traceState._internalState = new Map(this._internalState);
|
|
105
|
+
return traceState;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.TraceState = TraceState;
|
|
109
|
+
//# sourceMappingURL=TraceState.js.map
|