@redthreadlabs/tracelog 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +126 -0
  3. package/index.d.ts +464 -0
  4. package/index.js +11 -0
  5. package/lib/InflightEventSet.js +53 -0
  6. package/lib/activation-method.js +97 -0
  7. package/lib/agent.js +1226 -0
  8. package/lib/apm-client/apm-client.js +107 -0
  9. package/lib/apm-client/channel-writer.js +334 -0
  10. package/lib/apm-client/jsonl-file-client.js +241 -0
  11. package/lib/apm-client/ndjson.js +20 -0
  12. package/lib/apm-client/noop-apm-client.js +79 -0
  13. package/lib/apm-client/s3-uploader.js +308 -0
  14. package/lib/apm-client/truncate.js +507 -0
  15. package/lib/async-hooks-polyfill.js +58 -0
  16. package/lib/cloud-metadata/aws.js +175 -0
  17. package/lib/cloud-metadata/azure.js +123 -0
  18. package/lib/cloud-metadata/callback-coordination.js +159 -0
  19. package/lib/cloud-metadata/gcp.js +133 -0
  20. package/lib/cloud-metadata/index.js +175 -0
  21. package/lib/config/config.js +431 -0
  22. package/lib/config/normalizers.js +649 -0
  23. package/lib/config/schema.js +946 -0
  24. package/lib/constants.js +35 -0
  25. package/lib/errors.js +303 -0
  26. package/lib/filters/sanitize-field-names.js +69 -0
  27. package/lib/http-request.js +249 -0
  28. package/lib/instrumentation/context.js +56 -0
  29. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  30. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  31. package/lib/instrumentation/express-utils.js +91 -0
  32. package/lib/instrumentation/generic-span.js +322 -0
  33. package/lib/instrumentation/http-shared.js +424 -0
  34. package/lib/instrumentation/ids.js +39 -0
  35. package/lib/instrumentation/index.js +1078 -0
  36. package/lib/instrumentation/modules/@apollo/server.js +39 -0
  37. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  38. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  39. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  40. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  41. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  42. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  43. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  44. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  45. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  46. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  47. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  48. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  49. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  50. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  51. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  52. package/lib/instrumentation/modules/bluebird.js +93 -0
  53. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  54. package/lib/instrumentation/modules/elasticsearch.js +200 -0
  55. package/lib/instrumentation/modules/express-graphql.js +66 -0
  56. package/lib/instrumentation/modules/express-queue.js +28 -0
  57. package/lib/instrumentation/modules/express.js +162 -0
  58. package/lib/instrumentation/modules/fastify.js +179 -0
  59. package/lib/instrumentation/modules/finalhandler.js +41 -0
  60. package/lib/instrumentation/modules/generic-pool.js +85 -0
  61. package/lib/instrumentation/modules/graphql.js +256 -0
  62. package/lib/instrumentation/modules/handlebars.js +33 -0
  63. package/lib/instrumentation/modules/http.js +112 -0
  64. package/lib/instrumentation/modules/http2.js +320 -0
  65. package/lib/instrumentation/modules/https.js +68 -0
  66. package/lib/instrumentation/modules/ioredis.js +94 -0
  67. package/lib/instrumentation/modules/jade.js +29 -0
  68. package/lib/instrumentation/modules/kafkajs.js +476 -0
  69. package/lib/instrumentation/modules/knex.js +91 -0
  70. package/lib/instrumentation/modules/koa-router.js +74 -0
  71. package/lib/instrumentation/modules/koa.js +15 -0
  72. package/lib/instrumentation/modules/memcached.js +100 -0
  73. package/lib/instrumentation/modules/mimic-response.js +45 -0
  74. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  75. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  76. package/lib/instrumentation/modules/mongodb.js +259 -0
  77. package/lib/instrumentation/modules/mysql.js +200 -0
  78. package/lib/instrumentation/modules/mysql2.js +140 -0
  79. package/lib/instrumentation/modules/pg.js +148 -0
  80. package/lib/instrumentation/modules/pug.js +29 -0
  81. package/lib/instrumentation/modules/redis.js +176 -0
  82. package/lib/instrumentation/modules/restify.js +52 -0
  83. package/lib/instrumentation/modules/tedious.js +159 -0
  84. package/lib/instrumentation/modules/undici.js +270 -0
  85. package/lib/instrumentation/modules/ws.js +59 -0
  86. package/lib/instrumentation/noop-transaction.js +81 -0
  87. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  88. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  89. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  90. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  91. package/lib/instrumentation/run-context/RunContext.js +151 -0
  92. package/lib/instrumentation/run-context/index.js +23 -0
  93. package/lib/instrumentation/shimmer.js +123 -0
  94. package/lib/instrumentation/span-compression.js +239 -0
  95. package/lib/instrumentation/span.js +621 -0
  96. package/lib/instrumentation/template-shared.js +43 -0
  97. package/lib/instrumentation/timer.js +84 -0
  98. package/lib/instrumentation/transaction.js +571 -0
  99. package/lib/load-source-map.js +100 -0
  100. package/lib/logging.js +212 -0
  101. package/lib/metrics/index.js +92 -0
  102. package/lib/metrics/platforms/generic/index.js +40 -0
  103. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  104. package/lib/metrics/platforms/generic/process-top.js +157 -0
  105. package/lib/metrics/platforms/generic/stats.js +34 -0
  106. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  107. package/lib/metrics/platforms/linux/index.js +19 -0
  108. package/lib/metrics/platforms/linux/stats.js +213 -0
  109. package/lib/metrics/queue.js +90 -0
  110. package/lib/metrics/registry.js +52 -0
  111. package/lib/metrics/reporter.js +119 -0
  112. package/lib/metrics/runtime.js +77 -0
  113. package/lib/middleware/connect.js +16 -0
  114. package/lib/parsers.js +225 -0
  115. package/lib/propwrap.js +147 -0
  116. package/lib/stacktraces.js +537 -0
  117. package/lib/symbols.js +15 -0
  118. package/lib/tracecontext/index.js +115 -0
  119. package/lib/tracecontext/traceparent.js +185 -0
  120. package/lib/tracecontext/tracestate.js +388 -0
  121. package/lib/wildcard-matcher.js +52 -0
  122. package/loader.mjs +7 -0
  123. package/package.json +98 -0
  124. package/start.d.ts +8 -0
  125. package/start.js +29 -0
  126. package/types/aws-lambda.d.ts +98 -0
  127. package/types/connect.d.ts +23 -0
