@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,175 @@
|
|
|
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 { getMetadataAws } = require('./aws');
|
|
9
|
+
const { getMetadataAzure } = require('./azure');
|
|
10
|
+
const { getMetadataGcp } = require('./gcp');
|
|
11
|
+
const { CallbackCoordination } = require('./callback-coordination');
|
|
12
|
+
|
|
13
|
+
const logging = require('../logging');
|
|
14
|
+
|
|
15
|
+
// we "ping" (in the colloquial sense, not the ICMP sense) the metadata
|
|
16
|
+
// servers by having a listener that expects the underlying socket to
|
|
17
|
+
// connect. CONNECT_TIMEOUT_MS control that time
|
|
18
|
+
const CONNECT_TIMEOUT_MS = 100;
|
|
19
|
+
|
|
20
|
+
// some of the metadata servers have a dedicated domain name. This
|
|
21
|
+
// value is added to the above socket ping to allow extra time for
|
|
22
|
+
// the DNS to resolve
|
|
23
|
+
const DNS_TIMEOUT_MS = 100;
|
|
24
|
+
|
|
25
|
+
// the metadata servers are HTTP services. This value is
|
|
26
|
+
// used as the timeout for the HTTP request that's
|
|
27
|
+
// made.
|
|
28
|
+
const HTTP_TIMEOUT_MS = 1000;
|
|
29
|
+
|
|
30
|
+
// timeout for the CallbackCoordination object -- this is a fallback to
|
|
31
|
+
// account for a catastrophic error in the CallbackCoordination object
|
|
32
|
+
const COORDINATION_TIMEOUT_MS = 3000;
|
|
33
|
+
|
|
34
|
+
class CloudMetadata {
|
|
35
|
+
constructor(cloudProvider, logger, serviceName) {
|
|
36
|
+
this.cloudProvider = cloudProvider;
|
|
37
|
+
this.logger = logger;
|
|
38
|
+
this.serviceName = serviceName;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Fetches Cloud Metadata
|
|
43
|
+
*
|
|
44
|
+
* The module's main entry-point/primary method. The getCloudMetadata function
|
|
45
|
+
* will query the cloud metadata servers and return (via a callback function)
|
|
46
|
+
* to final metadata object. This function may be called with a single
|
|
47
|
+
* argument.
|
|
48
|
+
*
|
|
49
|
+
* getCloudMetadata(function(err, metadata){
|
|
50
|
+
* //...
|
|
51
|
+
* })
|
|
52
|
+
*
|
|
53
|
+
* Or with two
|
|
54
|
+
*
|
|
55
|
+
* getCloudMetadata(config, function(err, metadata){
|
|
56
|
+
* //...
|
|
57
|
+
* })
|
|
58
|
+
*
|
|
59
|
+
* The config parameter is an object that contains information on the
|
|
60
|
+
* metadata servers.
|
|
61
|
+
*/
|
|
62
|
+
getCloudMetadata(config, cb) {
|
|
63
|
+
// normalize arguments
|
|
64
|
+
if (!cb) {
|
|
65
|
+
cb = config;
|
|
66
|
+
config = {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// fill in blanks if any expected keys are missing
|
|
70
|
+
config.aws = config.aws ? config.aws : null;
|
|
71
|
+
config.azure = config.azure ? config.azure : null;
|
|
72
|
+
config.gcp = config.gcp ? config.gcp : null;
|
|
73
|
+
|
|
74
|
+
const fetcher = new CallbackCoordination(
|
|
75
|
+
COORDINATION_TIMEOUT_MS,
|
|
76
|
+
this.logger,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (this.shouldFetchGcp()) {
|
|
80
|
+
fetcher.schedule((fetcher) => {
|
|
81
|
+
const url = config.gcp;
|
|
82
|
+
getMetadataGcp(
|
|
83
|
+
CONNECT_TIMEOUT_MS + DNS_TIMEOUT_MS,
|
|
84
|
+
HTTP_TIMEOUT_MS,
|
|
85
|
+
this.logger,
|
|
86
|
+
url,
|
|
87
|
+
(err, result) => {
|
|
88
|
+
fetcher.recordResult(err, result);
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (this.shouldFetchAws()) {
|
|
95
|
+
fetcher.schedule((fetcher) => {
|
|
96
|
+
const url = config.aws;
|
|
97
|
+
getMetadataAws(
|
|
98
|
+
CONNECT_TIMEOUT_MS,
|
|
99
|
+
HTTP_TIMEOUT_MS,
|
|
100
|
+
this.logger,
|
|
101
|
+
url,
|
|
102
|
+
function (err, result) {
|
|
103
|
+
fetcher.recordResult(err, result);
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (this.shouldFetchAzure()) {
|
|
110
|
+
fetcher.schedule((fetcher) => {
|
|
111
|
+
const url = config.azure;
|
|
112
|
+
getMetadataAzure(
|
|
113
|
+
CONNECT_TIMEOUT_MS,
|
|
114
|
+
HTTP_TIMEOUT_MS,
|
|
115
|
+
this.logger,
|
|
116
|
+
url,
|
|
117
|
+
function (err, result) {
|
|
118
|
+
fetcher.recordResult(err, result);
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
fetcher.on('result', function (result) {
|
|
125
|
+
cb(null, result);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
fetcher.on('error', function (err) {
|
|
129
|
+
cb(err);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
fetcher.start();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
shouldFetchGcp() {
|
|
136
|
+
return this.cloudProvider === 'auto' || this.cloudProvider === 'gcp';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
shouldFetchAzure() {
|
|
140
|
+
return this.cloudProvider === 'auto' || this.cloudProvider === 'azure';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
shouldFetchAws() {
|
|
144
|
+
return this.cloudProvider === 'auto' || this.cloudProvider === 'aws';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Simple Command Line interface to fetch metadata
|
|
150
|
+
*
|
|
151
|
+
* $ node lib/cloud-metadata/index.js
|
|
152
|
+
*
|
|
153
|
+
* Will output metadata object or error if no servers are reachable
|
|
154
|
+
*/
|
|
155
|
+
function main(args) {
|
|
156
|
+
const cloudMetadata = new CloudMetadata('auto', logging.createLogger('off'));
|
|
157
|
+
cloudMetadata.getCloudMetadata(function (err, metadata) {
|
|
158
|
+
if (err) {
|
|
159
|
+
console.log('could not fetch metadata, see error below');
|
|
160
|
+
console.log(err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
} else {
|
|
163
|
+
console.log('fetched the following metadata');
|
|
164
|
+
console.log(metadata);
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (require.main === module) {
|
|
171
|
+
main(process.argv);
|
|
172
|
+
}
|
|
173
|
+
module.exports = {
|
|
174
|
+
CloudMetadata,
|
|
175
|
+
};
|
|
@@ -0,0 +1,458 @@
|
|
|
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 fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const truncate = require('unicode-byte-truncate');
|
|
13
|
+
|
|
14
|
+
const { INTAKE_STRING_MAX_SIZE, REDACTED } = require('../constants');
|
|
15
|
+
const logging = require('../logging');
|
|
16
|
+
const { NoopApmClient } = require('../apm-client/noop-apm-client');
|
|
17
|
+
const { isLambdaExecutionEnvironment } = require('../lambda');
|
|
18
|
+
const {
|
|
19
|
+
isAzureFunctionsEnvironment,
|
|
20
|
+
} = require('../instrumentation/azure-functions');
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
BOOL_OPTS,
|
|
24
|
+
NUM_OPTS,
|
|
25
|
+
DURATION_OPTS,
|
|
26
|
+
BYTES_OPTS,
|
|
27
|
+
MINUS_ONE_EQUAL_INFINITY,
|
|
28
|
+
ARRAY_OPTS,
|
|
29
|
+
KEY_VALUE_OPTS,
|
|
30
|
+
URL_OPTS,
|
|
31
|
+
getDefaultOptions,
|
|
32
|
+
getEnvironmentOptions,
|
|
33
|
+
getFileOptions,
|
|
34
|
+
setStartOptions,
|
|
35
|
+
getPreambleData,
|
|
36
|
+
readEnvOptions,
|
|
37
|
+
CENTRAL_CONFIG_OPTS,
|
|
38
|
+
} = require('./schema');
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
normalizeUrls,
|
|
42
|
+
normalizeArrays,
|
|
43
|
+
normalizeBools,
|
|
44
|
+
normalizeBytes,
|
|
45
|
+
normalizeDurationOptions,
|
|
46
|
+
normalizeIgnoreOptions,
|
|
47
|
+
normalizeInfinity,
|
|
48
|
+
normalizeKeyValuePairs,
|
|
49
|
+
normalizeNumbers,
|
|
50
|
+
normalizeElasticsearchCaptureBodyUrls,
|
|
51
|
+
normalizeDisableMetrics,
|
|
52
|
+
normalizeSanitizeFieldNames,
|
|
53
|
+
normalizeCloudProvider,
|
|
54
|
+
normalizeCustomMetricsHistogramBoundaries,
|
|
55
|
+
normalizeTransactionSampleRate,
|
|
56
|
+
normalizeTraceContinuationStrategy,
|
|
57
|
+
normalizeContextManager,
|
|
58
|
+
normalizeSpanStackTraceMinDuration,
|
|
59
|
+
} = require('./normalizers');
|
|
60
|
+
|
|
61
|
+
const EXCLUDE_FIELDS = {
|
|
62
|
+
logger: true,
|
|
63
|
+
transport: true,
|
|
64
|
+
};
|
|
65
|
+
const REDACT_FIELDS = {
|
|
66
|
+
apiKey: true,
|
|
67
|
+
secretToken: true,
|
|
68
|
+
serverUrl: true,
|
|
69
|
+
};
|
|
70
|
+
const NICE_REGEXPS_FIELDS = {
|
|
71
|
+
disableMetricsRegExp: true,
|
|
72
|
+
ignoreUrlRegExp: true,
|
|
73
|
+
ignoreUserAgentRegExp: true,
|
|
74
|
+
transactionIgnoreUrlRegExp: true,
|
|
75
|
+
sanitizeFieldNamesRegExp: true,
|
|
76
|
+
ignoreMessageQueuesRegExp: true,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const confDefaultOptions = getDefaultOptions();
|
|
80
|
+
const confEnvOpts = getEnvironmentOptions();
|
|
81
|
+
let confFileOpts = getFileOptions();
|
|
82
|
+
|
|
83
|
+
// Configure a logger for the agent.
|
|
84
|
+
//
|
|
85
|
+
// This is separate from `createConfig` to allow the agent to have an early
|
|
86
|
+
// logger before `agent.start()` is called.
|
|
87
|
+
function configLogger(opts = {}) {
|
|
88
|
+
const logLevel =
|
|
89
|
+
confEnvOpts.logLevel ||
|
|
90
|
+
opts.logLevel ||
|
|
91
|
+
(confFileOpts && confFileOpts.logLevel) ||
|
|
92
|
+
confDefaultOptions.logLevel;
|
|
93
|
+
|
|
94
|
+
// `ELASTIC_APM_LOGGER=false` is provided as a mechanism to *disable* a
|
|
95
|
+
// custom logger for troubleshooting because a wrapped custom logger does
|
|
96
|
+
// not get structured log data.
|
|
97
|
+
// https://www.elastic.co/guide/en/apm/agent/nodejs/current/troubleshooting.html#debug-mode
|
|
98
|
+
let customLogger = null;
|
|
99
|
+
if (process.env.ELASTIC_APM_LOGGER !== 'false') {
|
|
100
|
+
customLogger = opts.logger || (confFileOpts && confFileOpts.logger);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return logging.createLogger(logLevel, customLogger);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Create an initial configuration from DEFAULTS. This is used as a stand-in
|
|
107
|
+
// for Agent configuration until `agent.start(...)` is called.
|
|
108
|
+
function initialConfig(logger) {
|
|
109
|
+
const cfg = Object.assign({}, confDefaultOptions);
|
|
110
|
+
|
|
111
|
+
normalize(cfg, logger);
|
|
112
|
+
|
|
113
|
+
cfg.transport = new NoopApmClient();
|
|
114
|
+
|
|
115
|
+
return cfg;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createConfig(opts, logger) {
|
|
119
|
+
return new Config(opts, logger);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class Config {
|
|
123
|
+
constructor(opts, logger) {
|
|
124
|
+
// TODO: remove this call once config and logging-preamble tests are refactored
|
|
125
|
+
readEnvOptions();
|
|
126
|
+
|
|
127
|
+
const isLambda = isLambdaExecutionEnvironment();
|
|
128
|
+
const envOptions = getEnvironmentOptions();
|
|
129
|
+
|
|
130
|
+
setStartOptions(opts);
|
|
131
|
+
|
|
132
|
+
Object.assign(
|
|
133
|
+
this,
|
|
134
|
+
confDefaultOptions, // default options
|
|
135
|
+
getFileOptions(), // options read from config file
|
|
136
|
+
opts, // options passed in to agent.start()
|
|
137
|
+
envOptions, // options read from environment variables
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// The logger is used later in this function, so create/update it first.
|
|
141
|
+
// Unless a new custom `logger` was provided, we use the one created earlier
|
|
142
|
+
// in `configLogger()`.
|
|
143
|
+
const customLogger =
|
|
144
|
+
process.env.ELASTIC_APM_LOGGER === 'false' ? null : this.logger;
|
|
145
|
+
if (!customLogger && logger) {
|
|
146
|
+
logging.setLogLevel(logger, this.logLevel);
|
|
147
|
+
this.logger = logger;
|
|
148
|
+
} else {
|
|
149
|
+
this.logger = logging.createLogger(this.logLevel, customLogger);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fallback and validation handling for `serviceName` and `serviceVersion`.
|
|
153
|
+
if (this.serviceName) {
|
|
154
|
+
// A value here means an explicit value was given. Error out if invalid.
|
|
155
|
+
try {
|
|
156
|
+
validateServiceName(this.serviceName);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
this.logger.error(
|
|
159
|
+
'serviceName "%s" is invalid: %s',
|
|
160
|
+
this.serviceName,
|
|
161
|
+
err.message,
|
|
162
|
+
);
|
|
163
|
+
this.serviceName = null;
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
if (isLambda) {
|
|
167
|
+
this.serviceName = process.env.AWS_LAMBDA_FUNCTION_NAME;
|
|
168
|
+
} else if (isAzureFunctionsEnvironment && process.env.WEBSITE_SITE_NAME) {
|
|
169
|
+
this.serviceName = process.env.WEBSITE_SITE_NAME;
|
|
170
|
+
}
|
|
171
|
+
if (this.serviceName) {
|
|
172
|
+
try {
|
|
173
|
+
validateServiceName(this.serviceName);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
this.logger.warn(
|
|
176
|
+
'"%s" is not a valid serviceName: %s',
|
|
177
|
+
this.serviceName,
|
|
178
|
+
err.message,
|
|
179
|
+
);
|
|
180
|
+
this.serviceName = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!this.serviceName) {
|
|
184
|
+
// Zero-conf support: use package.json#name, else
|
|
185
|
+
// `unknown-${service.agent.name}-service`.
|
|
186
|
+
try {
|
|
187
|
+
this.serviceName = serviceNameFromPackageJson();
|
|
188
|
+
} catch (err) {
|
|
189
|
+
this.logger.warn(err.message);
|
|
190
|
+
}
|
|
191
|
+
if (!this.serviceName) {
|
|
192
|
+
this.serviceName = 'unknown-nodejs-service';
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (this.serviceVersion) {
|
|
197
|
+
// pass
|
|
198
|
+
} else if (isLambda) {
|
|
199
|
+
this.serviceVersion = process.env.AWS_LAMBDA_FUNCTION_VERSION;
|
|
200
|
+
} else if (isAzureFunctionsEnvironment && process.env.WEBSITE_SITE_NAME) {
|
|
201
|
+
// Leave this empty. There isn't a meaningful service version field
|
|
202
|
+
// in Azure Functions envvars, and falling back to package.json ends up
|
|
203
|
+
// finding the version of the "azure-functions-core-tools" package.
|
|
204
|
+
} else {
|
|
205
|
+
// Zero-conf support: use package.json#version, if possible.
|
|
206
|
+
try {
|
|
207
|
+
this.serviceVersion = serviceVersionFromPackageJson();
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// pass
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
normalize(this, this.logger);
|
|
214
|
+
|
|
215
|
+
if (isLambda || isAzureFunctionsEnvironment) {
|
|
216
|
+
// Override some config in AWS Lambda or Azure Functions environments.
|
|
217
|
+
this.metricsInterval = 0;
|
|
218
|
+
this.cloudProvider = 'none';
|
|
219
|
+
this.centralConfig = false;
|
|
220
|
+
}
|
|
221
|
+
if (this.metricsInterval === 0) {
|
|
222
|
+
this.breakdownMetrics = false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this.loggingPreambleData = getPreambleData(this);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Return a reasonably loggable object for this Config instance.
|
|
229
|
+
// Exclude undefined fields and complex objects like `logger`.
|
|
230
|
+
toJSON() {
|
|
231
|
+
const loggable = {};
|
|
232
|
+
for (const k in this) {
|
|
233
|
+
if (EXCLUDE_FIELDS[k] || this[k] === undefined) {
|
|
234
|
+
// pass
|
|
235
|
+
} else if (REDACT_FIELDS[k]) {
|
|
236
|
+
loggable[k] = REDACTED;
|
|
237
|
+
} else if (NICE_REGEXPS_FIELDS[k] && Array.isArray(this[k])) {
|
|
238
|
+
// JSON.stringify() on a RegExp is "{}", which isn't very helpful.
|
|
239
|
+
loggable[k] = this[k].map((r) =>
|
|
240
|
+
r instanceof RegExp ? r.toString() : r,
|
|
241
|
+
);
|
|
242
|
+
} else {
|
|
243
|
+
loggable[k] = this[k];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return loggable;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Returns an object showing the current config, excluding default values.
|
|
250
|
+
getCurrConfig() {
|
|
251
|
+
const currConfig = {};
|
|
252
|
+
|
|
253
|
+
// Start with the values from the logging preamble. This selected keys
|
|
254
|
+
// that were specified, and handles redaction.
|
|
255
|
+
for (let [k, v] of Object.entries(this.loggingPreambleData.config)) {
|
|
256
|
+
currConfig[k] = v.value;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Then add the current value of any var possibly set by central config.
|
|
260
|
+
currConfig.centralConfig = this.centralConfig;
|
|
261
|
+
if (this.centralConfig) {
|
|
262
|
+
for (let k of Object.values(CENTRAL_CONFIG_OPTS)) {
|
|
263
|
+
currConfig[k] = this[k];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return currConfig;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function validateServiceName(s) {
|
|
272
|
+
if (typeof s !== 'string') {
|
|
273
|
+
throw new Error('not a string');
|
|
274
|
+
} else if (!/^[a-zA-Z0-9 _-]+$/.test(s)) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
'contains invalid characters (allowed: a-z, A-Z, 0-9, _, -, <space>)',
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// findPkgInfo() looks up from the script dir (or cwd) for a "package.json" file
|
|
282
|
+
// from which to load the name and version. It returns:
|
|
283
|
+
// {
|
|
284
|
+
// startDir: "<full path to starting dir>",
|
|
285
|
+
// path: "/the/full/path/to/package.json", // may be null
|
|
286
|
+
// data: {
|
|
287
|
+
// name: "<the package name>", // may be missing
|
|
288
|
+
// version: "<the package version>" // may be missing
|
|
289
|
+
// }
|
|
290
|
+
// }
|
|
291
|
+
let pkgInfoCache;
|
|
292
|
+
function findPkgInfo() {
|
|
293
|
+
if (pkgInfoCache === undefined) {
|
|
294
|
+
// Determine a good starting dir from which to look for a "package.json".
|
|
295
|
+
let startDir =
|
|
296
|
+
require.main &&
|
|
297
|
+
require.main.filename &&
|
|
298
|
+
path.dirname(require.main.filename);
|
|
299
|
+
if (!startDir && process.argv[1]) {
|
|
300
|
+
// 'require.main' is undefined if the agent is preloaded with `node
|
|
301
|
+
// --require elastic-apm-node/... script.js`.
|
|
302
|
+
startDir = path.dirname(process.argv[1]);
|
|
303
|
+
}
|
|
304
|
+
if (!startDir) {
|
|
305
|
+
startDir = process.cwd();
|
|
306
|
+
}
|
|
307
|
+
pkgInfoCache = {
|
|
308
|
+
startDir,
|
|
309
|
+
path: null,
|
|
310
|
+
data: {},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Look up from the starting dir for a "package.json".
|
|
314
|
+
const { root } = path.parse(startDir);
|
|
315
|
+
let dir = startDir;
|
|
316
|
+
while (true) {
|
|
317
|
+
const pj = path.resolve(dir, 'package.json');
|
|
318
|
+
if (fs.existsSync(pj)) {
|
|
319
|
+
pkgInfoCache.path = pj;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
if (dir === root) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
dir = path.dirname(dir);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Attempt to load "name" and "version" from the package.json.
|
|
329
|
+
if (pkgInfoCache.path) {
|
|
330
|
+
try {
|
|
331
|
+
const data = JSON.parse(fs.readFileSync(pkgInfoCache.path));
|
|
332
|
+
if (data.name) {
|
|
333
|
+
// For backward compatibility, maintain the trimming done by
|
|
334
|
+
// https://github.com/npm/normalize-package-data#what-normalization-currently-entails
|
|
335
|
+
pkgInfoCache.data.name = data.name.trim();
|
|
336
|
+
}
|
|
337
|
+
if (data.version) {
|
|
338
|
+
pkgInfoCache.data.version = data.version;
|
|
339
|
+
}
|
|
340
|
+
} catch (_err) {
|
|
341
|
+
// Silently leave data empty.
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return pkgInfoCache;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function serviceNameFromPackageJson() {
|
|
349
|
+
const pkg = findPkgInfo();
|
|
350
|
+
if (!pkg.path) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`could not infer serviceName: could not find package.json up from ${pkg.startDir}`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (!pkg.data.name) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`could not infer serviceName: "${pkg.path}" does not contain a "name"`,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
if (typeof pkg.data.name !== 'string') {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`could not infer serviceName: "name" in "${pkg.path}" is not a string`,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
let serviceName = pkg.data.name;
|
|
366
|
+
|
|
367
|
+
// Normalize a namespaced npm package name, '@ns/name', to 'ns-name'.
|
|
368
|
+
const match = /^@([^/]+)\/([^/]+)$/.exec(serviceName);
|
|
369
|
+
if (match) {
|
|
370
|
+
serviceName = match[1] + '-' + match[2];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Sanitize, by replacing invalid service name chars with an underscore.
|
|
374
|
+
const SERVICE_NAME_BAD_CHARS = /[^a-zA-Z0-9 _-]/g;
|
|
375
|
+
serviceName = serviceName.replace(SERVICE_NAME_BAD_CHARS, '_');
|
|
376
|
+
|
|
377
|
+
// Disallow some weird sanitized values. For example, it is better to
|
|
378
|
+
// have the fallback "unknown-{service.agent.name}-service" than "_" or
|
|
379
|
+
// "____" or " ".
|
|
380
|
+
const ALL_NON_ALPHANUMERIC = /^[ _-]*$/;
|
|
381
|
+
if (ALL_NON_ALPHANUMERIC.test(serviceName)) {
|
|
382
|
+
serviceName = null;
|
|
383
|
+
}
|
|
384
|
+
if (!serviceName) {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`could not infer serviceName from name="${pkg.data.name}" in "${pkg.path}"`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return serviceName;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function serviceVersionFromPackageJson() {
|
|
394
|
+
const pkg = findPkgInfo();
|
|
395
|
+
if (!pkg.path) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`could not infer serviceVersion: could not find package.json up from ${pkg.startDir}`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
if (!pkg.data.version) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`could not infer serviceVersion: "${pkg.path}" does not contain a "version"`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
if (typeof pkg.data.version !== 'string') {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`could not infer serviceVersion: "version" in "${pkg.path}" is not a string`,
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
return pkg.data.version;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function normalize(opts, logger) {
|
|
414
|
+
const defaults = confDefaultOptions;
|
|
415
|
+
|
|
416
|
+
normalizeKeyValuePairs(opts, KEY_VALUE_OPTS, defaults, logger);
|
|
417
|
+
normalizeNumbers(opts, NUM_OPTS, defaults, logger);
|
|
418
|
+
normalizeInfinity(opts, MINUS_ONE_EQUAL_INFINITY, defaults, logger);
|
|
419
|
+
normalizeBytes(opts, BYTES_OPTS, defaults, logger);
|
|
420
|
+
normalizeArrays(opts, ARRAY_OPTS, defaults, logger);
|
|
421
|
+
normalizeDurationOptions(opts, DURATION_OPTS, defaults, logger);
|
|
422
|
+
normalizeBools(opts, BOOL_OPTS, defaults, logger);
|
|
423
|
+
normalizeIgnoreOptions(opts);
|
|
424
|
+
normalizeElasticsearchCaptureBodyUrls(opts);
|
|
425
|
+
normalizeDisableMetrics(opts);
|
|
426
|
+
normalizeSanitizeFieldNames(opts);
|
|
427
|
+
normalizeContextManager(opts, [], defaults, logger);
|
|
428
|
+
normalizeCloudProvider(opts, [], defaults, logger);
|
|
429
|
+
normalizeTransactionSampleRate(opts, [], defaults, logger);
|
|
430
|
+
normalizeTraceContinuationStrategy(opts, [], defaults, logger);
|
|
431
|
+
normalizeCustomMetricsHistogramBoundaries(opts, [], defaults, logger);
|
|
432
|
+
normalizeUrls(opts, URL_OPTS, defaults, logger);
|
|
433
|
+
|
|
434
|
+
// This must be after `normalizeDurationOptions()` and `normalizeBools()`
|
|
435
|
+
// because it synthesizes the deprecated `spanFramesMinDuration` and
|
|
436
|
+
// `captureSpanStackTraces` options into `spanStackTraceMinDuration`.
|
|
437
|
+
normalizeSpanStackTraceMinDuration(opts, [], defaults, logger);
|
|
438
|
+
|
|
439
|
+
truncateOptions(opts);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function truncateOptions(opts) {
|
|
443
|
+
if (opts.serviceVersion)
|
|
444
|
+
opts.serviceVersion = truncate(
|
|
445
|
+
String(opts.serviceVersion),
|
|
446
|
+
INTAKE_STRING_MAX_SIZE,
|
|
447
|
+
);
|
|
448
|
+
if (opts.hostname)
|
|
449
|
+
opts.hostname = truncate(String(opts.hostname), INTAKE_STRING_MAX_SIZE);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Exports.
|
|
453
|
+
module.exports = {
|
|
454
|
+
configLogger,
|
|
455
|
+
initialConfig,
|
|
456
|
+
createConfig,
|
|
457
|
+
normalize,
|
|
458
|
+
};
|