@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.
Files changed (154) hide show
  1. package/LICENSE +26 -0
  2. package/NOTICE.md +442 -0
  3. package/README.md +48 -0
  4. package/changes.json +78 -0
  5. package/index.d.ts +398 -0
  6. package/index.js +11 -0
  7. package/lib/InflightEventSet.js +53 -0
  8. package/lib/activation-method.js +119 -0
  9. package/lib/agent.js +941 -0
  10. package/lib/apm-client/apm-client.js +313 -0
  11. package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
  12. package/lib/apm-client/http-apm-client/README.md +485 -0
  13. package/lib/apm-client/http-apm-client/central-config.js +41 -0
  14. package/lib/apm-client/http-apm-client/container-info.js +111 -0
  15. package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
  16. package/lib/apm-client/http-apm-client/index.js +1975 -0
  17. package/lib/apm-client/http-apm-client/logging.js +31 -0
  18. package/lib/apm-client/http-apm-client/ndjson.js +20 -0
  19. package/lib/apm-client/http-apm-client/truncate.js +434 -0
  20. package/lib/apm-client/noop-apm-client.js +73 -0
  21. package/lib/async-hooks-polyfill.js +58 -0
  22. package/lib/cloud-metadata/aws.js +175 -0
  23. package/lib/cloud-metadata/azure.js +123 -0
  24. package/lib/cloud-metadata/callback-coordination.js +159 -0
  25. package/lib/cloud-metadata/gcp.js +133 -0
  26. package/lib/cloud-metadata/index.js +175 -0
  27. package/lib/config/config.js +458 -0
  28. package/lib/config/normalizers.js +701 -0
  29. package/lib/config/schema.js +1007 -0
  30. package/lib/constants.js +35 -0
  31. package/lib/errors.js +303 -0
  32. package/lib/filters/sanitize-field-names.js +69 -0
  33. package/lib/http-request.js +249 -0
  34. package/lib/instrumentation/azure-functions.js +519 -0
  35. package/lib/instrumentation/context.js +56 -0
  36. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  37. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  38. package/lib/instrumentation/express-utils.js +91 -0
  39. package/lib/instrumentation/generic-span.js +322 -0
  40. package/lib/instrumentation/http-shared.js +424 -0
  41. package/lib/instrumentation/ids.js +39 -0
  42. package/lib/instrumentation/index.js +1127 -0
  43. package/lib/instrumentation/modules/@apollo/server.js +30 -0
  44. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  45. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  46. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  47. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  48. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  49. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  50. package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
  51. package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
  52. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  53. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  54. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  55. package/lib/instrumentation/modules/_lambda-handler.js +40 -0
  56. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  57. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  58. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  59. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  60. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  61. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  62. package/lib/instrumentation/modules/bluebird.js +93 -0
  63. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  64. package/lib/instrumentation/modules/elasticsearch.js +191 -0
  65. package/lib/instrumentation/modules/express-graphql.js +66 -0
  66. package/lib/instrumentation/modules/express-queue.js +28 -0
  67. package/lib/instrumentation/modules/express.js +162 -0
  68. package/lib/instrumentation/modules/fastify.js +172 -0
  69. package/lib/instrumentation/modules/finalhandler.js +41 -0
  70. package/lib/instrumentation/modules/generic-pool.js +85 -0
  71. package/lib/instrumentation/modules/graphql.js +256 -0
  72. package/lib/instrumentation/modules/handlebars.js +22 -0
  73. package/lib/instrumentation/modules/http.js +112 -0
  74. package/lib/instrumentation/modules/http2.js +320 -0
  75. package/lib/instrumentation/modules/https.js +68 -0
  76. package/lib/instrumentation/modules/ioredis.js +94 -0
  77. package/lib/instrumentation/modules/jade.js +18 -0
  78. package/lib/instrumentation/modules/kafkajs.js +476 -0
  79. package/lib/instrumentation/modules/knex.js +91 -0
  80. package/lib/instrumentation/modules/koa-router.js +74 -0
  81. package/lib/instrumentation/modules/koa.js +15 -0
  82. package/lib/instrumentation/modules/memcached.js +99 -0
  83. package/lib/instrumentation/modules/mimic-response.js +45 -0
  84. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  85. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  86. package/lib/instrumentation/modules/mongodb.js +259 -0
  87. package/lib/instrumentation/modules/mysql.js +200 -0
  88. package/lib/instrumentation/modules/mysql2.js +140 -0
  89. package/lib/instrumentation/modules/pg.js +148 -0
  90. package/lib/instrumentation/modules/pug.js +18 -0
  91. package/lib/instrumentation/modules/redis.js +176 -0
  92. package/lib/instrumentation/modules/restify.js +52 -0
  93. package/lib/instrumentation/modules/tedious.js +159 -0
  94. package/lib/instrumentation/modules/undici.js +270 -0
  95. package/lib/instrumentation/modules/ws.js +59 -0
  96. package/lib/instrumentation/noop-transaction.js +81 -0
  97. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  98. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  99. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  100. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  101. package/lib/instrumentation/run-context/RunContext.js +151 -0
  102. package/lib/instrumentation/run-context/index.js +23 -0
  103. package/lib/instrumentation/shimmer.js +123 -0
  104. package/lib/instrumentation/span-compression.js +239 -0
  105. package/lib/instrumentation/span.js +621 -0
  106. package/lib/instrumentation/template-shared.js +43 -0
  107. package/lib/instrumentation/timer.js +84 -0
  108. package/lib/instrumentation/transaction.js +571 -0
  109. package/lib/lambda.js +992 -0
  110. package/lib/load-source-map.js +100 -0
  111. package/lib/logging.js +212 -0
  112. package/lib/metrics/index.js +92 -0
  113. package/lib/metrics/platforms/generic/index.js +40 -0
  114. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  115. package/lib/metrics/platforms/generic/process-top.js +157 -0
  116. package/lib/metrics/platforms/generic/stats.js +34 -0
  117. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  118. package/lib/metrics/platforms/linux/index.js +19 -0
  119. package/lib/metrics/platforms/linux/stats.js +213 -0
  120. package/lib/metrics/queue.js +90 -0
  121. package/lib/metrics/registry.js +52 -0
  122. package/lib/metrics/reporter.js +119 -0
  123. package/lib/metrics/runtime.js +77 -0
  124. package/lib/middleware/connect.js +16 -0
  125. package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
  126. package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
  127. package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
  128. package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
  129. package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
  130. package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
  131. package/lib/opentelemetry-bridge/README.md +244 -0
  132. package/lib/opentelemetry-bridge/index.js +15 -0
  133. package/lib/opentelemetry-bridge/oblog.js +23 -0
  134. package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
  135. package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
  136. package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
  137. package/lib/opentelemetry-bridge/otelutils.js +99 -0
  138. package/lib/opentelemetry-bridge/setup.js +76 -0
  139. package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
  140. package/lib/opentelemetry-metrics/index.js +50 -0
  141. package/lib/parsers.js +225 -0
  142. package/lib/propwrap.js +147 -0
  143. package/lib/stacktraces.js +537 -0
  144. package/lib/symbols.js +15 -0
  145. package/lib/tracecontext/index.js +118 -0
  146. package/lib/tracecontext/traceparent.js +185 -0
  147. package/lib/tracecontext/tracestate.js +388 -0
  148. package/lib/wildcard-matcher.js +52 -0
  149. package/loader.mjs +7 -0
  150. package/package.json +299 -0
  151. package/start.d.ts +8 -0
  152. package/start.js +29 -0
  153. package/types/aws-lambda.d.ts +98 -0
  154. 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
+ };