我有一个简单的javascript错误记录机制,它看起来像这样:
window.onerror = function (ErrorMsg, Url, LineNumber, Col, Error) {
// ajax these to the server, including Error.stack}
问题是我还想在发生错误时获取局部变量和函数参数的值。这甚至可能吗?
我正在考虑修改Function原型,以便每次函数运行时,其参数都存储在全局字符串数组中,然后错误处理程序只会将此数组添加到ajax调用中。 JavaScript能做到吗?
答案 0 :(得分:11)
onerror()
中恢复本地范围吗?如果this
范围内没有window.onerror()
或可以直接访问的周围变量,则无法重新获得对已设置的变量的访问权。
您最想要访问的内容是this.arguments
或arguments
或等效内容,但已被销毁。获得键值关联数组或类似哈希的对象的任何希望都将涉及元编程(即,读取函数定义以获取变量名称,并获取异常报告以尝试抢救数据)。
有关类似内容的更多信息,请参阅此答案:
但这"缺乏功能"是一件好事:
如果您可以获得对您要求的内容的访问权限,则可能是Javascript引擎中的错误。为什么?因为变量状态和内容本身是导致异常/错误的原因,所以假设错误的代码不是问题的开始。
换句话说,如果你可以访问一个有缺陷的变量,那可能是进入无限循环的一扇门:
是。它可以。这可能是真的坏主意(参见#1),但这是可能的。这是一个关于从哪里开始的指针:
从那里开始,你想要做的就是推送this.arguments
或等同于一堆函数调用。但同样,由于许多原因,这种情况正在逼近疯狂。其中最重要的是需要复制所有值,以免引用变异变量,或者根本无法访问数据......就像我上面所说的那样,数据不好的问题一般来说。但是, 可能。
答案 1 :(得分:5)
这甚至可能吗?
没有。堆栈跟踪证明堆栈已展开,所有堆栈帧及其包含的所有局部变量都消失了。至于获取变量的名称,这在运行时甚至是不可能的。
答案 2 :(得分:2)
这里的其他答案都是现货,但我可能会提出一个建议,以略微不同的方式来实现这一目标。为什么不添加跟踪一个函数参数范围而不影响函数运行时的标记函数,而不是尝试跟踪程序中的所有作用域。例如:
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
个呼叫,您可以精确控制记录的内容,而无需任何侵入式黑客攻击。
答案 3 :(得分:1)
首先,我完全接受@Tomalak。
我也遇到了你需要调试远程运行应用程序的情况。
作为一种解决方法,我已经在fiddler中为您分配了我的代码。请根据您的需要进行修改。
警告:您必须使用try{..}catch(e){..}
包装函数体,如fiddler代码所示。
请阅读内联评论以便了解。
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();
我使用了一些例子来说明在不同情况下调用的函数的行为。
下面表示用于
的变量其他变量与Error
对象
答案 4 :(得分:1)
您可以在this link找到答案。
在从服务器获取捆绑包之前,您必须修改它们。例如,要解决您的问题,您可以按照以下方式对上述链接进行更改。在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...
};
我希望能帮助你。