@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,30 @@
|
|
|
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 shimmer = require('../../shimmer');
|
|
10
|
+
|
|
11
|
+
module.exports = function (apolloServer, agent, { enabled }) {
|
|
12
|
+
if (!enabled) {
|
|
13
|
+
return apolloServer;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function wrapExecuteHTTPGraphQLRequest(orig) {
|
|
17
|
+
return function wrappedExecuteHTTPGraphQLRequest() {
|
|
18
|
+
var trans = agent._instrumentation.currTransaction();
|
|
19
|
+
if (trans) trans._graphqlRoute = true;
|
|
20
|
+
return orig.apply(this, arguments);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
shimmer.wrap(
|
|
25
|
+
apolloServer.ApolloServer.prototype,
|
|
26
|
+
'executeHTTPGraphQLRequest',
|
|
27
|
+
wrapExecuteHTTPGraphQLRequest,
|
|
28
|
+
);
|
|
29
|
+
return apolloServer;
|
|
30
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
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 constants = require('../../../constants');
|
|
10
|
+
const NAME = 'DynamoDB';
|
|
11
|
+
const TYPE = 'db';
|
|
12
|
+
const SUBTYPE = 'dynamodb';
|
|
13
|
+
const elasticAPMStash = Symbol('elasticAPMStash');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns middlewares to instrument an S3Client instance
|
|
17
|
+
*
|
|
18
|
+
* @param {import('@aws-sdk/client-dynamodb').DynamoDBClient} client
|
|
19
|
+
* @param {any} agent
|
|
20
|
+
* @returns {import('../@smithy/smithy-client').AWSMiddlewareEntry[]}
|
|
21
|
+
*/
|
|
22
|
+
function dynamoDBMiddlewareFactory(client, agent) {
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
middleware: (next, context) => async (args) => {
|
|
26
|
+
// Ensure there is a span from the wrapped `client.send()`.
|
|
27
|
+
const span = agent._instrumentation.currSpan();
|
|
28
|
+
if (!span || !(span.type === TYPE && span.subtype === SUBTYPE)) {
|
|
29
|
+
return await next(args);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const input = args.input;
|
|
33
|
+
const table = input && input.TableName;
|
|
34
|
+
// The given span comes with the operation name and we need to
|
|
35
|
+
// add the table if applies
|
|
36
|
+
if (table) {
|
|
37
|
+
span.name += ' ' + table;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let err;
|
|
41
|
+
let result;
|
|
42
|
+
let response;
|
|
43
|
+
let statusCode;
|
|
44
|
+
try {
|
|
45
|
+
result = await next(args);
|
|
46
|
+
response = result && result.response;
|
|
47
|
+
statusCode = response && response.statusCode;
|
|
48
|
+
} catch (ex) {
|
|
49
|
+
// Save the error for use in `finally` below, but re-throw it to
|
|
50
|
+
// not impact code flow.
|
|
51
|
+
err = ex;
|
|
52
|
+
|
|
53
|
+
// This code path happens with a GetObject conditional request
|
|
54
|
+
// that returns a 304 Not Modified.
|
|
55
|
+
statusCode = err && err.$metadata && err.$metadata.httpStatusCode;
|
|
56
|
+
throw ex;
|
|
57
|
+
} finally {
|
|
58
|
+
if (statusCode) {
|
|
59
|
+
span._setOutcomeFromHttpStatusCode(statusCode);
|
|
60
|
+
} else {
|
|
61
|
+
span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE);
|
|
62
|
+
}
|
|
63
|
+
if (err && (!statusCode || statusCode >= 400)) {
|
|
64
|
+
agent.captureError(err, { skipOutcome: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const config = client.config;
|
|
68
|
+
const region = await config.region();
|
|
69
|
+
|
|
70
|
+
// Set the db context
|
|
71
|
+
const dbContext = { type: SUBTYPE }; // dynamodb
|
|
72
|
+
if (region) {
|
|
73
|
+
dbContext.instance = region;
|
|
74
|
+
}
|
|
75
|
+
if (input && input.KeyConditionExpression) {
|
|
76
|
+
dbContext.statement = input.KeyConditionExpression;
|
|
77
|
+
}
|
|
78
|
+
span.setDbContext(dbContext);
|
|
79
|
+
|
|
80
|
+
// Set destination context
|
|
81
|
+
const destContext = {};
|
|
82
|
+
if (context[elasticAPMStash]) {
|
|
83
|
+
destContext.address = context[elasticAPMStash].hostname;
|
|
84
|
+
destContext.port = context[elasticAPMStash].port;
|
|
85
|
+
}
|
|
86
|
+
if (region) {
|
|
87
|
+
destContext.service = { resource: `dynamodb/${region}` };
|
|
88
|
+
destContext.cloud = { region };
|
|
89
|
+
}
|
|
90
|
+
span._setDestinationContext(destContext);
|
|
91
|
+
|
|
92
|
+
// TODO: add OTel attributes when spec is merged
|
|
93
|
+
// https://github.com/elastic/apm/pull/817
|
|
94
|
+
|
|
95
|
+
span.end();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return result;
|
|
99
|
+
},
|
|
100
|
+
options: { step: 'initialize', priority: 'high', name: 'elasticAPMSpan' },
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
middleware: (next, context) => async (args) => {
|
|
104
|
+
const req = args.request;
|
|
105
|
+
let port = req.port;
|
|
106
|
+
|
|
107
|
+
// Resolve port for HTTP(S) protocols
|
|
108
|
+
if (port === undefined) {
|
|
109
|
+
if (req.protocol === 'https:') {
|
|
110
|
+
port = 443;
|
|
111
|
+
} else if (req.protocol === 'http:') {
|
|
112
|
+
port = 80;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
context[elasticAPMStash] = {
|
|
117
|
+
hostname: req.hostname,
|
|
118
|
+
port,
|
|
119
|
+
};
|
|
120
|
+
return next(args);
|
|
121
|
+
},
|
|
122
|
+
options: { step: 'finalizeRequest', name: 'elasticAPMHTTPInfo' },
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Tells if the command needs to be ingored
|
|
129
|
+
* @param {import('@aws-sdk/types').Command} command the command sent by the SNS client
|
|
130
|
+
* @param {any} config the agent configuration
|
|
131
|
+
* @returns {boolean} false if the command should create a span
|
|
132
|
+
*/
|
|
133
|
+
function dynamoDBShouldIgnoreCommand(command, config) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
DYNAMODB_NAME: NAME,
|
|
139
|
+
DYNAMODB_TYPE: TYPE,
|
|
140
|
+
DYNAMODB_SUBTYPE: SUBTYPE,
|
|
141
|
+
dynamoDBMiddlewareFactory,
|
|
142
|
+
dynamoDBShouldIgnoreCommand,
|
|
143
|
+
};
|
|
@@ -0,0 +1,230 @@
|
|
|
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 constants = require('../../../constants');
|
|
10
|
+
const NAME = 'S3';
|
|
11
|
+
const TYPE = 'storage';
|
|
12
|
+
const SUBTYPE = 's3';
|
|
13
|
+
const elasticAPMStash = Symbol('elasticAPMStash');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets the region from the ARN
|
|
17
|
+
*
|
|
18
|
+
* @param {String} s3Arn
|
|
19
|
+
* @returns {String}
|
|
20
|
+
*/
|
|
21
|
+
function regionFromS3Arn(s3Arn) {
|
|
22
|
+
return s3Arn.split(':')[3];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Return an APM "resource" string for the bucket, Access Point ARN, or Outpost
|
|
27
|
+
* ARN. ARNs are normalized to a shorter resource name.
|
|
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
|
+
*
|
|
39
|
+
* @param {String} bucket The bucket string
|
|
40
|
+
* @returns {String | null}
|
|
41
|
+
*/
|
|
42
|
+
function resourceFromBucket(bucket) {
|
|
43
|
+
let resource = null;
|
|
44
|
+
if (bucket) {
|
|
45
|
+
resource = bucket;
|
|
46
|
+
if (resource.startsWith('arn:')) {
|
|
47
|
+
resource = bucket.split(':').slice(5).join(':');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return resource;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns middlewares to instrument an S3Client instance
|
|
55
|
+
*
|
|
56
|
+
* @param {import('@aws-sdk/client-s3').S3Client} client
|
|
57
|
+
* @param {any} agent
|
|
58
|
+
* @returns {import('./smithy-client').AWSMiddlewareEntry[]}
|
|
59
|
+
*/
|
|
60
|
+
function s3MiddlewareFactory(client, agent) {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
middleware: (next, context) => async (args) => {
|
|
64
|
+
// Ensure there is a span from the wrapped `client.send()`.
|
|
65
|
+
const span = agent._instrumentation.currSpan();
|
|
66
|
+
if (!span || !(span.type === TYPE && span.subtype === SUBTYPE)) {
|
|
67
|
+
return await next(args);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const input = args.input;
|
|
71
|
+
const bucket = input && input.Bucket;
|
|
72
|
+
const resource = resourceFromBucket(bucket);
|
|
73
|
+
// The given span comes with the operation name and we need to
|
|
74
|
+
// add the resource if applies
|
|
75
|
+
if (resource) {
|
|
76
|
+
span.name += ' ' + resource;
|
|
77
|
+
span.setServiceTarget('s3', resource);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// As for now OTel spec defines attributes for operations that require a Bucket
|
|
81
|
+
// if that changes we should review this guard
|
|
82
|
+
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435
|
|
83
|
+
if (bucket) {
|
|
84
|
+
const otelAttrs = span._getOTelAttributes();
|
|
85
|
+
|
|
86
|
+
otelAttrs['aws.s3.bucket'] = bucket;
|
|
87
|
+
|
|
88
|
+
if (input.Key) {
|
|
89
|
+
otelAttrs['aws.s3.key'] = input.Key;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let err;
|
|
94
|
+
let result;
|
|
95
|
+
let response;
|
|
96
|
+
let statusCode;
|
|
97
|
+
try {
|
|
98
|
+
result = await next(args);
|
|
99
|
+
response = result && result.response;
|
|
100
|
+
statusCode = response && response.statusCode;
|
|
101
|
+
} catch (ex) {
|
|
102
|
+
// Save the error for use in `finally` below, but re-throw it to
|
|
103
|
+
// not impact code flow.
|
|
104
|
+
err = ex;
|
|
105
|
+
|
|
106
|
+
// This code path happens with a GetObject conditional request
|
|
107
|
+
// that returns a 304 Not Modified.
|
|
108
|
+
statusCode = err && err.$metadata && err.$metadata.httpStatusCode;
|
|
109
|
+
throw ex;
|
|
110
|
+
} finally {
|
|
111
|
+
if (statusCode) {
|
|
112
|
+
span._setOutcomeFromHttpStatusCode(statusCode);
|
|
113
|
+
} else {
|
|
114
|
+
span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE);
|
|
115
|
+
}
|
|
116
|
+
if (err && (!statusCode || statusCode >= 400)) {
|
|
117
|
+
agent.captureError(err, { skipOutcome: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Set the httpContext
|
|
121
|
+
if (statusCode) {
|
|
122
|
+
const httpContext = {
|
|
123
|
+
status_code: statusCode,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
response &&
|
|
128
|
+
response.headers &&
|
|
129
|
+
response.headers['content-length']
|
|
130
|
+
) {
|
|
131
|
+
const encodedBodySize = Number(
|
|
132
|
+
response.headers['content-length'],
|
|
133
|
+
);
|
|
134
|
+
if (!isNaN(encodedBodySize)) {
|
|
135
|
+
httpContext.response = { encoded_body_size: encodedBodySize };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
span.setHttpContext(httpContext);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Configuring `new S3Client({useArnRegion:true})` allows one to
|
|
142
|
+
// use an Access Point bucket ARN for a region *other* than the
|
|
143
|
+
// one for which the client is configured. Therefore, we attempt
|
|
144
|
+
// to get the bucket region from the ARN first.
|
|
145
|
+
const config = client.config;
|
|
146
|
+
let useArnRegion;
|
|
147
|
+
if (typeof config.useArnRegion === 'boolean') {
|
|
148
|
+
useArnRegion = config.useArnRegion;
|
|
149
|
+
} else {
|
|
150
|
+
useArnRegion = await config.useArnRegion();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let region;
|
|
154
|
+
if (useArnRegion && bucket && bucket.startsWith('arn:')) {
|
|
155
|
+
region = regionFromS3Arn(args.input.Bucket);
|
|
156
|
+
} else {
|
|
157
|
+
region =
|
|
158
|
+
typeof config.region === 'boolean'
|
|
159
|
+
? region
|
|
160
|
+
: await config.region();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Destination context.
|
|
164
|
+
const destContext = {
|
|
165
|
+
service: {
|
|
166
|
+
name: SUBTYPE,
|
|
167
|
+
type: TYPE,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
if (context[elasticAPMStash]) {
|
|
171
|
+
destContext.address = context[elasticAPMStash].hostname;
|
|
172
|
+
destContext.port = context[elasticAPMStash].port;
|
|
173
|
+
}
|
|
174
|
+
if (resource) {
|
|
175
|
+
destContext.service.resource = resource;
|
|
176
|
+
}
|
|
177
|
+
if (region) {
|
|
178
|
+
destContext.cloud = { region };
|
|
179
|
+
}
|
|
180
|
+
span._setDestinationContext(destContext);
|
|
181
|
+
|
|
182
|
+
span.end();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return result;
|
|
186
|
+
},
|
|
187
|
+
options: { step: 'initialize', priority: 'high', name: 'elasticAPMSpan' },
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
middleware: (next, context) => async (args) => {
|
|
191
|
+
const req = args.request;
|
|
192
|
+
let port = req.port;
|
|
193
|
+
|
|
194
|
+
// Resolve port for HTTP(S) protocols
|
|
195
|
+
if (port === undefined) {
|
|
196
|
+
if (req.protocol === 'https:') {
|
|
197
|
+
port = 443;
|
|
198
|
+
} else if (req.protocol === 'http:') {
|
|
199
|
+
port = 80;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
context[elasticAPMStash] = {
|
|
204
|
+
hostname: req.hostname,
|
|
205
|
+
port,
|
|
206
|
+
};
|
|
207
|
+
return next(args);
|
|
208
|
+
},
|
|
209
|
+
options: { step: 'finalizeRequest', name: 'elasticAPMHTTPInfo' },
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Tells if the command needs to be ingored
|
|
216
|
+
* @param {import('@aws-sdk/types').Command} command the command sent by the SNS client
|
|
217
|
+
* @param {any} config the agent configuration
|
|
218
|
+
* @returns {boolean} false if the command should create a span
|
|
219
|
+
*/
|
|
220
|
+
function s3ShouldIgnoreCommand(command, config) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
S3_NAME: NAME,
|
|
226
|
+
S3_TYPE: TYPE,
|
|
227
|
+
S3_SUBTYPE: SUBTYPE,
|
|
228
|
+
s3MiddlewareFactory,
|
|
229
|
+
s3ShouldIgnoreCommand,
|
|
230
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
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 constants = require('../../../constants');
|
|
10
|
+
const NAME = 'SNS';
|
|
11
|
+
const TYPE = 'messaging';
|
|
12
|
+
const SUBTYPE = 'sns';
|
|
13
|
+
const MAX_SNS_MESSAGE_ATTRIBUTES = 10;
|
|
14
|
+
const elasticAPMStash = Symbol('elasticAPMStash');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns middlewares to instrument an S3Client instance
|
|
18
|
+
*
|
|
19
|
+
* @param {import('@aws-sdk/client-sns').SNSClient} client
|
|
20
|
+
* @param {any} agent
|
|
21
|
+
* @returns {import('./smithy-client').AWSMiddlewareEntry[]}
|
|
22
|
+
*/
|
|
23
|
+
function snsMiddlewareFactory(client, agent) {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
middleware: (next, context) => async (args) => {
|
|
27
|
+
const ins = agent._instrumentation;
|
|
28
|
+
const log = agent.logger;
|
|
29
|
+
const span = ins.currSpan();
|
|
30
|
+
const input = args.input;
|
|
31
|
+
|
|
32
|
+
// W3C trace-context propagation.
|
|
33
|
+
const runContext = ins.currRunContext();
|
|
34
|
+
const parentSpan =
|
|
35
|
+
span || runContext.currSpan() || runContext.currTransaction();
|
|
36
|
+
const targetId =
|
|
37
|
+
input && (input.TopicArn || input.TargetArn || input.PhoneNumber);
|
|
38
|
+
|
|
39
|
+
if (parentSpan) {
|
|
40
|
+
// Though our spec only mentions a 10-message-attribute limit for *SQS*, we'll
|
|
41
|
+
// do the same limit here, because
|
|
42
|
+
// https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html
|
|
43
|
+
// mentions the 10-message-attribute limit for SQS subscriptions.
|
|
44
|
+
input.MessageAttributes = input.MessageAttributes || {};
|
|
45
|
+
const attributesCount = Object.keys(input.MessageAttributes).length;
|
|
46
|
+
|
|
47
|
+
if (attributesCount + 2 > MAX_SNS_MESSAGE_ATTRIBUTES) {
|
|
48
|
+
log.warn(
|
|
49
|
+
'cannot propagate trace-context with SNS message to %s, too many MessageAttributes',
|
|
50
|
+
targetId,
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
parentSpan.propagateTraceContextHeaders(
|
|
54
|
+
input.MessageAttributes,
|
|
55
|
+
function (msgAttrs, name, value) {
|
|
56
|
+
if (name.startsWith('elastic-')) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
msgAttrs[name] = { DataType: 'String', StringValue: value };
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Ensure there is a span from the wrapped `client.send()`.
|
|
66
|
+
if (!span || !(span.type === TYPE && span.subtype === SUBTYPE)) {
|
|
67
|
+
return await next(args);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const destTargets = [
|
|
71
|
+
input.TopicArn && input.TopicArn.split(':').pop(),
|
|
72
|
+
input.TargetArn && input.TargetArn.split(':').pop().split('/').pop(),
|
|
73
|
+
input.PhoneNumber && '<PHONE_NUMBER>',
|
|
74
|
+
];
|
|
75
|
+
const topicName = destTargets.filter((a) => a).join(', ');
|
|
76
|
+
|
|
77
|
+
if (topicName) {
|
|
78
|
+
span.name += ' to ' + topicName;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let err;
|
|
82
|
+
let result;
|
|
83
|
+
let response;
|
|
84
|
+
let statusCode;
|
|
85
|
+
try {
|
|
86
|
+
result = await next(args);
|
|
87
|
+
response = result && result.response;
|
|
88
|
+
statusCode = response && response.statusCode;
|
|
89
|
+
} catch (ex) {
|
|
90
|
+
// Save the error for use in `finally` below, but re-throw it to
|
|
91
|
+
// not impact code flow.
|
|
92
|
+
err = ex;
|
|
93
|
+
|
|
94
|
+
// This code path happens with a Publish command that returns a 404 Not Found.
|
|
95
|
+
statusCode = err && err.$metadata && err.$metadata.httpStatusCode;
|
|
96
|
+
throw ex;
|
|
97
|
+
} finally {
|
|
98
|
+
if (statusCode) {
|
|
99
|
+
span._setOutcomeFromHttpStatusCode(statusCode);
|
|
100
|
+
} else {
|
|
101
|
+
span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE);
|
|
102
|
+
}
|
|
103
|
+
if (err && (!statusCode || statusCode >= 400)) {
|
|
104
|
+
agent.captureError(err, { skipOutcome: true });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Destination context.
|
|
108
|
+
const region = await client.config.region();
|
|
109
|
+
const service = { name: SUBTYPE, type: TYPE };
|
|
110
|
+
const destCtx = { service };
|
|
111
|
+
|
|
112
|
+
if (context[elasticAPMStash]) {
|
|
113
|
+
destCtx.address = context[elasticAPMStash].hostname;
|
|
114
|
+
destCtx.port = context[elasticAPMStash].port;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (region) {
|
|
118
|
+
destCtx.cloud = { region };
|
|
119
|
+
}
|
|
120
|
+
span._setDestinationContext(destCtx);
|
|
121
|
+
|
|
122
|
+
// Message context
|
|
123
|
+
if (topicName) {
|
|
124
|
+
span.setMessageContext({ queue: { name: topicName } });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
span.end();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
},
|
|
132
|
+
options: { step: 'initialize', priority: 'high', name: 'elasticAPMSpan' },
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
middleware: (next, context) => async (args) => {
|
|
136
|
+
const req = args.request;
|
|
137
|
+
let port = req.port;
|
|
138
|
+
|
|
139
|
+
// Resolve port for HTTP(S) protocols
|
|
140
|
+
if (port === undefined) {
|
|
141
|
+
if (req.protocol === 'https:') {
|
|
142
|
+
port = 443;
|
|
143
|
+
} else if (req.protocol === 'http:') {
|
|
144
|
+
port = 80;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
context[elasticAPMStash] = {
|
|
149
|
+
hostname: req.hostname,
|
|
150
|
+
port,
|
|
151
|
+
};
|
|
152
|
+
return next(args);
|
|
153
|
+
},
|
|
154
|
+
options: { step: 'finalizeRequest', name: 'elasticAPMHTTPInfo' },
|
|
155
|
+
},
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Tells if the command needs to be ingored
|
|
161
|
+
* @param {import('@aws-sdk/types').Command} command the command sent by the SNS client
|
|
162
|
+
* @param {any} config the agent configuration
|
|
163
|
+
* @returns {boolean} false if the command should create a span
|
|
164
|
+
*/
|
|
165
|
+
function snsShouldIgnoreCommand(command, config) {
|
|
166
|
+
if (command.constructor.name !== 'PublishCommand') {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (config.ignoreMessageQueuesRegExp) {
|
|
171
|
+
const input = command.input;
|
|
172
|
+
// It's unlikely but input can have multiple targets defined
|
|
173
|
+
// and having a priority list between them may result in
|
|
174
|
+
// not honoring `ignoreMessageQueues` config.
|
|
175
|
+
const topicNames = input
|
|
176
|
+
? [input.TopicArn, input.TargetArn, input.PhoneNumber].filter((t) => t)
|
|
177
|
+
: [];
|
|
178
|
+
|
|
179
|
+
for (const topicName of topicNames) {
|
|
180
|
+
for (const rule of config.ignoreMessageQueuesRegExp) {
|
|
181
|
+
if (rule.test(topicName)) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = {
|
|
192
|
+
SNS_NAME: NAME,
|
|
193
|
+
SNS_TYPE: TYPE,
|
|
194
|
+
SNS_SUBTYPE: SUBTYPE,
|
|
195
|
+
snsMiddlewareFactory,
|
|
196
|
+
snsShouldIgnoreCommand,
|
|
197
|
+
};
|