@@ -0,0 +1,155 @@
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 = 'db';
10
+ const SUBTYPE = 'dynamodb';
11
+ const ACTION = 'query';
12
+
13
+ function getRegionFromRequest(request) {
14
+ return (
15
+ request &&
16
+ request.service &&
17
+ request.service.config &&
18
+ request.service.config.region
19
+ );
20
+ }
21
+
22
+ function getPortFromRequest(request) {
23
+ return (
24
+ request &&
25
+ request.service &&
26
+ request.service.endpoint &&
27
+ request.service.endpoint.port
28
+ );
29
+ }
30
+
31
+ function getMethodFromRequest(request) {
32
+ const method = request && request.operation;
33
+ if (method) {
34
+ return method[0].toUpperCase() + method.slice(1);
35
+ }
36
+ }
37
+
38
+ function getStatementFromRequest(request) {
39
+ const method = getMethodFromRequest(request);
40
+ if (
41
+ method === 'Query' &&
42
+ request &&
43
+ request.params &&
44
+ request.params.KeyConditionExpression
45
+ ) {
46
+ return request.params.KeyConditionExpression;
47
+ }
48
+ return undefined;
49
+ }
50
+
51
+ function getAddressFromRequest(request) {
52
+ return (
53
+ request &&
54
+ request.service &&
55
+ request.service.endpoint &&
56
+ request.service.endpoint.hostname
57
+ );
58
+ }
59
+
60
+ function getTableFromRequest(request) {
61
+ const table = request && request.params && request.params.TableName;
62
+ if (!table) {
63
+ return '';
64
+ }
65
+ return ` ${table}`;
66
+ }
67
+
68
+ // Creates the span name from request information
69
+ function getSpanNameFromRequest(request) {
70
+ const method = getMethodFromRequest(request);
71
+ const table = getTableFromRequest(request);
72
+ const name = `DynamoDB ${method}${table}`;
73
+ return name;
74
+ }
75
+
76
+ function shouldIgnoreRequest(request, agent) {
77
+ return false;
78
+ }
79
+
80
+ // Main entrypoint for SQS instrumentation
81
+ //
82
+ // Must call (or one of its function calls must call) the
83
+ // `orig` function/method
84
+ function instrumentationDynamoDb(
85
+ orig,
86
+ origArguments,
87
+ request,
88
+ AWS,
89
+ agent,
90
+ { version, enabled },
91
+ ) {
92
+ if (shouldIgnoreRequest(request, agent)) {
93
+ return orig.apply(request, origArguments);
94
+ }
95
+
96
+ const ins = agent._instrumentation;
97
+ const name = getSpanNameFromRequest(request);
98
+ const span = ins.createSpan(name, TYPE, SUBTYPE, ACTION, { exitSpan: true });
99
+ if (!span) {
100
+ return orig.apply(request, origArguments);
101
+ }
102
+
103
+ const region = getRegionFromRequest(request);
104
+ const dbContext = {
105
+ type: SUBTYPE,
106
+ instance: region,
107
+ };
108
+ const dbStatement = getStatementFromRequest(request);
109
+ if (dbStatement) {
110
+ dbContext.statement = dbStatement;
111
+ }
112
+ span.setDbContext(dbContext);
113
+ span._setDestinationContext({
114
+ address: getAddressFromRequest(request),
115
+ port: getPortFromRequest(request),
116
+ cloud: {
117
+ region,
118
+ },
119
+ });
120
+
121
+ const onComplete = function (response) {
122
+ if (response && response.error) {
123
+ agent.captureError(response.error);
124
+ }
125
+ span.end();
126
+ };
127
+ // Bind onComplete to the span's run context so that `captureError` picks
128
+ // up the correct currentSpan.
129
+ const parentRunContext = ins.currRunContext();
130
+ const spanRunContext = parentRunContext.enterSpan(span);
131
+ request.on(
132
+ 'complete',
133
+ ins.bindFunctionToRunContext(spanRunContext, onComplete),
134
+ );
135
+
136
+ const cb = origArguments[origArguments.length - 1];
137
+ if (typeof cb === 'function') {
138
+ origArguments[origArguments.length - 1] = ins.bindFunctionToRunContext(
139
+ parentRunContext,
140
+ cb,
141
+ );
142
+ }
143
+ return ins.withRunContext(spanRunContext, orig, request, ...origArguments);
144
+ }
145
+
146
+ module.exports = {
147
+ instrumentationDynamoDb,
148
+
149
+ // exported for testing
150
+ getRegionFromRequest,
151
+ getPortFromRequest,
152
+ getStatementFromRequest,
153
+ getAddressFromRequest,
154
+ getMethodFromRequest,
155
+ };
@@ -0,0 +1,184 @@
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
+ // Instrument AWS S3 operations via the 'aws-sdk' package.
10
+
11
+ const constants = require('../../../constants');
12
+
13
+ const TYPE = 'storage';
14
+ const SUBTYPE = 's3';
15
+
16
+ // Return the PascalCase operation name from `request.operation` by undoing to
17
+ // `lowerFirst()` from
18
+ // https://github.com/aws/aws-sdk-js/blob/c0c44b8a4e607aae521686898f39a3e359f727e4/lib/model/api.js#L63-L65
19
+ //
20
+ // For example: 'headBucket' -> 'HeadBucket'
21
+ function opNameFromOperation(operation) {
22
+ return operation[0].toUpperCase() + operation.slice(1);
23
+ }
24
+
25
+ // Return an APM "resource" string for the bucket, Access Point ARN, or Outpost
26
+ // ARN. ARNs are normalized to a shorter resource name.
27
+ //
28
+ // Known ARN patterns:
29
+ // - arn:aws:s3:<region>:<account-id>:accesspoint/<accesspoint-name>
30
+ // - arn:aws:s3-outposts:<region>:<account>:outpost/<outpost-id>/bucket/<bucket-name>
31
+ // - arn:aws:s3-outposts:<region>:<account>:outpost/<outpost-id>/accesspoint/<accesspoint-name>
32
+ //
33
+ // In general that is:
34
+ // arn:$partition:$service:$region:$accountId:$resource
35
+ //
36
+ // This parses using the same "split on colon" used by the JavaScript AWS SDK v3.
37
+ // https://github.com/aws/aws-sdk-js-v3/blob/v3.18.0/packages/util-arn-parser/src/index.ts#L14-L37
38
+ function resourceFromBucket(bucket) {
39
+ let resource = null;
40
+ if (bucket) {
41
+ resource = bucket;
42
+ if (resource.startsWith('arn:')) {
43
+ resource = bucket.split(':').slice(5).join(':');
44
+ }
45
+ }
46
+ return resource;
47
+ }
48
+
49
+ // Instrument an awk-sdk@2.x operation (i.e. a AWS.Request.send or
50
+ // AWS.Request.promise).
51
+ //
52
+ // @param {AWS.Request} request https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html
53
+ function instrumentationS3(
54
+ orig,
55
+ origArguments,
56
+ request,
57
+ AWS,
58
+ agent,
59
+ { version, enabled },
60
+ ) {
61
+ const opName = opNameFromOperation(request.operation);
62
+ const params = request.params;
63
+ const bucket = params && params.Bucket;
64
+ const resource = resourceFromBucket(bucket);
65
+ let name = 'S3 ' + opName;
66
+ if (resource) {
67
+ name += ' ' + resource;
68
+ }
69
+
70
+ const ins = agent._instrumentation;
71
+
72
+ const span = ins.createSpan(name, TYPE, SUBTYPE, opName, { exitSpan: true });
73
+ if (!span) {
74
+ return orig.apply(request, origArguments);
75
+ }
76
+
77
+ // As for now OTel spec defines attributes for operations that require a Bucket
78
+ // if that changes we should review this guard
79
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
80
+ if (bucket) {
81
+ const otelAttrs = span._getOTelAttributes();
82
+
83
+ otelAttrs['aws.s3.bucket'] = bucket;
84
+
85
+ if (params.Key) {
86
+ otelAttrs['aws.s3.key'] = params.Key;
87
+ }
88
+ }
89
+
90
+ const onComplete = function (response) {
91
+ // `response` is an AWS.Response
92
+ // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Response.html
93
+
94
+ // Determining the bucket's region.
95
+ // `request.httpRequest.region` isn't documented, but the aws-sdk@2
96
+ // lib/services/s3.js will set it to the bucket's determined region.
97
+ // This can be asynchronously determined -- e.g. if it differs from the
98
+ // configured service endpoint region -- so this won't be set until
99
+ // 'complete'.
100
+ const httpRequest = request.httpRequest;
101
+ const region = httpRequest && httpRequest.region;
102
+
103
+ span.setServiceTarget('s3', resource);
104
+ const destContext = {};
105
+ // '.httpRequest.endpoint' might differ from '.service.endpoint' if
106
+ // the bucket is in a different region.
107
+ const endpoint = httpRequest && httpRequest.endpoint;
108
+ if (endpoint) {
109
+ destContext.address = endpoint.hostname;
110
+ destContext.port = endpoint.port;
111
+ }
112
+ if (region) {
113
+ destContext.cloud = { region };
114
+ }
115
+ span._setDestinationContext(destContext);
116
+
117
+ if (response) {
118
+ // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/HttpResponse.html
119
+ const httpResponse = response.httpResponse;
120
+ let statusCode;
121
+ if (httpResponse) {
122
+ statusCode = httpResponse.statusCode;
123
+
124
+ // Set HTTP context. Some context not being set, though it is available:
125
+ // - method: Not that helpful.
126
+ // - url: Mostly redundant with context.destination.address.
127
+ // - response.headers: A lot of added size for uncertain utility. The
128
+ // inclusion of Amazon's request ID headers might be worth it.
129
+ const httpContext = {
130
+ status_code: statusCode,
131
+ };
132
+ const encodedBodySize =
133
+ Buffer.isBuffer(httpResponse.body) && httpResponse.body.byteLength;
134
+ if (encodedBodySize) {
135
+ // I'm not actually sure if this might be decoded_body_size.
136
+ httpContext.response = { encoded_body_size: encodedBodySize };
137
+ }
138
+ span.setHttpContext(httpContext);
139
+ }
140
+
141
+ // Follow the spec for HTTP client span outcome.
142
+ // https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-http.md#outcome
143
+ //
144
+ // For example, a S3 GetObject conditional request (e.g. using the
145
+ // IfNoneMatch param) will respond with response.error=NotModifed and
146
+ // statusCode=304. This is a *successful* outcome.
147
+ if (statusCode) {
148
+ span._setOutcomeFromHttpStatusCode(statusCode);
149
+ } else {
150
+ // `statusCode` will be undefined for errors before sending a request, e.g.:
151
+ // InvalidConfiguration: Custom endpoint is not compatible with access point ARN
152
+ span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE);
153
+ }
154
+
155
+ if (response.error && (!statusCode || statusCode >= 400)) {
156
+ agent.captureError(response.error, { skipOutcome: true });
157
+ }
158
+ }
159
+
160
+ span.end();
161
+ };
162
+
163
+ // Run context notes: The `orig` should run in the context of the S3 span,
164
+ // because that is the point. The user's callback `cb` should run outside of
165
+ // the S3 span.
166
+ const parentRunContext = ins.currRunContext();
167
+ const spanRunContext = parentRunContext.enterSpan(span);
168
+ const cb = origArguments[origArguments.length - 1];
169
+ if (typeof cb === 'function') {
170
+ origArguments[origArguments.length - 1] = ins.bindFunctionToRunContext(
171
+ parentRunContext,
172
+ cb,
173
+ );
174
+ }
175
+ request.on(
176
+ 'complete',
177
+ ins.bindFunctionToRunContext(spanRunContext, onComplete),
178
+ );
179
+ return ins.withRunContext(spanRunContext, orig, request, ...origArguments);
180
+ }
181
+
182
+ module.exports = {
183
+ instrumentationS3,
184
+ };
@@ -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
+ };