如何在 Chrome 的 JavaScript 中覆盖/扩展 ReferenceError?

Posted

技术标签:

【中文标题】如何在 Chrome 的 JavaScript 中覆盖/扩展 ReferenceError?【英文标题】:How can I override/extend ReferenceError in Chrome's JavaScript? 【发布时间】:2013-07-16 18:51:22 【问题描述】:

为了使调试更容易,我在 Chrome 中捕获了所有控制台日志,以便提交反馈条目的用户也将所有日志提交到我们的服务器。当有人在生产中遇到问题时,我首先可以让他们恢复工作,这样我就可以坐下来更彻底地查看所有日志,以确定用户在生产中遇到的任何问题的根本原因。

我用来捕获日志的技术涉及覆盖 console.log,以便在第一个参数中输入的所有文本都存储在一个数组中,同时调用旧函数,这样我仍然可以在控制台中看到日志。

问题在于偶尔会出现未捕获的异常。这些不包含在上传的日志中,因此并不总是清楚导致问题的原因。所以我尝试通过编写一个 javascript 函数来覆盖 ReferenceError,该函数接受一个函数作为参数,然后返回一个新函数来处理它,比如将数据存储在一个变量中,然后在最后一步调用旧函数:

function overrideException(legacyFn)   

    /** arguments for original fn **/
    return function() 

        var args = [];

        args[0] = arguments[0];

        // pass in as arguments to original function and store result to 
          // prove we overrode the ReferenceError
        output = ">> " + legacyFn.apply(this, args).stack;

        return legacyFn.apply(this, arguments);
               


为了测试 overrideException 函数,我在控制台上运行了以下代码:

ReferenceError = overrideException(ReferenceError);

之后,我通过手动抛出 ReferenceError 来测试返回的函数,新的 ReferenceError:

throw new ReferenceError("YES!! IT WORKS! HAHAHA!");

控制台上的结果输出是:

参考错误:是的!有用!哈哈哈!

从 overrideException 函数检查全局变量 output 表明它确实运行了:

output
  ">> ReferenceError: YES!! IT WORKS! HAHAHA!
  at ReferenceError (<anonymous>)
  at new <anonymous> (<anonymous>:18:35)
  at <anonymous>:2:7
  at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
  at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
  at Object.InjectedScript.evaluate (<anonymous>:440:21)"

现在,事情开始分崩离析。在我们的代码中,我们不知道何时发生未捕获的异常,因此我尝试运行一个不存在的函数来测试它:

ttt();

结果:

ReferenceError: ttt 未定义

但是,与我们显式抛出错误的情况不同,在这种情况下,函数不会触发,我们只剩下遗留功能。变量output的内容与第一次测试相同。

所以问题似乎是这样的:我们如何覆盖 JavaScript 引擎用来抛出错误的 ReferenceError 功能,使其与我们在抛出 ReferenceError 时使用的功能相同?

请记住,目前我的问题仅限于 Chrome;我正在构建一个 Chrome 打包应用程序。

【问题讨论】:

猜测您不想将代码包装在 try/catch 中,然后处理错误对象然后重新抛出? @CrazyTrain - 我可以做到,而且我可能会做到。然而,这很乏味,也可能会错过一些东西。我喜欢防伪且包罗万象的解决方案,因为它们通常也是“防我太忙” :) 我的计划当然是使用更多的 try/catch,但这似乎是一个非常好的解决方案,所以我开始深入研究它。 我的意思是一个整体的try/catch,它包含了你所有的代码。由于似乎目标不是处理适当的错误,而是以某种方式重新格式化它们,因此将所有代码包装在单个 try/catch 中似乎与您当前替换构造函数的方法没有什么不同。你只需要确保你重新抛出你得到的任何错误。 @CrazyTrain - 我确实考虑过,但随后整个应用程序将停止工作......嗯......或者它会......我可能只是出于好奇而尝试你的建议,但我真的很想看看如何覆盖这些原生 Error 对象。 :) @CrazyTrain - 这是一个创造性的建议,但问题是 try/catch 中的代码仅在页面最初加载并加载所有其他函数和事件处理程序时运行一次。一旦运行,它就不再运行,因此每个函数和事件处理程序仍然需要自己独立的 try/catch 块。希望这会有所帮助! 【参考方案1】:

出于同样的原因,我做了很多研究:我想记录错误并报告它们。

无法“覆盖”本机类型(ReferenceErrorStringArray)。

Chrome 会在运行任何 Javascript 之前绑定这些,因此重新定义 window.ReferenceError 无效。

您可以使用ReferenceError.prototype.extension = function() return 0; 之类的东西扩展ReferenceError,甚至覆盖toString(为了保持一致性,请在页面上尝试,而不是在开发工具上尝试)。

这对你没有多大帮助。

但不用担心....

(1) 使用window.onerror 获取未捕获错误的文件名、1-indexed 行号、0-indexed 位置以及错误本身。

var errorData = [];
onerror = function(message, file, line, position, error) 
    errorData.push(message:message, file:file, line:line, position:position, error:error);
;

有关示例,请参阅fiddle。由于 OP 是特定于 Chrome 的,因此只能在 Chrome 中进行测试。

(2) 由于对 (1) 的改进,这不再是必需的,但为了完整起见,我将第二个技术留在这里,因为 onerror 是 not guaranteed to work for all errors on all browsers。您有时还会看到以下内容:

var errors = [];
function protectedFunction(f) 
    return function() 
        try 
            f.apply(this, arguments);
         catch(e) 
            errors.push(e);
            throw e;
        
    ;

setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...

仅供参考,所有这些都与 Google Closure Compiler 库中所做的非常相似,goog.debug 是在 Gmail 开发期间创建的,目的就是这样做。特别感兴趣的是goog.debug.ErrorHandlergoog.debug.ErrorReporter

【讨论】:

您先生,真是个天才!我试了一下,实际上我的代码中有一个我不知道的真正未被捕获的异常,它被记录到我的日志框架中!我将需要更多地解决这个问题,因为我注意到它在某些回调中不起作用。我希望您的 setTimeout/setInterval 方法适用于这些情况。 +1 谢谢。 (1) 应该适用于所有未捕获的异常(无论是来自脚本评估、事件侦听器等)。如果您发现它不起作用的情况,请告知。 明天我将不得不更多地使用它,我可能只是犯了一个错误,但我记得其他一些事件处理程序抛出了在 chrome.app.window.create 中生成的错误,而不是window.onerror 处理它们。我正在 Chrome 打包的应用程序 create 方法回调函数中尝试这个。 chrome.app.window.create 似乎对我造成了onerror;只要记住在正确的窗口上设置onerror。由于 Chrome 应用程序有一个背景页面,因此请确保也将其设置在那里。 哦,我发现我们可以onerror 中得到原始错误——以及堆栈跟踪(参见更新的答案和jsFiddle)。跨度>

以上是关于如何在 Chrome 的 JavaScript 中覆盖/扩展 ReferenceError?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 chrome 中的内联 javascript 中添加断点

如何在 Chrome 的 JavaScript 中覆盖/扩展 ReferenceError?

如何在 selenium 中使用无头 Chrome 启用 JavaScript

如何在 Chrome 的性能分析器中调试匿名 JavaScript 函数?

如何在没有Javascript调试的情况下在网站启动时在Visual Studio中启动新的Chrome实例?

如何在 Mac 上的 Chrome 开发者工具中搜索我所有的 javascript?