Возможно ли получить локальную переменную и значения параметров с помощью window.onerror
У меня есть простой механизм регистрации ошибок JavaScript, и это выглядит примерно так:
window.onerror = function (ErrorMsg, Url, LineNumber, Col, Error) {
// ajax these to the server, including Error.stack}
Проблема в том, что я также хотел бы получить значение локальных переменных и параметров функции при возникновении ошибки. Это вообще возможно?
Я думаю об изменении прототипа функции таким образом, чтобы при каждом запуске функции ее аргументы сохранялись в глобальном массиве строк, а затем обработчик ошибок просто добавлял этот массив к вызову ajax. Может ли JavaScript сделать это?
Ответы
Ответ 1
# 1 Можно ли восстановить локальную область в onerror()
без черной магии?
Без this
, связанного в области window.onerror()
или окружающих переменных, которые будут непосредственно доступны, невозможно восстановить доступ к указанным вами переменным.
В основном вы хотите получить доступ к this.arguments
или arguments
или эквивалент, но это уничтожено. Любая надежда на получение ассоциативного массива с ключом или хэш-подобного объекта будет включать метапрограммирование (т.е. Чтение определения функции для получения имен переменных и получение отчета об исключении для попытки спасти данные).
Подробнее см. этот ответ:
Но эта "недостающая функциональность" - это хорошо:
Если вы можете получить доступ к тому, что вы просите, это, скорее всего, будет ошибкой в движке Javascript. Зачем? Поскольку сами переменные состояния и содержимое являются причиной исключения/ошибки, предполагая, что плохой код не является проблемой для начала.
Другими словами, если вы можете получить доступ к неисправной переменной, это может быть дверь в бесконечный цикл:
- Отказ из-за переменного содержимого.
- Обработчик ошибок активирован.
- Содержимое трассировки переменной.
- Отказ из-за переменного содержимого.
- Обработчик ошибок активирован.
- Содержимое трассировки переменной.
- Etc.
# 2 Может ли Javascript хранить все аргументы каждого вызова функции с помощью voodoo?
Да. Оно может. Вероятно, это действительно плохая идея (см. № 1), но это возможно. Вот указатель на то, с чего начать:
Оттуда то, что вы хотите сделать, это нажать this.arguments
или эквивалентно стеку вызовов функций. Но опять же, это приближается к безумию по многим причинам. Не последним из них является необходимость дублирования всех значений, чтобы вы не ссылались на измененные переменные или не могли вообще получить доступ к данным... и, как я уже сказал, проблема плохих данных в целом. Но все же это возможно.
Ответ 2
Возможно ли это?
Нет. Трассировка стека является доказательством того, что стек размотан, все стековые фреймы и все локальные переменные, которые они содержат, исчезли. Что касается получения имени переменной, это даже не возможно во время выполнения.
Ответ 3
Остальные ответы здесь, но я мог бы предложить предложение немного по-другому для этого. Вместо того, чтобы пытаться отслеживать всю область вашей программы, почему бы не добавить функцию тегов, которая отслеживает область действия одного параметра функции, не влияя на время выполнения функции. Например:
var globalRecord = {};
function record(name, fn) {
return function () {
var args = [].slice.call(arguments);
var record = globalRecord[name] = {
args: args,
arg: {}
};
args.unshift(function (name, value) {
return record[name] = value;
});
fn.apply(args, arguments);
}
}
// Then, you track variables like this
var func = record("func", function (record, a, b, c) {
record("a", a); // named parameters are accessible now
record("b", b); // if some error occurs in the function body
return a + b + c;
});
// Calling func still behaves as before.
func(1, 2, 3);
// Errors handled like this:
window.onerror = function () {
globalRecord.func.args; // ==> last set of arguments past to function
globalRecord.func.arg.a; // specific arguments recorded with names
};
Вы даже можете использовать этот метод для отслеживания области действия без использования функции путем анонимного вызова записанной функции.
record("test", function (record) {
var a = record("a", /* whatever */);
var b = record("b", /* ... */ );
// do scope specific stuff that might fail
})();
Конечно, это не полированная реализация на любом участке, но с небольшой работой, я думаю, вы сможете получить поведение, которое вы ищете, без серьезной черной магии. Путем выборочного добавления и удаления вызовов record
по мере необходимости, вы можете иметь точный контроль над тем, что регистрируется без каких-либо навязчивых хаков.
Ответ 4
Чтобы начать, я полностью соглашаюсь @Tomalak.
Я также попал в вашу ситуацию, когда мне нужно было отладить удаленное приложение в случае сбоя.
Как работа вокруг, я разработал свой код для вас в скрипке. Пожалуйста, измените в соответствии с вашими потребностями.
Предостережение: Вы должны обернуть тело функции try{..}catch(e){..}
, как показано в коде скрипта.
Пожалуйста, прочитайте встроенные комментарии для понимания.
window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {
console.log(errorObj);
}
window.addEventListener("reportOnError", function(e){
console.log(e.detail);
/*Send to the server or any listeners for analysis.*/
//Http.send(e.detail);
});
function ExceptionReport(ex, args, scope) {
var self = {};
self.message = ex.message;
self.stack = ex.stack;
self.name = ex.name;
self.whoCalled = args.callee.caller.name == "" ? "Window": args.callee.caller.name;
self.errorInFunction = args.callee.name;
self.instanceOf = scope.constructor;
self.KeyPairValues = getParamNames(arguments.callee.caller.toString(), Array.prototype.slice.call(args)); //Contains the parameters value set during runtime
window.dispatchEvent(new CustomEvent('reportOnError', {'detail':self}));
}
//Utilties
function getParamNames(fnBody, values) {
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
ARGUMENT_NAMES = /([^\s,]+)/g,
result = fnBody.slice(fnBody.indexOf('(')+1, fnBody.indexOf(')')).match(ARGUMENT_NAMES),
obj={};
fnBody.replace(STRIP_COMMENTS, '');
if(result !== null){
for(var i=0; i < result.length; i++){
obj[result[i]] = values.length !==0 ? values[i] : null;
}
}else{
obj = null;
}
return obj;
}
/*
This is a testing/sample function that throws the error
*/
function testing(a,b,c){
try{
dummy(1,2) ; //This line throws the error as reference error.
}catch(e){
ExceptionReport(e, arguments, this);
}
}
//Class Emulation: For instanceof illustration.
function testingClass(){
this.testing = testing;
}
//Named self executing function: This calls the function
var myvar = (function myvar(){
testing(1,2,3);
})();
//Illustrating instanceof in exception
var myVar2 = new testingClass();
myVar2.testing(1,2,3);
//Calling from global scope this is Window
testing(1,2,3);
//Without variables
testing();
Я использовал примеры для иллюстрации поведения функций, вызванных в разных обстоятельствах.
Ниже приведено значение varialble, используемое для
- self.KeyPairValues : используется для хранения набора параметров функции/переданного во время выполнения
- self.errorInFunction: здесь хранится имя ошибки функции.
- self.whoCalled: здесь хранится имя функции, вызывающая неисправную функцию
- self.instanceOf. Здесь хранится имя экземпляра, называемого созданием нового экземпляра.
Другие переменные такие же, как в Error
object
Ответ 5
Вы можете найти свой ответ в этой ссылке.
Прежде чем брать пакеты с сервера, вы должны их изменить. Например, чтобы решить вашу проблему, вы можете внести изменения в указанную ссылку следующим образом. В классе BuildBundleContent
сделайте это изменение:
contents.Insert(blockContentIndex,
string.Format("if(customErrorLogging)customErrorLogging({0}, this){1}",
errVariable, hasContent ? ";" : ""));
Если в модулях вы должны использовать что-то вроде:
var self = this;
Вы можете использовать:
contents.Insert(blockContentIndex,
string.Format("if(customErrorLogging)customErrorLogging({0}, self ? self : this){1}",
errVariable, hasContent ? ";" : ""));
И в добавленном файле js:
"use strict";
var customErrorLogging = function (ex, module) {
console.log(module);
//do something...
};
Я надеюсь помочь вам.