-
Notifications
You must be signed in to change notification settings - Fork 5.7k
/
handle-error.js
186 lines (163 loc) · 6.86 KB
/
handle-error.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
'use strict';
const path = require('path');
const isObject = require('type/object/is');
const chalk = require('chalk');
const isStandaloneExecutable = require('../utils/isStandaloneExecutable');
const resolveLocalServerlessPath = require('./resolve-local-serverless-path');
const slsVersion = require('./../../package').version;
const sfeVersion = require('@serverless/dashboard-plugin/package.json').version;
const { platformClientVersion } = require('@serverless/dashboard-plugin');
const ServerlessError = require('../serverless-error');
const tokenizeException = require('../utils/tokenize-exception');
const resolveIsLocallyInstalled = require('../utils/is-locally-installed');
const isTelemetryDisabled = require('../utils/telemetry/areDisabled');
const { storeLocally: storeTelemetryLocally, send: sendTelemetry } = require('../utils/telemetry');
const generateTelemetryPayload = require('../utils/telemetry/generatePayload');
const resolveErrorLocation = require('../utils/telemetry/resolve-error-location');
const resolveInput = require('./resolve-input');
const consoleLog = (message) => process.stdout.write(`${message}\n`);
const writeMessage = (title, message) => {
let line = '';
while (line.length < 56 - title.length) {
line = `${line}-`;
}
process.stdout.write(' \n');
consoleLog(chalk.yellow(` ${title} ${line}`));
consoleLog(' ');
if (message) {
consoleLog(` ${message.split('\n').join('\n ')}`);
}
consoleLog(' ');
};
const isErrorCodeNormative = RegExp.prototype.test.bind(/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$/);
module.exports = async (exception, options = {}) => {
if (!isObject(options)) options = {};
// Due to the fact that the handler can be invoked via fallback, we need to support both `serverless`
// and `isLocallyInstalled` + `isInvokedByGlobalInstallation` properties
// TODO: Support for these properties should be removed with next major
const {
isUncaughtException,
isLocallyInstalled: passedIsLocallyInstalled,
isInvokedByGlobalInstallation: passedIsInvokedByGlobalInstallation,
command,
options: cliOptions,
commandSchema,
serviceDir,
configuration,
serverless,
hasTelemetryBeenReported,
} = options;
const isLocallyInstalled = serverless ? serverless.isLocallyInstalled : passedIsLocallyInstalled;
const isInvokedByGlobalInstallation = serverless
? serverless.isInvokedByGlobalInstallation
: passedIsInvokedByGlobalInstallation;
// If provided serverless instance is a local fallback, and we're not in context of it
// Pass error handling to this local fallback implementation
if (isInvokedByGlobalInstallation && !(await resolveIsLocallyInstalled())) {
const localServerlessPath = await resolveLocalServerlessPath();
try {
// Attempt to use error handler from local version
// TODO: Remove local version fallback with next major (when its moved to the top of the process)
try {
require(path.resolve(localServerlessPath, 'lib/cli/handle-error'))(exception, {
serverless,
isLocallyInstalled,
isUncaughtException,
command,
options: cliOptions,
commandSchema,
serviceDir,
configuration,
hasTelemetryBeenReported,
});
return;
} catch (error) {
// Pass and attempt to use old-style error handler from local version
// Ugly mock of serverless below is used to ensure that Framework version will be logged with `(local)` suffix
require(path.resolve(localServerlessPath, 'lib/classes/Error')).logError(exception, {
serverless: serverless || { isLocallyInstalled },
forceExit: isUncaughtException,
});
return;
}
} catch (err) {
// This is just a fallback as for most (all?) versions it shouldn't happen
throw new ServerlessError(
'Could not resolve path to locally installed error handler.',
'INVALID_LOCAL_SERVERLESS_ERROR_HANDLER'
);
}
}
const exceptionTokens = tokenizeException(exception);
const isUserError = !isUncaughtException && exceptionTokens.isUserError;
writeMessage(
exceptionTokens.title,
exceptionTokens.stack && (!isUserError || process.env.SLS_DEBUG)
? exceptionTokens.stack
: exceptionTokens.message
);
if (!isUserError && !process.env.SLS_DEBUG) {
const debugInfo = [
' ',
' For debugging logs, run again after setting the',
' "SLS_DEBUG=*" environment variable.',
].join('');
consoleLog(chalk.red(debugInfo));
consoleLog(' ');
}
const platform = process.platform;
const nodeVersion = process.version.replace(/^[v|V]/, '');
consoleLog(chalk.yellow(' Get Support --------------------------------------------'));
consoleLog(`${chalk.yellow(' Docs: ')}docs.serverless.com`);
consoleLog(`${chalk.yellow(' Bugs: ')}github.com/serverless/serverless/issues`);
consoleLog(`${chalk.yellow(' Issues: ')}forum.serverless.com`);
consoleLog(' ');
consoleLog(chalk.yellow(' Your Environment Information ---------------------------'));
consoleLog(chalk.yellow(` Operating System: ${platform}`));
consoleLog(chalk.yellow(` Node Version: ${nodeVersion}`));
const installationModePostfix = await (async () => {
if (isStandaloneExecutable) return ' (standalone)';
if (isLocallyInstalled != null) return isLocallyInstalled ? ' (local)' : '';
return (await resolveIsLocallyInstalled()) ? ' (local)' : '';
})();
consoleLog(
chalk.yellow(` Framework Version: ${slsVersion}${installationModePostfix}`)
);
consoleLog(chalk.yellow(` Plugin Version: ${sfeVersion}`));
consoleLog(chalk.yellow(` SDK Version: ${platformClientVersion}`));
const componentsVersion = (() => {
try {
return require('@serverless/components/package').version;
} catch (error) {
return 'Unavailable';
}
})();
consoleLog(chalk.yellow(` Components Version: ${componentsVersion}`));
consoleLog(' ');
if (
!isTelemetryDisabled &&
hasTelemetryBeenReported === false &&
(serverless ? serverless.isTelemetryReportedExternally : true) &&
resolveInput().commandSchema // Do not report for unrecognized commands
) {
const telemetryPayload = await generateTelemetryPayload({
command,
options: cliOptions,
commandSchema,
serviceDir,
configuration,
serverless,
});
const failureReason = {
kind: isUserError ? 'user' : 'programmer',
code: exception.code,
};
if (!isUserError || !exception.code || !isErrorCodeNormative(exception.code)) {
failureReason.location = resolveErrorLocation(exceptionTokens);
}
await storeTelemetryLocally({ ...telemetryPayload, failureReason, outcome: 'failure' });
await sendTelemetry();
}
process.exitCode = 1;
if (isUncaughtException) process.exit();
};