Make errors more debuggable; capture correct stack, include name (#1944)
No more errors like this in the logs! ``` INFO 2018-01-05T18:33:15.942Z Message.saveErrors: null Error at file:///C:/Users/Test/AppData/Local/Programs/signal-desktop/resources/app.asar/js/libtextsecure.js:30:33 at file:///C:/Users/Test/AppData/Local/Programs/signal-desktop/resources/app.asar/js/libtextsecure.js:138:3 at file:///C:/Users/Test/AppData/Local/Programs/signal-desktop/resources/app.asar/js/libtextsecure.js:40718:3 ``` It has no information in the title, and then the callstack points to the `new Error()` line in the old `errors.js`. This change will include the actual error name and message details in the stack, and will include the original http error stack as well if provided.
This commit is contained in:
parent
94a8c7e524
commit
6464d0a5fa
3 changed files with 164 additions and 100 deletions
|
@ -21,14 +21,36 @@
|
|||
}
|
||||
};
|
||||
|
||||
function inherit(Parent, Child) {
|
||||
Child.prototype = Object.create(Parent.prototype, {
|
||||
constructor: {
|
||||
value: Child,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
}
|
||||
function appendStack(newError, originalError) {
|
||||
newError.stack += '\nOriginal stack:\n' + originalError.stack;
|
||||
}
|
||||
|
||||
function ReplayableError(options) {
|
||||
options = options || {};
|
||||
this.name = options.name || 'ReplayableError';
|
||||
this.name = options.name || 'ReplayableError';
|
||||
this.message = options.message;
|
||||
|
||||
Error.call(this, options.message);
|
||||
|
||||
// Maintains proper stack trace, where our error was thrown (only available on V8)
|
||||
// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this);
|
||||
}
|
||||
|
||||
this.functionCode = options.functionCode;
|
||||
this.args = options.args;
|
||||
this.args = options.args;
|
||||
}
|
||||
ReplayableError.prototype = new Error();
|
||||
ReplayableError.prototype.constructor = ReplayableError;
|
||||
inherit(Error, ReplayableError);
|
||||
|
||||
ReplayableError.prototype.replay = function() {
|
||||
var argumentsAsArray = Array.prototype.slice.call(arguments, 0);
|
||||
|
@ -37,94 +59,103 @@
|
|||
};
|
||||
|
||||
function IncomingIdentityKeyError(number, message, key) {
|
||||
this.number = number.split('.')[0];
|
||||
this.identityKey = key;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.INIT_SESSION,
|
||||
args : [number, message]
|
||||
|
||||
args : [number, message],
|
||||
name : 'IncomingIdentityKeyError',
|
||||
message : "The identity of " + this.number + " has changed."
|
||||
});
|
||||
this.number = number.split('.')[0];
|
||||
this.name = 'IncomingIdentityKeyError';
|
||||
this.message = "The identity of " + this.number + " has changed.";
|
||||
this.identityKey = key;
|
||||
}
|
||||
IncomingIdentityKeyError.prototype = new ReplayableError();
|
||||
IncomingIdentityKeyError.prototype.constructor = IncomingIdentityKeyError;
|
||||
inherit(ReplayableError, IncomingIdentityKeyError);
|
||||
|
||||
function OutgoingIdentityKeyError(number, message, timestamp, identityKey) {
|
||||
this.number = number.split('.')[0];
|
||||
this.identityKey = identityKey;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.ENCRYPT_MESSAGE,
|
||||
args : [number, message, timestamp]
|
||||
args : [number, message, timestamp],
|
||||
name : 'OutgoingIdentityKeyError',
|
||||
message : "The identity of " + this.number + " has changed."
|
||||
});
|
||||
this.number = number.split('.')[0];
|
||||
this.name = 'OutgoingIdentityKeyError';
|
||||
this.message = "The identity of " + this.number + " has changed.";
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
OutgoingIdentityKeyError.prototype = new ReplayableError();
|
||||
OutgoingIdentityKeyError.prototype.constructor = OutgoingIdentityKeyError;
|
||||
inherit(ReplayableError, OutgoingIdentityKeyError);
|
||||
|
||||
function OutgoingMessageError(number, message, timestamp, httpError) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.ENCRYPT_MESSAGE,
|
||||
args : [number, message, timestamp]
|
||||
args : [number, message, timestamp],
|
||||
name : 'OutgoingMessageError',
|
||||
message : httpError ? httpError.message : 'no http error'
|
||||
});
|
||||
this.name = 'OutgoingMessageError';
|
||||
|
||||
if (httpError) {
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
}
|
||||
OutgoingMessageError.prototype = new ReplayableError();
|
||||
OutgoingMessageError.prototype.constructor = OutgoingMessageError;
|
||||
inherit(ReplayableError, OutgoingMessageError);
|
||||
|
||||
function SendMessageNetworkError(number, jsonData, httpError, timestamp) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.TRANSMIT_MESSAGE,
|
||||
args : [number, jsonData, timestamp]
|
||||
});
|
||||
this.name = 'SendMessageNetworkError';
|
||||
this.number = number;
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.TRANSMIT_MESSAGE,
|
||||
args : [number, jsonData, timestamp],
|
||||
name : 'SendMessageNetworkError',
|
||||
message : httpError.message
|
||||
});
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
SendMessageNetworkError.prototype = new ReplayableError();
|
||||
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
|
||||
inherit(ReplayableError, SendMessageNetworkError);
|
||||
|
||||
function SignedPreKeyRotationError(numbers, message, timestamp) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
|
||||
args : [numbers, message, timestamp]
|
||||
args : [numbers, message, timestamp],
|
||||
name : 'SignedPreKeyRotationError',
|
||||
message : "Too many signed prekey rotation failures"
|
||||
});
|
||||
this.name = 'SignedPreKeyRotationError';
|
||||
this.message = "Too many signed prekey rotation failures";
|
||||
}
|
||||
SignedPreKeyRotationError.prototype = new ReplayableError();
|
||||
SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;
|
||||
inherit(ReplayableError, SignedPreKeyRotationError);
|
||||
|
||||
function MessageError(message, httpError) {
|
||||
this.code = httpError.code;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.REBUILD_MESSAGE,
|
||||
args : [message]
|
||||
args : [message],
|
||||
name : 'MessageError',
|
||||
message : httpError.message
|
||||
});
|
||||
this.name = 'MessageError';
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
MessageError.prototype = new ReplayableError();
|
||||
MessageError.prototype.constructor = MessageError;
|
||||
inherit(ReplayableError, MessageError);
|
||||
|
||||
function UnregisteredUserError(number, httpError) {
|
||||
this.message = httpError.message;
|
||||
this.name = 'UnregisteredUserError';
|
||||
|
||||
Error.call(this, this.message);
|
||||
|
||||
// Maintains proper stack trace, where our error was thrown (only available on V8)
|
||||
// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this);
|
||||
}
|
||||
|
||||
this.number = number;
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
UnregisteredUserError.prototype = new Error();
|
||||
UnregisteredUserError.prototype.constructor = UnregisteredUserError;
|
||||
inherit(Error, UnregisteredUserError);
|
||||
|
||||
window.textsecure.UnregisteredUserError = UnregisteredUserError;
|
||||
window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
|
||||
|
|
|
@ -20,14 +20,36 @@
|
|||
}
|
||||
};
|
||||
|
||||
function inherit(Parent, Child) {
|
||||
Child.prototype = Object.create(Parent.prototype, {
|
||||
constructor: {
|
||||
value: Child,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
}
|
||||
function appendStack(newError, originalError) {
|
||||
newError.stack += '\nOriginal stack:\n' + originalError.stack;
|
||||
}
|
||||
|
||||
function ReplayableError(options) {
|
||||
options = options || {};
|
||||
this.name = options.name || 'ReplayableError';
|
||||
this.name = options.name || 'ReplayableError';
|
||||
this.message = options.message;
|
||||
|
||||
Error.call(this, options.message);
|
||||
|
||||
// Maintains proper stack trace, where our error was thrown (only available on V8)
|
||||
// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this);
|
||||
}
|
||||
|
||||
this.functionCode = options.functionCode;
|
||||
this.args = options.args;
|
||||
this.args = options.args;
|
||||
}
|
||||
ReplayableError.prototype = new Error();
|
||||
ReplayableError.prototype.constructor = ReplayableError;
|
||||
inherit(Error, ReplayableError);
|
||||
|
||||
ReplayableError.prototype.replay = function() {
|
||||
var argumentsAsArray = Array.prototype.slice.call(arguments, 0);
|
||||
|
@ -36,94 +58,103 @@
|
|||
};
|
||||
|
||||
function IncomingIdentityKeyError(number, message, key) {
|
||||
this.number = number.split('.')[0];
|
||||
this.identityKey = key;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.INIT_SESSION,
|
||||
args : [number, message]
|
||||
|
||||
args : [number, message],
|
||||
name : 'IncomingIdentityKeyError',
|
||||
message : "The identity of " + this.number + " has changed."
|
||||
});
|
||||
this.number = number.split('.')[0];
|
||||
this.name = 'IncomingIdentityKeyError';
|
||||
this.message = "The identity of " + this.number + " has changed.";
|
||||
this.identityKey = key;
|
||||
}
|
||||
IncomingIdentityKeyError.prototype = new ReplayableError();
|
||||
IncomingIdentityKeyError.prototype.constructor = IncomingIdentityKeyError;
|
||||
inherit(ReplayableError, IncomingIdentityKeyError);
|
||||
|
||||
function OutgoingIdentityKeyError(number, message, timestamp, identityKey) {
|
||||
this.number = number.split('.')[0];
|
||||
this.identityKey = identityKey;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.ENCRYPT_MESSAGE,
|
||||
args : [number, message, timestamp]
|
||||
args : [number, message, timestamp],
|
||||
name : 'OutgoingIdentityKeyError',
|
||||
message : "The identity of " + this.number + " has changed."
|
||||
});
|
||||
this.number = number.split('.')[0];
|
||||
this.name = 'OutgoingIdentityKeyError';
|
||||
this.message = "The identity of " + this.number + " has changed.";
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
OutgoingIdentityKeyError.prototype = new ReplayableError();
|
||||
OutgoingIdentityKeyError.prototype.constructor = OutgoingIdentityKeyError;
|
||||
inherit(ReplayableError, OutgoingIdentityKeyError);
|
||||
|
||||
function OutgoingMessageError(number, message, timestamp, httpError) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.ENCRYPT_MESSAGE,
|
||||
args : [number, message, timestamp]
|
||||
args : [number, message, timestamp],
|
||||
name : 'OutgoingMessageError',
|
||||
message : httpError ? httpError.message : 'no http error'
|
||||
});
|
||||
this.name = 'OutgoingMessageError';
|
||||
|
||||
if (httpError) {
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
}
|
||||
OutgoingMessageError.prototype = new ReplayableError();
|
||||
OutgoingMessageError.prototype.constructor = OutgoingMessageError;
|
||||
inherit(ReplayableError, OutgoingMessageError);
|
||||
|
||||
function SendMessageNetworkError(number, jsonData, httpError, timestamp) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.TRANSMIT_MESSAGE,
|
||||
args : [number, jsonData, timestamp]
|
||||
});
|
||||
this.name = 'SendMessageNetworkError';
|
||||
this.number = number;
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.TRANSMIT_MESSAGE,
|
||||
args : [number, jsonData, timestamp],
|
||||
name : 'SendMessageNetworkError',
|
||||
message : httpError.message
|
||||
});
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
SendMessageNetworkError.prototype = new ReplayableError();
|
||||
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
|
||||
inherit(ReplayableError, SendMessageNetworkError);
|
||||
|
||||
function SignedPreKeyRotationError(numbers, message, timestamp) {
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
|
||||
args : [numbers, message, timestamp]
|
||||
args : [numbers, message, timestamp],
|
||||
name : 'SignedPreKeyRotationError',
|
||||
message : "Too many signed prekey rotation failures"
|
||||
});
|
||||
this.name = 'SignedPreKeyRotationError';
|
||||
this.message = "Too many signed prekey rotation failures";
|
||||
}
|
||||
SignedPreKeyRotationError.prototype = new ReplayableError();
|
||||
SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;
|
||||
inherit(ReplayableError, SignedPreKeyRotationError);
|
||||
|
||||
function MessageError(message, httpError) {
|
||||
this.code = httpError.code;
|
||||
|
||||
ReplayableError.call(this, {
|
||||
functionCode : Type.REBUILD_MESSAGE,
|
||||
args : [message]
|
||||
args : [message],
|
||||
name : 'MessageError',
|
||||
message : httpError.message
|
||||
});
|
||||
this.name = 'MessageError';
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
MessageError.prototype = new ReplayableError();
|
||||
MessageError.prototype.constructor = MessageError;
|
||||
inherit(ReplayableError, MessageError);
|
||||
|
||||
function UnregisteredUserError(number, httpError) {
|
||||
this.message = httpError.message;
|
||||
this.name = 'UnregisteredUserError';
|
||||
|
||||
Error.call(this, this.message);
|
||||
|
||||
// Maintains proper stack trace, where our error was thrown (only available on V8)
|
||||
// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this);
|
||||
}
|
||||
|
||||
this.number = number;
|
||||
this.code = httpError.code;
|
||||
this.message = httpError.message;
|
||||
this.stack = httpError.stack;
|
||||
|
||||
appendStack(this, httpError);
|
||||
}
|
||||
UnregisteredUserError.prototype = new Error();
|
||||
UnregisteredUserError.prototype.constructor = UnregisteredUserError;
|
||||
inherit(Error, UnregisteredUserError);
|
||||
|
||||
window.textsecure.UnregisteredUserError = UnregisteredUserError;
|
||||
window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
<script type="text/javascript" src="../task_with_timeout.js" data-cover></script>
|
||||
|
||||
<script type="text/javascript" src="fake_api.js"></script>
|
||||
|
||||
<script type="text/javascript" src="errors_test.js"></script>
|
||||
<script type="text/javascript" src="helpers_test.js"></script>
|
||||
<script type="text/javascript" src="storage_test.js"></script>
|
||||
<script type="text/javascript" src="crypto_test.js"></script>
|
||||
|
|
Loading…
Reference in a new issue