@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.
- package/LICENSE +26 -0
- package/README.md +126 -0
- package/index.d.ts +464 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +97 -0
- package/lib/agent.js +1226 -0
- package/lib/apm-client/apm-client.js +107 -0
- package/lib/apm-client/channel-writer.js +334 -0
- package/lib/apm-client/jsonl-file-client.js +241 -0
- package/lib/apm-client/ndjson.js +20 -0
- package/lib/apm-client/noop-apm-client.js +79 -0
- package/lib/apm-client/s3-uploader.js +308 -0
- package/lib/apm-client/truncate.js +507 -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 +431 -0
- package/lib/config/normalizers.js +649 -0
- package/lib/config/schema.js +946 -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/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 +1078 -0
- package/lib/instrumentation/modules/@apollo/server.js +39 -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/@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/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 +200 -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 +179 -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 +33 -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 +29 -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 +100 -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 +29 -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/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/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 +115 -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 +98 -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,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
|
+
};
|