@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
package/lib/constants.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* Central location for shared constants
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
// The default span or transaction `type`.
|
|
13
|
+
DEFAULT_SPAN_TYPE: 'custom',
|
|
14
|
+
|
|
15
|
+
REDACTED: '[REDACTED]',
|
|
16
|
+
OUTCOME_FAILURE: 'failure',
|
|
17
|
+
OUTCOME_SUCCESS: 'success',
|
|
18
|
+
OUTCOME_UNKNOWN: 'unknown',
|
|
19
|
+
RESULT_SUCCESS: 'success',
|
|
20
|
+
RESULT_FAILURE: 'failure',
|
|
21
|
+
|
|
22
|
+
// https://github.com/elastic/apm/blob/main/specs/agents/tracing-instrumentation-messaging.md#receiving-trace-context
|
|
23
|
+
MAX_MESSAGES_PROCESSED_FOR_TRACE_CONTEXT: 1000,
|
|
24
|
+
|
|
25
|
+
// Config constants
|
|
26
|
+
INTAKE_STRING_MAX_SIZE: 1024,
|
|
27
|
+
CAPTURE_ERROR_LOG_STACK_TRACES_NEVER: 'never',
|
|
28
|
+
CAPTURE_ERROR_LOG_STACK_TRACES_MESSAGES: 'messages',
|
|
29
|
+
CAPTURE_ERROR_LOG_STACK_TRACES_ALWAYS: 'always',
|
|
30
|
+
CONTEXT_MANAGER_ASYNCHOOKS: 'asynchooks',
|
|
31
|
+
CONTEXT_MANAGER_ASYNCLOCALSTORAGE: 'asynclocalstorage',
|
|
32
|
+
TRACE_CONTINUATION_STRATEGY_CONTINUE: 'continue',
|
|
33
|
+
TRACE_CONTINUATION_STRATEGY_RESTART: 'restart',
|
|
34
|
+
TRACE_CONTINUATION_STRATEGY_RESTART_EXTERNAL: 'restart_external',
|
|
35
|
+
};
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
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
|
+
// Handle creating error event objects to be sent to APM server.
|
|
10
|
+
// https://github.com/elastic/apm-server/blob/master/docs/spec/v2/error.json
|
|
11
|
+
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
var path = require('path');
|
|
14
|
+
const util = require('util');
|
|
15
|
+
|
|
16
|
+
const { gatherStackTrace } = require('./stacktraces');
|
|
17
|
+
|
|
18
|
+
const MYSQL_ERROR_MSG_RE = /(ER_[A-Z_]+): /;
|
|
19
|
+
|
|
20
|
+
// ---- internal support functions
|
|
21
|
+
|
|
22
|
+
// Default `culprit` to the top of the stack or the highest non `library_frame`
|
|
23
|
+
// frame if such exists
|
|
24
|
+
function culpritFromStacktrace(frames) {
|
|
25
|
+
if (frames.length === 0) return;
|
|
26
|
+
|
|
27
|
+
var filename = frames[0].filename;
|
|
28
|
+
var fnName = frames[0].function;
|
|
29
|
+
for (var n = 0; n < frames.length; n++) {
|
|
30
|
+
if (!frames[n].library_frame) {
|
|
31
|
+
filename = frames[n].filename;
|
|
32
|
+
fnName = frames[n].function;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return filename ? fnName + ' (' + filename + ')' : fnName;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Infer the node.js module name from the top frame filename, if possible.
|
|
41
|
+
// Here `frames` is a data structure as returned by `parseStackTrace`.
|
|
42
|
+
//
|
|
43
|
+
// Examples:
|
|
44
|
+
// node_modules/mymodule/index.js
|
|
45
|
+
// ^^^^^^^^
|
|
46
|
+
// node_modules/@myorg/mymodule/index.js
|
|
47
|
+
// ^^^^^^^^^^^^^^^
|
|
48
|
+
// or on Windows:
|
|
49
|
+
// node_modules\@myorg\mymodule\lib\subpath\index.js
|
|
50
|
+
// ^^^^^^^^^^^^^^^
|
|
51
|
+
function _moduleNameFromFrames(frames) {
|
|
52
|
+
if (frames.length === 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
var frame = frames[0];
|
|
56
|
+
if (!frame.library_frame) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
var idx = frame.filename.lastIndexOf('node_modules' + path.sep);
|
|
60
|
+
if (idx === -1) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
var parts = frame.filename.slice(idx).split(path.sep);
|
|
64
|
+
if (!parts[1]) {
|
|
65
|
+
return null;
|
|
66
|
+
} else if (parts[1].startsWith('@')) {
|
|
67
|
+
if (!parts[2]) {
|
|
68
|
+
// node_modules/@foo
|
|
69
|
+
return null;
|
|
70
|
+
} else {
|
|
71
|
+
// Normalize the module name separator to '/', even on Windows.
|
|
72
|
+
return parts[1] + '/' + parts[2];
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
return parts[1];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Gather properties from `err` to be used for `error.exception.attributes`.
|
|
80
|
+
// If there are no properties to include, then it returns undefined.
|
|
81
|
+
function attributesFromErr(err) {
|
|
82
|
+
let n = 0;
|
|
83
|
+
const attrs = {};
|
|
84
|
+
const keys = Object.keys(err);
|
|
85
|
+
for (let i = 0; i < keys.length; i++) {
|
|
86
|
+
const key = keys[i];
|
|
87
|
+
if (key === 'stack') {
|
|
88
|
+
continue; // 'stack' seems to be enumerable in Node 0.11
|
|
89
|
+
}
|
|
90
|
+
if (key === 'code') {
|
|
91
|
+
continue; // 'code' is already used for `error.exception.code`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let val = err[key];
|
|
95
|
+
if (val === null) {
|
|
96
|
+
continue; // null is typeof object and well break the switch below
|
|
97
|
+
}
|
|
98
|
+
switch (typeof val) {
|
|
99
|
+
case 'function':
|
|
100
|
+
continue;
|
|
101
|
+
case 'object':
|
|
102
|
+
// Ignore all objects except Dates.
|
|
103
|
+
if (
|
|
104
|
+
typeof val.toISOString !== 'function' ||
|
|
105
|
+
typeof val.getTime !== 'function'
|
|
106
|
+
) {
|
|
107
|
+
continue;
|
|
108
|
+
} else if (Number.isNaN(val.getTime())) {
|
|
109
|
+
val = 'Invalid Date'; // calling toISOString() on invalid dates throws
|
|
110
|
+
} else {
|
|
111
|
+
val = val.toISOString();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
attrs[key] = val;
|
|
115
|
+
n++;
|
|
116
|
+
}
|
|
117
|
+
return n ? attrs : undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---- exports
|
|
121
|
+
|
|
122
|
+
function generateErrorId() {
|
|
123
|
+
return crypto.randomBytes(16).toString('hex');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Create an "error" APM event object to be sent to APM server.
|
|
127
|
+
//
|
|
128
|
+
// Required args:
|
|
129
|
+
// - Exactly one of `args.exception` or `args.logMessage` must be set.
|
|
130
|
+
// `args.exception` is an Error instance. `args.logMessage` is a log message
|
|
131
|
+
// string, or an object of the form `{ message: 'template', params: [ ... ]}`
|
|
132
|
+
// which will be formated with `util.format()`.
|
|
133
|
+
// - `args.id` - An ID for the error. It should be created with
|
|
134
|
+
// `errors.generateErrorId()`.
|
|
135
|
+
// - `args.log` {Logger}
|
|
136
|
+
// - `args.shouldCaptureAttributes` {Boolean}
|
|
137
|
+
// - `args.timestampUs` {Integer} - Timestamp of the error in microseconds.
|
|
138
|
+
// - `args.handled` {Boolean}
|
|
139
|
+
// - `args.sourceLinesAppFrames` {Integer} - Number of lines of source context
|
|
140
|
+
// to include in stack traces.
|
|
141
|
+
// - `args.sourceLinesLibraryFrames` {Integer} - Number of lines of source
|
|
142
|
+
// context to include in stack traces. This and the previous arg are typically
|
|
143
|
+
// select from the agent `sourceLines{Error,Span}{App,Library}Frames` config
|
|
144
|
+
// vars.
|
|
145
|
+
//
|
|
146
|
+
// Optional args:
|
|
147
|
+
// - `args.callSiteLoc` - A `Error.captureStackTrace`d object with a stack to
|
|
148
|
+
// include as `error.log.stacktrace`.
|
|
149
|
+
// - `args.traceContext` - The current TraceContext, if any.
|
|
150
|
+
// - `args.trans` - The current transaction, if any.
|
|
151
|
+
// - `args.errorContext` - An object to be included as `error.context`.
|
|
152
|
+
// - `args.message` - A message string that will be included as `error.log.message`
|
|
153
|
+
// if `args.exception` is given. Ignored if `args.logMessage` is given.
|
|
154
|
+
// - `args.exceptionType` - A string to use for `error.exception.type`. By
|
|
155
|
+
// default `args.exception.name` is used. This argument is only relevant if
|
|
156
|
+
// `args.exception` was provided.
|
|
157
|
+
//
|
|
158
|
+
// This always calls back with `cb(null, apmError)`, i.e. it doesn't fail.
|
|
159
|
+
function createAPMError(args, cb) {
|
|
160
|
+
let numAsyncStepsRemaining = 0; // finish() will call cb() only when this is 0.
|
|
161
|
+
|
|
162
|
+
const error = {
|
|
163
|
+
id: args.id,
|
|
164
|
+
timestamp: args.timestampUs,
|
|
165
|
+
};
|
|
166
|
+
if (args.traceContext) {
|
|
167
|
+
error.parent_id = args.traceContext.traceparent.id;
|
|
168
|
+
error.trace_id = args.traceContext.traceparent.traceId;
|
|
169
|
+
}
|
|
170
|
+
if (args.trans) {
|
|
171
|
+
error.transaction_id = args.trans.id;
|
|
172
|
+
error.transaction = {
|
|
173
|
+
name: args.trans.name,
|
|
174
|
+
type: args.trans.type,
|
|
175
|
+
sampled: args.trans.sampled,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (args.errorContext) {
|
|
179
|
+
error.context = args.errorContext;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (args.exception) {
|
|
183
|
+
// Handle an exception, i.e. `captureError(<an Error instance>, ...)`.
|
|
184
|
+
const err = args.exception;
|
|
185
|
+
const errMsg = String(err.message);
|
|
186
|
+
error.exception = {
|
|
187
|
+
message: errMsg,
|
|
188
|
+
type: args.exceptionType || String(err.name),
|
|
189
|
+
handled: args.handled,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if ('code' in err) {
|
|
193
|
+
error.exception.code = String(err.code);
|
|
194
|
+
} else {
|
|
195
|
+
// To provide better grouping of mysql errors that happens after the async
|
|
196
|
+
// boundery, we modify to exception type to include the custom mysql error
|
|
197
|
+
// type (e.g. ER_PARSE_ERROR)
|
|
198
|
+
var match = errMsg.match(MYSQL_ERROR_MSG_RE);
|
|
199
|
+
if (match) {
|
|
200
|
+
error.exception.code = match[1];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Optional add an alternative error message as well as the exception message.
|
|
205
|
+
if (args.message && typeof args.message === 'string') {
|
|
206
|
+
error.log = { message: args.message };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (args.shouldCaptureAttributes) {
|
|
210
|
+
const attrs = attributesFromErr(err);
|
|
211
|
+
if (attrs) {
|
|
212
|
+
error.exception.attributes = attrs;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
numAsyncStepsRemaining++;
|
|
217
|
+
gatherStackTrace(
|
|
218
|
+
args.log,
|
|
219
|
+
args.exception,
|
|
220
|
+
args.sourceLinesAppFrames,
|
|
221
|
+
args.sourceLinesLibraryFrames,
|
|
222
|
+
null, // filterCallSite
|
|
223
|
+
function (_err, stacktrace) {
|
|
224
|
+
// _err from gatherStackTrace is always null.
|
|
225
|
+
|
|
226
|
+
const culprit = culpritFromStacktrace(stacktrace);
|
|
227
|
+
if (culprit) {
|
|
228
|
+
error.culprit = culprit;
|
|
229
|
+
}
|
|
230
|
+
const moduleName = _moduleNameFromFrames(stacktrace);
|
|
231
|
+
if (moduleName) {
|
|
232
|
+
// TODO: consider if we should include this as it's not originally what module was intended for
|
|
233
|
+
error.exception.module = moduleName;
|
|
234
|
+
}
|
|
235
|
+
error.exception.stacktrace = stacktrace;
|
|
236
|
+
finish();
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
} else {
|
|
240
|
+
// Handle a logMessage, i.e. `captureError(<not an Error instance>, ...)`.
|
|
241
|
+
error.log = {};
|
|
242
|
+
const msg = args.logMessage;
|
|
243
|
+
if (typeof msg === 'string') {
|
|
244
|
+
error.log.message = msg;
|
|
245
|
+
} else if (typeof msg === 'object' && msg !== null) {
|
|
246
|
+
if (msg.message) {
|
|
247
|
+
error.log.message = util.format.apply(
|
|
248
|
+
this,
|
|
249
|
+
[msg.message].concat(msg.params),
|
|
250
|
+
);
|
|
251
|
+
error.log.param_message = msg.message;
|
|
252
|
+
} else {
|
|
253
|
+
error.log.message = util.inspect(msg);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
error.log.message = String(msg);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (args.callSiteLoc) {
|
|
261
|
+
numAsyncStepsRemaining++;
|
|
262
|
+
gatherStackTrace(
|
|
263
|
+
args.log,
|
|
264
|
+
args.callSiteLoc,
|
|
265
|
+
args.sourceLinesAppFrames,
|
|
266
|
+
args.sourceLinesLibraryFrames,
|
|
267
|
+
null, // filterCallSite
|
|
268
|
+
function (_err, stacktrace) {
|
|
269
|
+
// _err from gatherStackTrace is always null.
|
|
270
|
+
|
|
271
|
+
if (stacktrace) {
|
|
272
|
+
// In case there isn't any log object, we'll make a dummy message
|
|
273
|
+
// as the APM Server requires a message to be present if a
|
|
274
|
+
// stacktrace also present
|
|
275
|
+
if (!error.log) {
|
|
276
|
+
error.log = { message: error.exception.message };
|
|
277
|
+
}
|
|
278
|
+
error.log.stacktrace = stacktrace;
|
|
279
|
+
finish();
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
} else {
|
|
284
|
+
numAsyncStepsRemaining++;
|
|
285
|
+
setImmediate(finish);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function finish() {
|
|
289
|
+
numAsyncStepsRemaining--;
|
|
290
|
+
if (numAsyncStepsRemaining === 0) {
|
|
291
|
+
cb(null, error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = {
|
|
297
|
+
generateErrorId,
|
|
298
|
+
createAPMError,
|
|
299
|
+
|
|
300
|
+
// Exported for testing.
|
|
301
|
+
attributesFromErr,
|
|
302
|
+
_moduleNameFromFrames,
|
|
303
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
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 querystring = require('querystring');
|
|
9
|
+
|
|
10
|
+
const HEADER_FORM_URLENCODED = 'application/x-www-form-urlencoded';
|
|
11
|
+
const REDACTED = require('../constants').REDACTED;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Handles req.body as object or string
|
|
15
|
+
*
|
|
16
|
+
* Express provides multiple body parser middlewares with x-www-form-urlencoded
|
|
17
|
+
* handling. See http://expressjs.com/en/resources/middleware/body-parser.html
|
|
18
|
+
*
|
|
19
|
+
* @param {Object | String} body
|
|
20
|
+
* @param {Object} requestHeaders
|
|
21
|
+
* @param {Array<RegExp>} regexes
|
|
22
|
+
* @returns {Object | String} a copy of the body with the redacted fields
|
|
23
|
+
*/
|
|
24
|
+
function redactKeysFromPostedFormVariables(body, requestHeaders, regexes) {
|
|
25
|
+
// only redact from application/x-www-form-urlencoded
|
|
26
|
+
if (HEADER_FORM_URLENCODED !== requestHeaders['content-type']) {
|
|
27
|
+
return body;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// if body is a plain object, use redactKeysFromObject
|
|
31
|
+
if (body !== null && !Buffer.isBuffer(body) && typeof body === 'object') {
|
|
32
|
+
return redactKeysFromObject(body, regexes);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// if body is a string, use querystring to create object,
|
|
36
|
+
// pass to redactKeysFromObject, and reserialize as string
|
|
37
|
+
if (typeof body === 'string') {
|
|
38
|
+
const objBody = redactKeysFromObject(querystring.parse(body), regexes);
|
|
39
|
+
return querystring.stringify(objBody);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return body;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns a copy of the provided object. Each entry of the copy will have
|
|
47
|
+
* its value REDACTEd if the key matches any of the regexes
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} obj The source object be copied with redacted fields
|
|
50
|
+
* @param {Array<RegExp>} regexes RegExps to check if the entry value needd to be redacted
|
|
51
|
+
* @param {String} redactedStr The string to use for redacted values. Defaults to '[REDACTED]'.
|
|
52
|
+
* @returns {Object} Copy of the source object with REDACTED entries or the original if falsy or regexes is not an array
|
|
53
|
+
*/
|
|
54
|
+
function redactKeysFromObject(obj, regexes, redactedStr = REDACTED) {
|
|
55
|
+
if (!obj || !Array.isArray(regexes)) {
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
const result = {};
|
|
59
|
+
for (const key of Object.keys(obj)) {
|
|
60
|
+
const shouldRedact = regexes.some((regex) => regex.test(key));
|
|
61
|
+
result[key] = shouldRedact ? redactedStr : obj[key];
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
redactKeysFromObject,
|
|
68
|
+
redactKeysFromPostedFormVariables,
|
|
69
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
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
|
+
// A lib that provides a slightly enhanced method for making an HTTP request.
|
|
10
|
+
|
|
11
|
+
const { URL } = require('url');
|
|
12
|
+
|
|
13
|
+
// Getting handles on these `.request()` functions at the top-level ensures
|
|
14
|
+
// that we have *un*instrumented functions for internal HTTP request usage.
|
|
15
|
+
const coreHttpRequest = require('http').request;
|
|
16
|
+
const coreHttpsRequest = require('https').request;
|
|
17
|
+
|
|
18
|
+
// From https://github.com/nodejs/node/blob/v14.15.4/lib/internal/url.js#L1267-L1289
|
|
19
|
+
// for node v8 support.
|
|
20
|
+
//
|
|
21
|
+
// Utility function that converts a URL object into an ordinary
|
|
22
|
+
// options object as expected by the http.request and https.request
|
|
23
|
+
// APIs.
|
|
24
|
+
function urlToOptions(url) {
|
|
25
|
+
const options = {
|
|
26
|
+
protocol: url.protocol,
|
|
27
|
+
hostname:
|
|
28
|
+
typeof url.hostname === 'string' && url.hostname.startsWith('[')
|
|
29
|
+
? url.hostname.slice(1, -1)
|
|
30
|
+
: url.hostname,
|
|
31
|
+
hash: url.hash,
|
|
32
|
+
search: url.search,
|
|
33
|
+
pathname: url.pathname,
|
|
34
|
+
path: `${url.pathname || ''}${url.search || ''}`,
|
|
35
|
+
href: url.href,
|
|
36
|
+
};
|
|
37
|
+
if (url.port !== '') {
|
|
38
|
+
options.port = Number(url.port);
|
|
39
|
+
}
|
|
40
|
+
if (url.username || url.password) {
|
|
41
|
+
options.auth = `${url.username}:${url.password}`;
|
|
42
|
+
}
|
|
43
|
+
return options;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A wrapper around `{http|https}.request()` that adds support for a connection
|
|
47
|
+
// timeout separate from the existing `options.timeout`.
|
|
48
|
+
//
|
|
49
|
+
// The existing `options.timeout` to `http.request()` sets `socket.setTimeout()`
|
|
50
|
+
// which will emit the 'timeout' event if there is an period of socket idleness
|
|
51
|
+
// that is this long. In practice for short-lived requests, it is a timeout on
|
|
52
|
+
// getting the start of response data back from the server.
|
|
53
|
+
//
|
|
54
|
+
// The new `opts.connectTimeout` is a number of milliseconds count from socket
|
|
55
|
+
// creation to socket 'connect'. If this time is reached a 'connectTimeout'
|
|
56
|
+
// event will be emitted on the request object. As with 'timeout', it is up
|
|
57
|
+
// to the caller to handle destroying the request. See "Usage" below.
|
|
58
|
+
// In pratice this allows for a shorter timeout to see if the remote server
|
|
59
|
+
// is handling connections in a timely manner. To be useful, a `connectTimeout`
|
|
60
|
+
// is typically shorter than a given `timeout`.
|
|
61
|
+
//
|
|
62
|
+
// Usage:
|
|
63
|
+
// const { httpRequest } = require('./http-request')
|
|
64
|
+
//
|
|
65
|
+
// var req = httpRequest(url, {
|
|
66
|
+
// connectTimeout: connectTimeout,
|
|
67
|
+
// // Any {http|https}.request options ...
|
|
68
|
+
// timeout: timeout
|
|
69
|
+
// }, function onRes(res) {
|
|
70
|
+
// // Handling of the response ...
|
|
71
|
+
// })
|
|
72
|
+
//
|
|
73
|
+
// // For both 'timeout' and 'connectTimeout', it is the responsibility
|
|
74
|
+
// // of the caller to abort the request to clean up.
|
|
75
|
+
// //
|
|
76
|
+
// // This `req.destroy()` has the side-effect of self-induced
|
|
77
|
+
// // "socket hang up" error event, so typically an 'error' event handler
|
|
78
|
+
// // is also required. One technique is to pass a specific error to
|
|
79
|
+
// // `req.destroy(...)` that can be inspected in the 'error' event handler.
|
|
80
|
+
// req.on('timeout', function () {
|
|
81
|
+
// // ...
|
|
82
|
+
// req.destroy(new Error('got timeout'))
|
|
83
|
+
// });
|
|
84
|
+
//
|
|
85
|
+
// req.on('connectTimeout', function () {
|
|
86
|
+
// // ...
|
|
87
|
+
// req.destroy(new Error('got connectTimeout'))
|
|
88
|
+
// });
|
|
89
|
+
//
|
|
90
|
+
// req.on('error', function (err) {
|
|
91
|
+
// // ...
|
|
92
|
+
// })
|
|
93
|
+
//
|
|
94
|
+
// req.end()
|
|
95
|
+
//
|
|
96
|
+
function httpRequest(url, opts, cb) {
|
|
97
|
+
// Handle call signature:
|
|
98
|
+
// httpRequest(url: String, opts?: Object, cb?: Function)
|
|
99
|
+
if (typeof url !== 'string') {
|
|
100
|
+
throw new TypeError(
|
|
101
|
+
'"url" argument is not a string, this does not support the "httpRequest(opts, cb)" call signature',
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (typeof opts === 'function') {
|
|
105
|
+
cb = opts;
|
|
106
|
+
opts = {};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Pull out the 'connectTimeout' option that is handled here.
|
|
110
|
+
const { connectTimeout, ...otherOpts } = opts;
|
|
111
|
+
|
|
112
|
+
// While node v8.x is still supported, we need to merge options from the url
|
|
113
|
+
// and opts to call `http.request(opts, cb)`.
|
|
114
|
+
const u = new URL(url);
|
|
115
|
+
const mergedOpts = Object.assign(urlToOptions(u), otherOpts);
|
|
116
|
+
|
|
117
|
+
// http or https
|
|
118
|
+
let requestFn;
|
|
119
|
+
if (mergedOpts.protocol === 'http:') {
|
|
120
|
+
requestFn = coreHttpRequest;
|
|
121
|
+
} else if (mergedOpts.protocol === 'https:') {
|
|
122
|
+
requestFn = coreHttpsRequest;
|
|
123
|
+
} else {
|
|
124
|
+
throw new Error(`unsupported protocol: "${mergedOpts.protocol}"`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const req = requestFn(mergedOpts, cb);
|
|
128
|
+
|
|
129
|
+
if (connectTimeout) {
|
|
130
|
+
// Handle a connection timeout with a timer starting when the request
|
|
131
|
+
// socket is *created* ("socket" event) and ending when the socket
|
|
132
|
+
// is connected.
|
|
133
|
+
req.on('socket', function (socket) {
|
|
134
|
+
// log.trace({url: url}, 'start connectTimeout')
|
|
135
|
+
var connectTimer = setTimeout(function onConnectTimeout() {
|
|
136
|
+
// log.trace({url: url}, 'connectTimeout')
|
|
137
|
+
req.emit('connectTimeout');
|
|
138
|
+
}, connectTimeout);
|
|
139
|
+
|
|
140
|
+
socket.on('connect', function () {
|
|
141
|
+
// log.trace({url: url}, 'socket connected, clear connectTimeout')
|
|
142
|
+
clearTimeout(connectTimer);
|
|
143
|
+
connectTimer = null;
|
|
144
|
+
});
|
|
145
|
+
socket.on('close', function () {
|
|
146
|
+
if (connectTimer) {
|
|
147
|
+
// log.trace({url: url}, 'socket close with active connectTimer, clear connectTimeout')
|
|
148
|
+
clearTimeout(connectTimer);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return req;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
httpRequest,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// ---- mainline
|
|
162
|
+
|
|
163
|
+
// This main is only intended to demonstrate usage of this lib; not to be a
|
|
164
|
+
// useful tool.
|
|
165
|
+
//
|
|
166
|
+
// Example:
|
|
167
|
+
// node http-request.js https://www.elastic.co 100 1000 > elastic.html
|
|
168
|
+
//
|
|
169
|
+
// Example: download a ~1GB file, requiring a 30ms connect time, 2s idle timeout
|
|
170
|
+
// node http-request.js http://mirror.uoregon.edu/ubuntu-releases/20.04/ubuntu-20.04.1-live-server-amd64.iso 30 2000 > ubuntu.iso
|
|
171
|
+
//
|
|
172
|
+
// Example: connect timeout because google port 81 drops TCP SYN packets:
|
|
173
|
+
// node http-request.js http://www.google.com:81/foo 1000 10000
|
|
174
|
+
//
|
|
175
|
+
// Example: use `NODE_DEBUG=*` to see internal node debugging details
|
|
176
|
+
// NODE_DEBUG=* node http-request.js $url 30 1000
|
|
177
|
+
//
|
|
178
|
+
// Example: quick connection, slow response
|
|
179
|
+
// % cat server.js
|
|
180
|
+
// var http = require('http');
|
|
181
|
+
// http.createServer(function (req, res) {
|
|
182
|
+
// console.log('SERVER: got request')
|
|
183
|
+
// setTimeout((function() {
|
|
184
|
+
// res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
185
|
+
// res.write('line one\n')
|
|
186
|
+
// setTimeout((function() {
|
|
187
|
+
// res.write('line two\n')
|
|
188
|
+
// setTimeout((function() {
|
|
189
|
+
// res.write('line three\n')
|
|
190
|
+
// res.end()
|
|
191
|
+
// console.log('SERVER: responded')
|
|
192
|
+
// }), 500);
|
|
193
|
+
// }), 500);
|
|
194
|
+
// }), 500);
|
|
195
|
+
// }).listen(8080);
|
|
196
|
+
// % node server.js &
|
|
197
|
+
// % node http-request.js http://127.0.0.1:8080/ 10 1000
|
|
198
|
+
function main(argv) {
|
|
199
|
+
if (argv.length !== 5) {
|
|
200
|
+
process.stderr.write('http-request: error: incorrect number of args\n');
|
|
201
|
+
process.stderr.write(
|
|
202
|
+
'usage: http-request $url $connectTimeoutMs $timeoutMs\n',
|
|
203
|
+
);
|
|
204
|
+
process.exitCode = 1;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const url = argv[2];
|
|
208
|
+
const connectTimeout = Number(argv[3]);
|
|
209
|
+
const timeout = Number(argv[4]);
|
|
210
|
+
|
|
211
|
+
var req = httpRequest(
|
|
212
|
+
url,
|
|
213
|
+
{
|
|
214
|
+
timeout,
|
|
215
|
+
connectTimeout,
|
|
216
|
+
// TODO: log support
|
|
217
|
+
},
|
|
218
|
+
function onRes(res) {
|
|
219
|
+
res.pipe(process.stdout);
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
req.on('timeout', function () {
|
|
224
|
+
console.warn(
|
|
225
|
+
`http-request: response timeout (${timeout}ms): destroying request`,
|
|
226
|
+
);
|
|
227
|
+
req.destroy(new Error('got timeout event'));
|
|
228
|
+
process.exitCode = 28; // using cURL's errno for a timeout
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
req.on('connectTimeout', function () {
|
|
232
|
+
console.warn(
|
|
233
|
+
`http-request: connect timeout (${connectTimeout}ms): destroying request`,
|
|
234
|
+
);
|
|
235
|
+
req.destroy(new Error('got connectTimeout event'));
|
|
236
|
+
process.exitCode = 28; // using cURL's errno for a timeout
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
req.on('error', function (err) {
|
|
240
|
+
console.warn('http-request: request error:', err);
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
req.end();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (require.main === module) {
|
|
248
|
+
main(process.argv);
|
|
249
|
+
}
|