@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,232 @@
|
|
|
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 TYPE = 'messaging';
|
|
10
|
+
const SUBTYPE = 'sns';
|
|
11
|
+
const ACTION = 'publish';
|
|
12
|
+
const PHONE_NUMBER = '<PHONE_NUMBER>';
|
|
13
|
+
const MAX_SNS_MESSAGE_ATTRIBUTES = 10;
|
|
14
|
+
|
|
15
|
+
function getArnOrPhoneNumberFromRequest(request) {
|
|
16
|
+
let arn = request && request.params && request.params.TopicArn;
|
|
17
|
+
if (!arn) {
|
|
18
|
+
arn = request && request.params && request.params.TargetArn;
|
|
19
|
+
}
|
|
20
|
+
if (!arn) {
|
|
21
|
+
arn = request && request.params && request.params.PhoneNumber;
|
|
22
|
+
}
|
|
23
|
+
return arn;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getRegionFromRequest(request) {
|
|
27
|
+
return (
|
|
28
|
+
request &&
|
|
29
|
+
request.service &&
|
|
30
|
+
request.service.config &&
|
|
31
|
+
request.service.config.region
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getSpanNameFromRequest(request) {
|
|
36
|
+
const topicName = getDestinationNameFromRequest(request);
|
|
37
|
+
return `SNS PUBLISH to ${topicName}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getMessageContextFromRequest(request) {
|
|
41
|
+
return {
|
|
42
|
+
queue: {
|
|
43
|
+
name: getDestinationNameFromRequest(request),
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getAddressFromRequest(request) {
|
|
49
|
+
return (
|
|
50
|
+
request &&
|
|
51
|
+
request.service &&
|
|
52
|
+
request.service.endpoint &&
|
|
53
|
+
request.service.endpoint.hostname
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getPortFromRequest(request) {
|
|
58
|
+
return (
|
|
59
|
+
request &&
|
|
60
|
+
request.service &&
|
|
61
|
+
request.service.endpoint &&
|
|
62
|
+
request.service.endpoint.port
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getMessageDestinationContextFromRequest(request) {
|
|
67
|
+
return {
|
|
68
|
+
address: getAddressFromRequest(request),
|
|
69
|
+
port: getPortFromRequest(request),
|
|
70
|
+
cloud: {
|
|
71
|
+
region: getRegionFromRequest(request),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getDestinationNameFromRequest(request) {
|
|
77
|
+
const phoneNumber = request && request.params && request.params.PhoneNumber;
|
|
78
|
+
if (phoneNumber) {
|
|
79
|
+
return PHONE_NUMBER;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const topicArn = request && request.params && request.params.TopicArn;
|
|
83
|
+
const targetArn = request && request.params && request.params.TargetArn;
|
|
84
|
+
|
|
85
|
+
if (topicArn) {
|
|
86
|
+
const parts = topicArn.split(':');
|
|
87
|
+
const topicName = parts.pop();
|
|
88
|
+
return topicName;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (targetArn) {
|
|
92
|
+
const fullName = targetArn.split(':').pop();
|
|
93
|
+
if (fullName.lastIndexOf('/') !== -1) {
|
|
94
|
+
return fullName.substring(0, fullName.lastIndexOf('/'));
|
|
95
|
+
} else {
|
|
96
|
+
return fullName;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function shouldIgnoreRequest(request, agent) {
|
|
102
|
+
if (request.operation !== 'publish') {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// is the named topic on our ignore list?
|
|
107
|
+
if (agent._conf && agent._conf.ignoreMessageQueuesRegExp) {
|
|
108
|
+
const queueName = getArnOrPhoneNumberFromRequest(request);
|
|
109
|
+
if (queueName) {
|
|
110
|
+
for (const rule of agent._conf.ignoreMessageQueuesRegExp) {
|
|
111
|
+
if (rule.test(queueName)) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Though our spec only mentions a 10-message-attribute limit for *SQS*, we'll
|
|
122
|
+
// do the same limit here, because
|
|
123
|
+
// https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html
|
|
124
|
+
// mentions the 10-message-attribute limit for SQS subscriptions.
|
|
125
|
+
function propagateTraceContextInMessageAttributes(
|
|
126
|
+
parentSpan,
|
|
127
|
+
msgParams,
|
|
128
|
+
log,
|
|
129
|
+
targetId,
|
|
130
|
+
) {
|
|
131
|
+
const msgAttrs = msgParams.MessageAttributes;
|
|
132
|
+
if (
|
|
133
|
+
msgAttrs &&
|
|
134
|
+
Object.keys(msgAttrs).length + 2 > MAX_SNS_MESSAGE_ATTRIBUTES
|
|
135
|
+
) {
|
|
136
|
+
log.warn(
|
|
137
|
+
'cannot propagate trace-context with SNS message to %s, too many MessageAttributes',
|
|
138
|
+
targetId,
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
parentSpan.propagateTraceContextHeaders(
|
|
142
|
+
msgAttrs,
|
|
143
|
+
function (msgAttrs, name, value) {
|
|
144
|
+
if (name.startsWith('elastic-')) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
msgAttrs[name] = { DataType: 'String', StringValue: value };
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function instrumentationSns(
|
|
154
|
+
orig,
|
|
155
|
+
origArguments,
|
|
156
|
+
request,
|
|
157
|
+
AWS,
|
|
158
|
+
agent,
|
|
159
|
+
{ version, enabled },
|
|
160
|
+
) {
|
|
161
|
+
if (shouldIgnoreRequest(request, agent)) {
|
|
162
|
+
return orig.apply(request, origArguments);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const ins = agent._instrumentation;
|
|
166
|
+
const log = agent.logger;
|
|
167
|
+
const parentRunContext = ins.currRunContext();
|
|
168
|
+
const name = getSpanNameFromRequest(request);
|
|
169
|
+
const span = ins.createSpan(name, TYPE, SUBTYPE, ACTION, { exitSpan: true });
|
|
170
|
+
|
|
171
|
+
// W3C trace-context propagation.
|
|
172
|
+
const parent =
|
|
173
|
+
span || parentRunContext.currSpan() || parentRunContext.currTransaction();
|
|
174
|
+
if (parent) {
|
|
175
|
+
// Currently only instrumenting a 'publish' operation, for which we want
|
|
176
|
+
// to propagate trace-context.
|
|
177
|
+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#publish-property
|
|
178
|
+
const paramsCopy = Object.assign({}, request.params);
|
|
179
|
+
paramsCopy.MessageAttributes = Object.assign(
|
|
180
|
+
{},
|
|
181
|
+
paramsCopy.MessageAttributes,
|
|
182
|
+
);
|
|
183
|
+
request.params = paramsCopy;
|
|
184
|
+
const targetId = getArnOrPhoneNumberFromRequest(request);
|
|
185
|
+
propagateTraceContextInMessageAttributes(
|
|
186
|
+
parent,
|
|
187
|
+
request.params,
|
|
188
|
+
log,
|
|
189
|
+
targetId,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!span) {
|
|
194
|
+
return orig.apply(request, origArguments);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
span._setDestinationContext(getMessageDestinationContextFromRequest(request));
|
|
198
|
+
span.setMessageContext(getMessageContextFromRequest(request));
|
|
199
|
+
|
|
200
|
+
const onComplete = function (response) {
|
|
201
|
+
if (response && response.error) {
|
|
202
|
+
agent.captureError(response.error);
|
|
203
|
+
}
|
|
204
|
+
span.end();
|
|
205
|
+
};
|
|
206
|
+
// Bind onComplete to the span's run context so that `captureError` picks
|
|
207
|
+
// up the correct currentSpan.
|
|
208
|
+
const spanRunContext = parentRunContext.enterSpan(span);
|
|
209
|
+
request.on(
|
|
210
|
+
'complete',
|
|
211
|
+
ins.bindFunctionToRunContext(spanRunContext, onComplete),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const cb = origArguments[origArguments.length - 1];
|
|
215
|
+
if (typeof cb === 'function') {
|
|
216
|
+
origArguments[origArguments.length - 1] = ins.bindFunctionToRunContext(
|
|
217
|
+
parentRunContext,
|
|
218
|
+
cb,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
return ins.withRunContext(spanRunContext, orig, request, ...origArguments);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
instrumentationSns,
|
|
226
|
+
|
|
227
|
+
// exported for testing
|
|
228
|
+
getSpanNameFromRequest,
|
|
229
|
+
getDestinationNameFromRequest,
|
|
230
|
+
getMessageDestinationContextFromRequest,
|
|
231
|
+
getArnOrPhoneNumberFromRequest,
|
|
232
|
+
};
|
|
@@ -0,0 +1,361 @@
|
|
|
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 { URL } = require('url');
|
|
10
|
+
const {
|
|
11
|
+
MAX_MESSAGES_PROCESSED_FOR_TRACE_CONTEXT,
|
|
12
|
+
} = require('../../../constants');
|
|
13
|
+
|
|
14
|
+
const OPERATIONS_TO_ACTIONS = {
|
|
15
|
+
deleteMessage: 'delete',
|
|
16
|
+
deleteMessageBatch: 'delete_batch',
|
|
17
|
+
receiveMessage: 'poll',
|
|
18
|
+
sendMessageBatch: 'send_batch',
|
|
19
|
+
sendMessage: 'send',
|
|
20
|
+
unknown: 'unknown',
|
|
21
|
+
};
|
|
22
|
+
const OPERATIONS = Object.keys(OPERATIONS_TO_ACTIONS);
|
|
23
|
+
const TYPE = 'messaging';
|
|
24
|
+
const SUBTYPE = 'sqs';
|
|
25
|
+
const queueMetrics = new Map();
|
|
26
|
+
|
|
27
|
+
const MAX_SQS_MESSAGE_ATTRIBUTES = 10;
|
|
28
|
+
|
|
29
|
+
// Returns Message Queue action from AWS SDK method name
|
|
30
|
+
function getActionFromRequest(request) {
|
|
31
|
+
request = request || {};
|
|
32
|
+
const operation = request.operation ? request.operation : 'unknown';
|
|
33
|
+
const action = OPERATIONS_TO_ACTIONS[operation];
|
|
34
|
+
|
|
35
|
+
return action;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Returns preposition to use in span name
|
|
39
|
+
//
|
|
40
|
+
// POLL from ...
|
|
41
|
+
// SEND to ...
|
|
42
|
+
function getToFromFromOperation(operation) {
|
|
43
|
+
let result = 'from';
|
|
44
|
+
if (operation === 'sendMessage' || operation === 'sendMessageBatch') {
|
|
45
|
+
result = 'to';
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Parses queue/topic name from AWS queue URL
|
|
51
|
+
function getQueueNameFromRequest(request) {
|
|
52
|
+
const unknown = 'unknown';
|
|
53
|
+
if (!request || !request.params || !request.params.QueueUrl) {
|
|
54
|
+
return unknown;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(request.params.QueueUrl);
|
|
58
|
+
return url.pathname.split('/').pop();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return unknown;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Parses region name from AWS service configuration
|
|
65
|
+
function getRegionFromRequest(request) {
|
|
66
|
+
const region =
|
|
67
|
+
request &&
|
|
68
|
+
request.service &&
|
|
69
|
+
request.service.config &&
|
|
70
|
+
request.service.config.region;
|
|
71
|
+
return region || '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getAddressFromRequest(request) {
|
|
75
|
+
return (
|
|
76
|
+
request &&
|
|
77
|
+
request.service &&
|
|
78
|
+
request.service.endpoint &&
|
|
79
|
+
request.service.endpoint.hostname
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getPortFromRequest(request) {
|
|
84
|
+
return (
|
|
85
|
+
request &&
|
|
86
|
+
request.service &&
|
|
87
|
+
request.service.endpoint &&
|
|
88
|
+
request.service.endpoint.port
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Creates message destination context suitable for setDestinationContext
|
|
93
|
+
function getMessageDestinationContextFromRequest(request) {
|
|
94
|
+
const destination = {
|
|
95
|
+
address: getAddressFromRequest(request),
|
|
96
|
+
port: getPortFromRequest(request),
|
|
97
|
+
cloud: {
|
|
98
|
+
region: getRegionFromRequest(request),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
return destination;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// create message context suitable for setMessageContext
|
|
105
|
+
function getMessageContextFromRequest(request) {
|
|
106
|
+
const message = {
|
|
107
|
+
queue: {
|
|
108
|
+
name: getQueueNameFromRequest(request),
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return message;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Record queue related metrics
|
|
115
|
+
//
|
|
116
|
+
// Creates metric collector objects on first run, and
|
|
117
|
+
// updates their data with data from received messages
|
|
118
|
+
function recordMetrics(queueName, data, agent) {
|
|
119
|
+
const messages = data && data.Messages;
|
|
120
|
+
if (!messages || messages.length < 1) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!queueMetrics.get(queueName)) {
|
|
124
|
+
const collector = agent._metrics.createQueueMetricsCollector(queueName);
|
|
125
|
+
if (!collector) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
queueMetrics.set(queueName, collector);
|
|
129
|
+
}
|
|
130
|
+
const metrics = queueMetrics.get(queueName);
|
|
131
|
+
|
|
132
|
+
for (const message of messages) {
|
|
133
|
+
const sentTimestamp =
|
|
134
|
+
message.Attributes && message.Attributes.SentTimestamp;
|
|
135
|
+
const delay = new Date().getTime() - sentTimestamp;
|
|
136
|
+
metrics.updateStats(delay);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Creates the span name from request information
|
|
141
|
+
function getSpanNameFromRequest(request) {
|
|
142
|
+
const action = getActionFromRequest(request);
|
|
143
|
+
const toFrom = getToFromFromOperation(request.operation);
|
|
144
|
+
const queueName = getQueueNameFromRequest(request);
|
|
145
|
+
|
|
146
|
+
const name = `${SUBTYPE.toUpperCase()} ${action.toUpperCase()} ${toFrom} ${queueName}`;
|
|
147
|
+
return name;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Extract span links from up to 1000 messages in this batch.
|
|
151
|
+
// https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-messaging.md#receiving-trace-context
|
|
152
|
+
//
|
|
153
|
+
// A span link is created from a `traceparent` message attribute in a message.
|
|
154
|
+
// `msg.messageAttributes` is of the form:
|
|
155
|
+
// { <attribute-name>: { DataType: <attr-type>, StringValue: <attr-value>, ... } }
|
|
156
|
+
// For example:
|
|
157
|
+
// { traceparent: { DataType: 'String', StringValue: 'test-traceparent' } }
|
|
158
|
+
function getSpanLinksFromResponseData(data) {
|
|
159
|
+
if (!data || !data.Messages || data.Messages.length === 0) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const links = [];
|
|
163
|
+
const limit = Math.min(
|
|
164
|
+
data.Messages.length,
|
|
165
|
+
MAX_MESSAGES_PROCESSED_FOR_TRACE_CONTEXT,
|
|
166
|
+
);
|
|
167
|
+
for (let i = 0; i < limit; i++) {
|
|
168
|
+
const attrs = data.Messages[i].MessageAttributes;
|
|
169
|
+
if (!attrs) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let traceparent;
|
|
174
|
+
const attrNames = Object.keys(attrs);
|
|
175
|
+
for (let j = 0; j < attrNames.length; j++) {
|
|
176
|
+
const attrVal = attrs[attrNames[j]];
|
|
177
|
+
if (attrVal.DataType !== 'String') {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const attrNameLc = attrNames[j].toLowerCase();
|
|
181
|
+
if (attrNameLc === 'traceparent') {
|
|
182
|
+
traceparent = attrVal.StringValue;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (traceparent) {
|
|
187
|
+
links.push({ context: traceparent });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return links;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function shouldIgnoreRequest(request, agent) {
|
|
194
|
+
const operation = request && request.operation;
|
|
195
|
+
// are we interested in this operation/method call?
|
|
196
|
+
if (OPERATIONS.indexOf(operation) === -1) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// is the named queue on our ignore list?
|
|
201
|
+
if (agent._conf && agent._conf.ignoreMessageQueuesRegExp) {
|
|
202
|
+
const queueName = getQueueNameFromRequest(request);
|
|
203
|
+
for (const rule of agent._conf.ignoreMessageQueuesRegExp) {
|
|
204
|
+
if (rule.test(queueName)) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function propagateTraceContextInMessageAttributes(
|
|
214
|
+
parentSpan,
|
|
215
|
+
msgParams,
|
|
216
|
+
log,
|
|
217
|
+
queueUrl,
|
|
218
|
+
) {
|
|
219
|
+
const msgAttrs = msgParams.MessageAttributes;
|
|
220
|
+
if (
|
|
221
|
+
msgAttrs &&
|
|
222
|
+
Object.keys(msgAttrs).length + 2 > MAX_SQS_MESSAGE_ATTRIBUTES
|
|
223
|
+
) {
|
|
224
|
+
log.warn(
|
|
225
|
+
{ QueueUrl: queueUrl },
|
|
226
|
+
'cannot propagate trace-context with SQS message, too many MessageAttributes',
|
|
227
|
+
);
|
|
228
|
+
} else {
|
|
229
|
+
parentSpan.propagateTraceContextHeaders(
|
|
230
|
+
msgAttrs,
|
|
231
|
+
function (msgAttrs, name, value) {
|
|
232
|
+
if (name.startsWith('elastic-')) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
msgAttrs[name] = { DataType: 'String', StringValue: value };
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Main entrypoint for SQS instrumentation
|
|
242
|
+
//
|
|
243
|
+
// Must call (or one of its function calls must call) the
|
|
244
|
+
// `orig` function/method
|
|
245
|
+
function instrumentationSqs(
|
|
246
|
+
orig,
|
|
247
|
+
origArguments,
|
|
248
|
+
request,
|
|
249
|
+
AWS,
|
|
250
|
+
agent,
|
|
251
|
+
{ version, enabled },
|
|
252
|
+
) {
|
|
253
|
+
if (shouldIgnoreRequest(request, agent)) {
|
|
254
|
+
return orig.apply(request, origArguments);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const ins = agent._instrumentation;
|
|
258
|
+
const log = agent.logger;
|
|
259
|
+
const action = getActionFromRequest(request);
|
|
260
|
+
const name = getSpanNameFromRequest(request);
|
|
261
|
+
const parentRunContext = ins.currRunContext();
|
|
262
|
+
const span = ins.createSpan(name, TYPE, SUBTYPE, action, { exitSpan: true });
|
|
263
|
+
|
|
264
|
+
// W3C trace-context propagation.
|
|
265
|
+
const parent =
|
|
266
|
+
span || parentRunContext.currSpan() || parentRunContext.currTransaction();
|
|
267
|
+
if (parent) {
|
|
268
|
+
if (!request.params || typeof request.params !== 'object') {
|
|
269
|
+
// Pass. This is a sanity guard against bogus user input.
|
|
270
|
+
} else if (request.operation === 'sendMessage') {
|
|
271
|
+
const paramsCopy = Object.assign({}, request.params);
|
|
272
|
+
paramsCopy.MessageAttributes = Object.assign(
|
|
273
|
+
{},
|
|
274
|
+
paramsCopy.MessageAttributes,
|
|
275
|
+
);
|
|
276
|
+
request.params = paramsCopy;
|
|
277
|
+
propagateTraceContextInMessageAttributes(
|
|
278
|
+
parent,
|
|
279
|
+
request.params,
|
|
280
|
+
log,
|
|
281
|
+
request.params.QueueUrl,
|
|
282
|
+
);
|
|
283
|
+
} else if (
|
|
284
|
+
request.operation === 'sendMessageBatch' &&
|
|
285
|
+
Array.isArray(request.params.Entries)
|
|
286
|
+
) {
|
|
287
|
+
const paramsCopy = Object.assign({}, request.params);
|
|
288
|
+
paramsCopy.Entries = paramsCopy.Entries.map((msgParams) => {
|
|
289
|
+
const msgParamsCopy = Object.assign({}, msgParams);
|
|
290
|
+
msgParamsCopy.MessageAttributes = Object.assign(
|
|
291
|
+
{},
|
|
292
|
+
msgParamsCopy.MessageAttributes,
|
|
293
|
+
);
|
|
294
|
+
propagateTraceContextInMessageAttributes(
|
|
295
|
+
parent,
|
|
296
|
+
msgParamsCopy,
|
|
297
|
+
log,
|
|
298
|
+
request.params.QueueUrl,
|
|
299
|
+
);
|
|
300
|
+
return msgParamsCopy;
|
|
301
|
+
});
|
|
302
|
+
request.params = paramsCopy;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!span) {
|
|
307
|
+
return orig.apply(request, origArguments);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
span._setDestinationContext(getMessageDestinationContextFromRequest(request));
|
|
311
|
+
span.setMessageContext(getMessageContextFromRequest(request));
|
|
312
|
+
|
|
313
|
+
const onComplete = function (response) {
|
|
314
|
+
if (response && response.error) {
|
|
315
|
+
agent.captureError(response.error);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const receiveMsgData =
|
|
319
|
+
request.operation === 'receiveMessage' && response && response.data;
|
|
320
|
+
if (receiveMsgData) {
|
|
321
|
+
const links = getSpanLinksFromResponseData(receiveMsgData);
|
|
322
|
+
if (links) {
|
|
323
|
+
span.addLinks(links);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
span.end();
|
|
328
|
+
|
|
329
|
+
if (receiveMsgData) {
|
|
330
|
+
recordMetrics(getQueueNameFromRequest(request), receiveMsgData, agent);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
// Bind onComplete to the span's run context so that `captureError` picks
|
|
334
|
+
// up the correct currentSpan.
|
|
335
|
+
const spanRunContext = parentRunContext.enterSpan(span);
|
|
336
|
+
request.on(
|
|
337
|
+
'complete',
|
|
338
|
+
ins.bindFunctionToRunContext(spanRunContext, onComplete),
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const cb = origArguments[origArguments.length - 1];
|
|
342
|
+
if (typeof cb === 'function') {
|
|
343
|
+
origArguments[origArguments.length - 1] = ins.bindFunctionToRunContext(
|
|
344
|
+
parentRunContext,
|
|
345
|
+
cb,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
return ins.withRunContext(spanRunContext, orig, request, ...origArguments);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
module.exports = {
|
|
352
|
+
instrumentationSqs,
|
|
353
|
+
|
|
354
|
+
// exported for tests
|
|
355
|
+
getToFromFromOperation,
|
|
356
|
+
getActionFromRequest,
|
|
357
|
+
getQueueNameFromRequest,
|
|
358
|
+
getRegionFromRequest,
|
|
359
|
+
getMessageDestinationContextFromRequest,
|
|
360
|
+
shouldIgnoreRequest,
|
|
361
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and other contributors where applicable.
|
|
3
|
+
* Licensed under the BSD 2-Clause License; you may not use this file except in
|
|
4
|
+
* compliance with the BSD 2-Clause License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
const semver = require('semver');
|
|
9
|
+
const shimmer = require('../shimmer');
|
|
10
|
+
const { instrumentationS3 } = require('./aws-sdk/s3');
|
|
11
|
+
const { instrumentationSqs } = require('./aws-sdk/sqs');
|
|
12
|
+
const { instrumentationDynamoDb } = require('./aws-sdk/dynamodb.js');
|
|
13
|
+
const { instrumentationSns } = require('./aws-sdk/sns.js');
|
|
14
|
+
|
|
15
|
+
const instrumentorFromSvcId = {
|
|
16
|
+
s3: instrumentationS3,
|
|
17
|
+
sqs: instrumentationSqs,
|
|
18
|
+
dynamodb: instrumentationDynamoDb,
|
|
19
|
+
sns: instrumentationSns,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Called in place of AWS.Request.send and AWS.Request.promise
|
|
23
|
+
//
|
|
24
|
+
// Determines which amazon service an API request is for
|
|
25
|
+
// and then passes call on to an appropriate instrumentation
|
|
26
|
+
// function.
|
|
27
|
+
function instrumentOperation(
|
|
28
|
+
orig,
|
|
29
|
+
origArguments,
|
|
30
|
+
request,
|
|
31
|
+
AWS,
|
|
32
|
+
agent,
|
|
33
|
+
{ version, enabled },
|
|
34
|
+
) {
|
|
35
|
+
const instrumentor = instrumentorFromSvcId[request.service.serviceIdentifier];
|
|
36
|
+
if (instrumentor) {
|
|
37
|
+
return instrumentor(orig, origArguments, request, AWS, agent, {
|
|
38
|
+
version,
|
|
39
|
+
enabled,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// if we're still here, then we still need to call the original method
|
|
44
|
+
return orig.apply(request, origArguments);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// main entry point for aws-sdk instrumentation
|
|
48
|
+
module.exports = function (AWS, agent, { version, enabled }) {
|
|
49
|
+
if (!enabled) return AWS;
|
|
50
|
+
if (!semver.satisfies(version, '>1 <3')) {
|
|
51
|
+
agent.logger.debug(
|
|
52
|
+
'aws-sdk version %s not supported - aborting...',
|
|
53
|
+
version,
|
|
54
|
+
);
|
|
55
|
+
return AWS;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
shimmer.wrap(AWS.Request.prototype, 'send', function (orig) {
|
|
59
|
+
return function _wrappedAWSRequestSend() {
|
|
60
|
+
return instrumentOperation(orig, arguments, this, AWS, agent, {
|
|
61
|
+
version,
|
|
62
|
+
enabled,
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
shimmer.wrap(AWS.Request.prototype, 'promise', function (orig) {
|
|
68
|
+
return function _wrappedAWSRequestPromise() {
|
|
69
|
+
return instrumentOperation(orig, arguments, this, AWS, agent, {
|
|
70
|
+
version,
|
|
71
|
+
enabled,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
return AWS;
|
|
76
|
+
};
|