-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
132 lines (112 loc) · 3.66 KB
/
index.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
const util = require('util');
function extendError(AWS, opts) {
const needRunAt = !(opts.noRunStack || false);
const paramsInspectOpts = opts.paramsInspectOptions || { showHidden: true };
const rsLines = opts.runStackLines || 3;
function getRunStack(target) {
const orig = Error.prepareStackTrace;
Error.prepareStackTrace = function(_, stack) { return stack };
const err = new Error;
Error.captureStackTrace(err, target);
const stack = err.stack;
Error.prepareStackTrace = orig;
return stack.map(line => line.toString());
}
function injectRequestInfo(err, req, runStack) {
try {
const host = req.service.endpoint.host;
const service = host.split('.')[0];
const reqInfo = {
host: host,
operation: req.operation,
params: req.params
};
const paramStr = util.inspect(req.params, paramsInspectOpts);
let reqErrStr = `AWS Request failed: ${service}.${req.operation}(${paramStr})\n`;
if (runStack) {
const rs = runStack.slice(0, rsLines);
reqErrStr += ' at ' + rs.join('\n at ') + '\n';
err.runStack = rs;
}
err.requestInfo = reqInfo;
err.stack = `${reqErrStr}\n${err.stack}`;
} catch(e) {
console.warn('Failed to inject RequestInfo to AWS error', e);
}
}
/*
* Extend a method with callback
*/
const origSend = AWS.Request.prototype.send;
AWS.Request.prototype.send = function sendEx(callback) {
if (!callback) {
return origSend.call(this)
}
const req = this;
let runStack = null;
if (needRunAt) runStack = getRunStack(arguments.callee).slice(2);
const callbackEx = function () {
if (arguments.length > 0) {
const err = arguments[0];
if (err instanceof Error) {
injectRequestInfo(err, req, runStack);
}
}
callback.apply(this, arguments);
};
return origSend.call(this, callbackEx);
}
/*
* Extend a promise method
*/
const origPromise = AWS.Request.prototype.promise;
AWS.Request.prototype.promise = function promiseEx() {
let runStack = null;
if (needRunAt) runStack = getRunStack(arguments.callee);
return origPromise.call(this)
.catch(err => {
injectRequestInfo(err, this, runStack);
throw err;
});
}
/*
* Extend a createReadStream method
*/
const origCreateReadStream = AWS.Request.prototype.createReadStream;
AWS.Request.prototype.createReadStream = function createReadStreamEx() {
const req = this;
let runStack = null;
if (needRunAt) runStack = getRunStack(arguments.callee);
const stream = origCreateReadStream.call(this);
const origOn = stream.on;
stream.on = function (name, callback) {
if (name === 'error') {
origOn.call(this, name, function(err) {
if (err instanceof Error) {
injectRequestInfo(err, req, runStack);
}
callback(err);
})
} else {
origOn.call(this, name, callback)
}
}
return stream
}
return AWS;
}
/**
* Extend AWS Error
*
* @param {Boolean} options.noRunStack - If it is true, the run stack is not outputed. default is false.
* @param {Number} options.runStackLines - Specify run stack lines outputed. default is 3.
* @param {Object} options.paramsInspectOptions - The options to inspect requset params, default is `{ showHidden: true }`.
* @param {AWS} options.AWS - For test. if not specified, load aws-sdk automatically.
* @return {AWS} aws-sdk
*/
module.exports = function extendAWSError(options) {
const opts = options || {};
const AWS = opts.AWS || require('aws-sdk');
delete opts.AWS;
return extendError(AWS, opts);
}