@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,150 @@
|
|
|
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 agent = require('../..');
|
|
12
|
+
const { OUTCOME_UNKNOWN } = require('../constants');
|
|
13
|
+
const { traceparentStrFromOTelSpanContext } = require('./otelutils');
|
|
14
|
+
|
|
15
|
+
// This class is used to handle OTel's concept of a `NonRecordingSpan` -- a
|
|
16
|
+
// span that is never sent/exported, but can carry SpanContext (i.e. W3C
|
|
17
|
+
// trace-context) that should be propagated. For a use case, see:
|
|
18
|
+
// "test/opentelemetry-bridge/fixtures/nonrecordingspan-parent.js"
|
|
19
|
+
//
|
|
20
|
+
// This masquerades as a Transaction on the agent's internal run-context
|
|
21
|
+
// tracking. Therefore it needs to support enough of Transaction's interface
|
|
22
|
+
// for that to work.
|
|
23
|
+
//
|
|
24
|
+
// This also needs to support enough of OTel API's `interface Span` -- mostly
|
|
25
|
+
// mimicking the behavior of OTel's internal `NonRecordingSpan`:
|
|
26
|
+
// https://github.com/open-telemetry/opentelemetry-js-api/blob/main/src/trace/NonRecordingSpan.ts
|
|
27
|
+
class OTelBridgeNonRecordingSpan {
|
|
28
|
+
constructor(otelNonRecordingSpan) {
|
|
29
|
+
this._spanContext = otelNonRecordingSpan.spanContext();
|
|
30
|
+
this.name = '';
|
|
31
|
+
this.type = null;
|
|
32
|
+
this.subtype = null;
|
|
33
|
+
this.action = null;
|
|
34
|
+
this.outcome = OUTCOME_UNKNOWN;
|
|
35
|
+
this.ended = false;
|
|
36
|
+
this.result = '';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get id() {
|
|
40
|
+
return this._spanContext.spanId;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get traceparent() {
|
|
44
|
+
return traceparentStrFromOTelSpanContext(this._spanContext);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get ids() {
|
|
48
|
+
return {
|
|
49
|
+
'trace.id': this._spanContext.traceId,
|
|
50
|
+
'transaction.id': this._spanContext.spanId,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setType() {}
|
|
55
|
+
|
|
56
|
+
setLabel(_key, _value, _stringify) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
addLabels(_labels, _stringify) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setOutcome(_outcome) {}
|
|
65
|
+
|
|
66
|
+
startSpan() {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ensureParentId() {
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---- private class Transaction API
|
|
75
|
+
// Only the parts of that API that are used on instances of this class.
|
|
76
|
+
|
|
77
|
+
createSpan() {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// GenericSpan#propagateTraceContextHeaders()
|
|
82
|
+
//
|
|
83
|
+
// Implementation adapted from OTel's W3CTraceContextPropagator#inject().
|
|
84
|
+
propagateTraceContextHeaders(carrier, setter) {
|
|
85
|
+
if (!carrier || !setter) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!this._spanContext || !otel.isSpanContextValid(this._spanContext)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const traceparentStr = traceparentStrFromOTelSpanContext(this._spanContext);
|
|
93
|
+
setter(carrier, 'traceparent', traceparentStr);
|
|
94
|
+
if (agent._conf.useElasticTraceparentHeader) {
|
|
95
|
+
setter(carrier, 'elastic-apm-traceparent', traceparentStr);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this._spanContext.traceState) {
|
|
99
|
+
setter(carrier, 'tracestate', this._spanContext.traceState.serialize());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---- OTel interface Span
|
|
104
|
+
// Implementation adapted from opentelemetry-js-api/src/trace/NonRecordingSpan.ts
|
|
105
|
+
|
|
106
|
+
spanContext() {
|
|
107
|
+
return this._spanContext;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setAttribute(_key, _value) {
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setAttributes(_attributes) {
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
addEvent(_name, _attributes) {
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setStatus(_status) {
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
updateName(_name) {
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
addLink(_link) {
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
addLinks(_links) {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
end(_endTime) {}
|
|
139
|
+
|
|
140
|
+
// isRecording always returns false for NonRecordingSpan.
|
|
141
|
+
isRecording() {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
recordException(_exception, _time) {}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
OTelBridgeNonRecordingSpan,
|
|
150
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
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 { OTelBridgeNonRecordingSpan } = require('./OTelBridgeNonRecordingSpan');
|
|
13
|
+
const { OTelSpan } = require('./OTelSpan');
|
|
14
|
+
const { RunContext } = require('../instrumentation/run-context');
|
|
15
|
+
const Span = require('../instrumentation/span');
|
|
16
|
+
|
|
17
|
+
const OTEL_CONTEXT_KEY = otel.createContextKey(
|
|
18
|
+
'Elastic APM Context Key OTEL CONTEXT',
|
|
19
|
+
);
|
|
20
|
+
let SPAN_KEY = null;
|
|
21
|
+
|
|
22
|
+
// `fetchSpanKey()` is called once during OTel SDK setup to get the `SPAN_KEY`
|
|
23
|
+
// that will be used by the OTel JS API during tracing -- when
|
|
24
|
+
// `otel.trace.setSpan(context, span)` et al are called.
|
|
25
|
+
//
|
|
26
|
+
// The fetched SPAN_KEY is used later by OTelBridgeRunContext to intercept
|
|
27
|
+
// `Context.{get,set,delete}Value` and translate to the agent's internal
|
|
28
|
+
// RunContext semantics for controlling the active/current span.
|
|
29
|
+
function fetchSpanKey() {
|
|
30
|
+
const capturingContext = {
|
|
31
|
+
spanKey: null,
|
|
32
|
+
setValue(key, _value) {
|
|
33
|
+
this.spanKey = key;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const fakeSpan = {};
|
|
37
|
+
otel.trace.setSpan(capturingContext, fakeSpan);
|
|
38
|
+
SPAN_KEY = capturingContext.spanKey;
|
|
39
|
+
if (!SPAN_KEY) {
|
|
40
|
+
throw new Error('could not fetch OTel API "SPAN_KEY"');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// This is a subclass of RunContext that is used when the agent's OTel SDK
|
|
45
|
+
// is enabled. It bridges between the OTel API's `Context` and the agent's
|
|
46
|
+
// `RunContext`.
|
|
47
|
+
//
|
|
48
|
+
// 1. It bridges between `<Context>.setValue(SPAN_KEY, ...)`, `.getValue(SPAN_KEY)`
|
|
49
|
+
// used by the OTel API and the RunContext methods used to track the current
|
|
50
|
+
// transaction and span.
|
|
51
|
+
// 2. It can propagate an OTel API `Context` instance (e.g. the internal
|
|
52
|
+
// `BaseContext` that is exposed by `otel.ROOT_CONTEXT`) and proxy
|
|
53
|
+
// `.getValue(key)` calls to it. See `OTEL_CONTEXT_KEY` below.
|
|
54
|
+
class OTelBridgeRunContext extends RunContext {
|
|
55
|
+
setOTelContext(otelContext) {
|
|
56
|
+
// First, save the `Context` instance in case it holds keys other than SPAN_KEY.
|
|
57
|
+
let runContext = this.setValue(OTEL_CONTEXT_KEY, otelContext);
|
|
58
|
+
// Second, if the `Context` holds a span, then pass that to our `setValue`
|
|
59
|
+
// that knows how to translate that to RunContext semantics.
|
|
60
|
+
const span = otel.trace.getSpan(otelContext);
|
|
61
|
+
if (span) {
|
|
62
|
+
runContext = runContext.setValue(SPAN_KEY, span);
|
|
63
|
+
}
|
|
64
|
+
return runContext;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getValue(key) {
|
|
68
|
+
oblog.apicall('OTelBridgeRunContext.getValue(%o)', key);
|
|
69
|
+
if (key === SPAN_KEY) {
|
|
70
|
+
const curr = this.currSpan() || this.currTransaction();
|
|
71
|
+
if (!curr) {
|
|
72
|
+
return undefined;
|
|
73
|
+
} else if (curr instanceof OTelBridgeNonRecordingSpan) {
|
|
74
|
+
return curr;
|
|
75
|
+
} else {
|
|
76
|
+
return new OTelSpan(curr);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const value = super.getValue(key);
|
|
80
|
+
if (value !== undefined) {
|
|
81
|
+
return value;
|
|
82
|
+
} else {
|
|
83
|
+
// Fallback to possibly-stashed OTel API Context instance.
|
|
84
|
+
const otelContext = super.getValue(OTEL_CONTEXT_KEY);
|
|
85
|
+
if (otelContext) {
|
|
86
|
+
return otelContext.getValue(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setValue(key, value) {
|
|
92
|
+
oblog.apicall('OTelBridgeRunContext.setValue(%o, %s)', key, value);
|
|
93
|
+
if (key === SPAN_KEY) {
|
|
94
|
+
if (value instanceof OTelSpan) {
|
|
95
|
+
if (value._span instanceof Span) {
|
|
96
|
+
return this.enterSpan(value._span);
|
|
97
|
+
} else {
|
|
98
|
+
// assert(value._span instanceof Transaction || value._span instanceof OTelBridgeNonRecordingSpan)
|
|
99
|
+
return this.enterTrans(value._span);
|
|
100
|
+
}
|
|
101
|
+
} else if (
|
|
102
|
+
typeof value.isRecording === 'function' &&
|
|
103
|
+
!value.isRecording()
|
|
104
|
+
) {
|
|
105
|
+
return this.enterTrans(new OTelBridgeNonRecordingSpan(value));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return super.setValue(key, value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
deleteValue(key) {
|
|
112
|
+
oblog.apicall('OTelBridgeRunContext.deleteValue(%o)', key);
|
|
113
|
+
if (key === SPAN_KEY) {
|
|
114
|
+
return this.leaveTrans();
|
|
115
|
+
}
|
|
116
|
+
// TODO: Should perhaps proxy deleteValue(key) to the possible underlying OTEL_CONTEXT_KEY entry.
|
|
117
|
+
return super.deleteValue(key);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
fetchSpanKey,
|
|
123
|
+
OTelBridgeRunContext,
|
|
124
|
+
};
|
|
@@ -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 EventEmitter = require('events');
|
|
10
|
+
const oblog = require('./oblog');
|
|
11
|
+
const { OTelBridgeRunContext } = require('./OTelBridgeRunContext');
|
|
12
|
+
|
|
13
|
+
// Implements interface ContextManager from:
|
|
14
|
+
// https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.0.4/src/context/types.ts#L43
|
|
15
|
+
class OTelContextManager {
|
|
16
|
+
constructor(agent) {
|
|
17
|
+
this._agent = agent;
|
|
18
|
+
this._ins = agent._instrumentation;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
active() {
|
|
22
|
+
oblog.apicall('OTelContextManager.active()');
|
|
23
|
+
return this._ins.currRunContext();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_runContextFromOTelContext(otelContext) {
|
|
27
|
+
let runContext;
|
|
28
|
+
if (otelContext instanceof OTelBridgeRunContext) {
|
|
29
|
+
runContext = otelContext;
|
|
30
|
+
} else {
|
|
31
|
+
// `otelContext` is some object implementing OTel's `interface Context`
|
|
32
|
+
// (typically a `BaseContext` from @opentelemetry/api). We derive a new
|
|
33
|
+
// OTelBridgeRunContext from the root run-context that properly uses
|
|
34
|
+
// the Context.
|
|
35
|
+
runContext = this._ins._runCtxMgr.root().setOTelContext(otelContext);
|
|
36
|
+
}
|
|
37
|
+
return runContext;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
with(otelContext, fn, thisArg, ...args) {
|
|
41
|
+
oblog.apicall(
|
|
42
|
+
'OTelContextManager.with(%s<...>, function %s, ...)',
|
|
43
|
+
otelContext.constructor.name,
|
|
44
|
+
fn.name || '<anonymous>',
|
|
45
|
+
);
|
|
46
|
+
const runContext = this._runContextFromOTelContext(otelContext);
|
|
47
|
+
return this._ins._runCtxMgr.with(runContext, fn, thisArg, ...args);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
bind(otelContext, target) {
|
|
51
|
+
oblog.apicall(
|
|
52
|
+
'OTelContextManager.bind(%s, %s type)',
|
|
53
|
+
otelContext,
|
|
54
|
+
typeof target,
|
|
55
|
+
);
|
|
56
|
+
if (target instanceof EventEmitter) {
|
|
57
|
+
const runContext = this._runContextFromOTelContext(otelContext);
|
|
58
|
+
return this._ins._runCtxMgr.bindEE(runContext, target);
|
|
59
|
+
}
|
|
60
|
+
if (typeof target === 'function') {
|
|
61
|
+
const runContext = this._runContextFromOTelContext(otelContext);
|
|
62
|
+
return this._ins._runCtxMgr.bindFn(runContext, target);
|
|
63
|
+
}
|
|
64
|
+
return target;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
enable() {
|
|
68
|
+
oblog.apicall('OTelContextManager.enable()');
|
|
69
|
+
this._ins._runCtxMgr.enable();
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
disable() {
|
|
74
|
+
oblog.apicall('OTelContextManager.disable()');
|
|
75
|
+
this._ins._runCtxMgr.disable();
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
OTelContextManager,
|
|
82
|
+
};
|
|
@@ -0,0 +1,344 @@
|
|
|
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 assert = require('assert');
|
|
10
|
+
const URL = require('url').URL;
|
|
11
|
+
|
|
12
|
+
const otel = require('@opentelemetry/api');
|
|
13
|
+
|
|
14
|
+
const GenericSpan = require('../instrumentation/generic-span');
|
|
15
|
+
const oblog = require('./oblog');
|
|
16
|
+
const {
|
|
17
|
+
otelSpanContextFromTraceContext,
|
|
18
|
+
epochMsFromOTelTimeInput,
|
|
19
|
+
} = require('./otelutils');
|
|
20
|
+
const {
|
|
21
|
+
RESULT_SUCCESS,
|
|
22
|
+
OUTCOME_UNKNOWN,
|
|
23
|
+
OUTCOME_SUCCESS,
|
|
24
|
+
RESULT_FAILURE,
|
|
25
|
+
OUTCOME_FAILURE,
|
|
26
|
+
} = require('../constants');
|
|
27
|
+
const Span = require('../instrumentation/span');
|
|
28
|
+
const Transaction = require('../instrumentation/transaction');
|
|
29
|
+
|
|
30
|
+
// Based on `isHomogeneousAttributeValueArray` from
|
|
31
|
+
// packages/opentelemetry-core/src/common/attributes.ts
|
|
32
|
+
function isHomogeneousArrayOfStrNumBool(arr) {
|
|
33
|
+
const len = arr.length;
|
|
34
|
+
let elemType = null;
|
|
35
|
+
for (let i = 0; i < len; i++) {
|
|
36
|
+
const elem = arr[i];
|
|
37
|
+
if (elem === undefined || elem === null) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!elemType) {
|
|
41
|
+
elemType = typeof elem;
|
|
42
|
+
if (
|
|
43
|
+
!(
|
|
44
|
+
elemType === 'string' ||
|
|
45
|
+
elemType === 'number' ||
|
|
46
|
+
elemType === 'boolean'
|
|
47
|
+
)
|
|
48
|
+
) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
} else if (typeof elem !== elemType) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Set the given attribute key `k` and `v` according to these OTel rules:
|
|
59
|
+
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute
|
|
60
|
+
function maybeSetOTelAttr(attrs, k, v) {
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
// Is it homogeneous? Nulls and undefineds are allowed.
|
|
63
|
+
if (isHomogeneousArrayOfStrNumBool(v)) {
|
|
64
|
+
attrs[k] = v.slice();
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
switch (typeof v) {
|
|
68
|
+
case 'number':
|
|
69
|
+
case 'boolean':
|
|
70
|
+
attrs[k] = v;
|
|
71
|
+
break;
|
|
72
|
+
case 'string':
|
|
73
|
+
// Truncation (at 1024 bytes) is done in elastic-apm-http-client.
|
|
74
|
+
attrs[k] = v;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// This wraps a core Transaction or Span in the OTel API's `inteface Span`.
|
|
81
|
+
class OTelSpan {
|
|
82
|
+
constructor(span) {
|
|
83
|
+
assert(span instanceof GenericSpan);
|
|
84
|
+
this._span = span;
|
|
85
|
+
this._spanContext = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
toString() {
|
|
89
|
+
return `OTelSpan<${this._span.constructor.name}<${this._span.id}, "${this._span.name}">>`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---- OTel interface Span
|
|
93
|
+
// https://github.com/open-telemetry/opentelemetry-js-api/blob/v1.0.4/src/trace/span.ts
|
|
94
|
+
|
|
95
|
+
spanContext() {
|
|
96
|
+
oblog.apicall('%s.spanContext()', this);
|
|
97
|
+
if (!this._spanContext) {
|
|
98
|
+
this._spanContext = otelSpanContextFromTraceContext(this._span._context);
|
|
99
|
+
}
|
|
100
|
+
return this._spanContext;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setAttribute(key, value) {
|
|
104
|
+
if (this._span.ended || !key || typeof key !== 'string') {
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const attrs = this._span._getOTelAttributes();
|
|
109
|
+
maybeSetOTelAttr(attrs, key, value);
|
|
110
|
+
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setAttributes(attributes) {
|
|
115
|
+
if (this._span.ended || !attributes || typeof attributes !== 'object') {
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const attrs = this._span._getOTelAttributes();
|
|
120
|
+
for (const k in attributes) {
|
|
121
|
+
if (k.length === 0) continue;
|
|
122
|
+
maybeSetOTelAttr(attrs, k, attributes[k]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Span events are not currently supported.
|
|
129
|
+
addEvent(name, attributesOrStartTime, startTime) {
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
setStatus(otelSpanStatus) {
|
|
134
|
+
if (this._span.ended) {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
switch (otelSpanStatus) {
|
|
138
|
+
case otel.SpanStatusCode.ERROR:
|
|
139
|
+
this._span.setOutcome(OUTCOME_FAILURE);
|
|
140
|
+
break;
|
|
141
|
+
case otel.SpanStatusCode.OK:
|
|
142
|
+
this._span.setOutcome(OUTCOME_SUCCESS);
|
|
143
|
+
break;
|
|
144
|
+
case otel.SpanStatusCode.UNSET:
|
|
145
|
+
this._span.setOutcome(OUTCOME_UNKNOWN);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
// Also set transaction.result, similar to the Java APM agent.
|
|
149
|
+
if (this._span instanceof Transaction) {
|
|
150
|
+
switch (otelSpanStatus) {
|
|
151
|
+
case otel.SpanStatusCode.ERROR:
|
|
152
|
+
this._span.result = RESULT_FAILURE;
|
|
153
|
+
break;
|
|
154
|
+
case otel.SpanStatusCode.OK:
|
|
155
|
+
this._span.result = RESULT_SUCCESS;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
updateName(name) {
|
|
163
|
+
if (this._span.ended) {
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
this._span.name = name;
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
addLink(link) {
|
|
171
|
+
this._span.addLink(link);
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
addLinks(links) {
|
|
176
|
+
this._span.addLinks(links);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
end(otelEndTime) {
|
|
181
|
+
oblog.apicall('%s.end(endTime=%s)', this, otelEndTime);
|
|
182
|
+
const endTime =
|
|
183
|
+
otelEndTime === undefined
|
|
184
|
+
? undefined
|
|
185
|
+
: epochMsFromOTelTimeInput(otelEndTime);
|
|
186
|
+
if (this._span instanceof Transaction) {
|
|
187
|
+
this._transCompatMapping();
|
|
188
|
+
this._span.end(undefined, endTime);
|
|
189
|
+
} else {
|
|
190
|
+
assert(this._span instanceof Span);
|
|
191
|
+
this._spanCompatMapping();
|
|
192
|
+
this._span.end(endTime);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// https://github.com/elastic/apm/blob/main/specs/agents/tracing-api-otel.md#compatibility-mapping
|
|
197
|
+
_transCompatMapping() {
|
|
198
|
+
const attrs = this._span._otelAttributes;
|
|
199
|
+
let type = 'unknown';
|
|
200
|
+
if (!attrs) {
|
|
201
|
+
this._span.type = type;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const otelKind = otel.SpanKind[this._span._otelKind]; // map from string to int form
|
|
206
|
+
const isRpc = attrs['rpc.system'] !== undefined;
|
|
207
|
+
const isHttp =
|
|
208
|
+
attrs['http.url'] !== undefined || attrs['http.scheme'] !== undefined;
|
|
209
|
+
const isMessaging = attrs['messaging.system'] !== undefined;
|
|
210
|
+
if (otelKind === otel.SpanKind.SERVER && (isRpc || isHttp)) {
|
|
211
|
+
type = 'request';
|
|
212
|
+
} else if (otelKind === otel.SpanKind.CONSUMER && isMessaging) {
|
|
213
|
+
type = 'messaging';
|
|
214
|
+
} else {
|
|
215
|
+
type = 'unknown';
|
|
216
|
+
}
|
|
217
|
+
this._span.type = type;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// https://github.com/elastic/apm/blob/main/specs/agents/tracing-api-otel.md#compatibility-mapping
|
|
221
|
+
_spanCompatMapping() {
|
|
222
|
+
const attrs = this._span._otelAttributes;
|
|
223
|
+
const otelKind = otel.SpanKind[this._span._otelKind]; // map from string to int form
|
|
224
|
+
if (!attrs) {
|
|
225
|
+
if (otelKind === otel.SpanKind.INTERNAL) {
|
|
226
|
+
this._span.type = 'app';
|
|
227
|
+
this._span.subtype = 'internal';
|
|
228
|
+
} else {
|
|
229
|
+
this._span.type = 'unknown';
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let type;
|
|
235
|
+
let subtype;
|
|
236
|
+
let serviceTargetType = null;
|
|
237
|
+
let serviceTargetName = null;
|
|
238
|
+
|
|
239
|
+
const httpPortFromScheme = function (scheme) {
|
|
240
|
+
if (scheme === 'http') {
|
|
241
|
+
return 80;
|
|
242
|
+
} else if (scheme === 'https') {
|
|
243
|
+
return 443;
|
|
244
|
+
}
|
|
245
|
+
return -1;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Extracts 'host:port' from URL.
|
|
249
|
+
const parseNetName = function (url) {
|
|
250
|
+
let u;
|
|
251
|
+
try {
|
|
252
|
+
u = new URL(url); // https://developer.mozilla.org/en-US/docs/Web/API/URL
|
|
253
|
+
} catch (_err) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
if (u.port !== '') {
|
|
257
|
+
return u.host; // host:port already in URL
|
|
258
|
+
} else {
|
|
259
|
+
var port = httpPortFromScheme(
|
|
260
|
+
u.protocol.substring(0, u.protocol.length - 1),
|
|
261
|
+
);
|
|
262
|
+
return port > 0 ? u.hostname + ':' + port : u.hostname;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
let netPort = attrs['net.peer.port'] || -1;
|
|
267
|
+
const netPeer = attrs['net.peer.name'] || attrs['net.peer.ip'];
|
|
268
|
+
let netName = netPeer; // netName includes port, if provided
|
|
269
|
+
if (netName && netPort > 0) {
|
|
270
|
+
netName += ':' + netPort;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (attrs['db.system']) {
|
|
274
|
+
type = 'db';
|
|
275
|
+
subtype = attrs['db.system'];
|
|
276
|
+
serviceTargetType = subtype;
|
|
277
|
+
serviceTargetName = attrs['db.name'] || null;
|
|
278
|
+
} else if (attrs['messaging.system']) {
|
|
279
|
+
type = 'messaging';
|
|
280
|
+
subtype = attrs['messaging.system'];
|
|
281
|
+
if (!netName && attrs['messaging.url']) {
|
|
282
|
+
netName = parseNetName(attrs['messaging.url']);
|
|
283
|
+
}
|
|
284
|
+
serviceTargetType = subtype;
|
|
285
|
+
serviceTargetName = attrs['messaging.destination'] || null;
|
|
286
|
+
} else if (attrs['rpc.system']) {
|
|
287
|
+
type = 'external';
|
|
288
|
+
subtype = attrs['rpc.system'];
|
|
289
|
+
serviceTargetType = subtype;
|
|
290
|
+
serviceTargetName = netName || attrs['rpc.service'] || null;
|
|
291
|
+
} else if (attrs['http.url'] || attrs['http.scheme']) {
|
|
292
|
+
type = 'external';
|
|
293
|
+
subtype = 'http';
|
|
294
|
+
serviceTargetType = 'http';
|
|
295
|
+
const httpHost = attrs['http.host'] || netPeer;
|
|
296
|
+
if (httpHost) {
|
|
297
|
+
if (netPort < 0) {
|
|
298
|
+
netPort = httpPortFromScheme(attrs['http.scheme']);
|
|
299
|
+
}
|
|
300
|
+
serviceTargetName = netPort < 0 ? httpHost : httpHost + ':' + netPort;
|
|
301
|
+
} else if (attrs['http.url']) {
|
|
302
|
+
serviceTargetName = parseNetName(attrs['http.url']);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (type === undefined) {
|
|
307
|
+
if (otelKind === otel.SpanKind.INTERNAL) {
|
|
308
|
+
type = 'app';
|
|
309
|
+
subtype = 'internal';
|
|
310
|
+
} else {
|
|
311
|
+
type = 'unknown';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this._span.type = type;
|
|
316
|
+
if (subtype) {
|
|
317
|
+
this._span.subtype = subtype;
|
|
318
|
+
}
|
|
319
|
+
if (serviceTargetType || serviceTargetName) {
|
|
320
|
+
this._span.setServiceTarget(serviceTargetType, serviceTargetName);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
isRecording() {
|
|
325
|
+
return !this._span.ended && this._span.sampled;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
recordException(otelException, otelTime) {
|
|
329
|
+
const errOpts = {
|
|
330
|
+
parent: this._span,
|
|
331
|
+
captureAttributes: false,
|
|
332
|
+
skipOutcome: true,
|
|
333
|
+
};
|
|
334
|
+
if (otelTime !== undefined) {
|
|
335
|
+
errOpts.timestamp = epochMsFromOTelTimeInput(otelTime);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this._span._agent.captureError(otelException, errOpts);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
module.exports = {
|
|
343
|
+
OTelSpan,
|
|
344
|
+
};
|