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

时间:2022-08-22 21:21:34

To make debugging easier, I'm capturing all of the console logs in Chrome so that users who submit a feedback entry will also submit all of the logs to our server. When someone encounters a problem in production, I can first and foremost get them back to work so that I can then sit down and more thoroughly go through all of the logs to determine the root cause of whatever issue the user encountered in production.

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

The technique I use to capture the logs involves overriding console.log so that all text entered in the first argument gets stored in an array while simultaneously invoking the legacy function so that I can still see the logs in the console too.

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

The problem is when there's the occasional uncaught exception. These aren't included in the uploaded logs, so it's not always clear what caused the problem. So I tried overriding ReferenceError by writing a JavaScript function that takes a function as an argument, then returns a new function that does stuff with it, like storing data in a variable, and then invoking the legacy function as the last step:

问题是偶尔会出现未捕获的异常。这些不包含在上载的日志中,因此并不总是清楚导致问题的原因。所以我尝试通过编写一个将函数作为参数的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);
    }           

}

To test the overrideException function, I ran the following code on the console:

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

ReferenceError = overrideException(ReferenceError);

Afterwards, I tested the returned function, the new ReferenceError, by manually throwing a ReferenceError:

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

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

The resulting output on the console is:

控制台上的结果输出是:

ReferenceError: YES!! IT WORKS! HAHAHA!

ReferenceError:是!!有用!哈哈哈!

And checking the global variable output from the overrideException function shows that it did indeed run:

从overrideException函数检查全局变量输出显示它确实运行:

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)"

Now, here's where things start to fall apart. In our code, we're not going to know when an uncaught exception occurs, so I tested it by attempting to run a function that doesn't exist:

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

ttt();

Which results in:

结果如下:

ReferenceError: ttt is not defined

ReferenceError:未定义ttt

However, unlike the case where we explicitly throw an error, in this case, the function doesn't fire, and we're left with only the legacy functionality. The contents of the variable output is the same as in the first test.

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

So the question seems to be this: How do we override the ReferenceError functionality that the JavaScript engine uses to throw errors so that it's the same one we use when we throw a ReferenceError?

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

Keep in mind that my problem is limited only to Chrome at this time; I'm building a Chrome Packaged app.

请注意,此时我的问题仅限于Chrome;我正在构建Chrome打包应用。

1 个解决方案

#1


12  

I have done quite a bit of research for the same reason: I wanted to log errors and report them.

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

"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.

“覆盖”本机类型(无论是ReferenceError,String还是Array)是不可能的。

Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.

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

You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).

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

That doesn't help you much.

这对你没有多大帮助。

But not to worry....

但不要担心....

(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.

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

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

See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.

请看小提琴的例子。由于OP是特定于Chrome的,因此仅在Chrome中进行了测试。

(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:

(2)由于(1)的改进,这不再是必要的,但是为了完整性,我在这里留下了第二种技术,并且因为不能保证在所有浏览器上都能使用错误。您有时也会看到以下内容:

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...

FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.

仅供参考,所有这些与谷歌闭包编译器库中的goog.debug非常类似,它是在Gmail开发期间创建的,旨在实现这一目的。特别感兴趣的是goog.debug.ErrorHandler和goog.debug.ErrorReporter。

#1


12  

I have done quite a bit of research for the same reason: I wanted to log errors and report them.

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

"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.

“覆盖”本机类型(无论是ReferenceError,String还是Array)是不可能的。

Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.

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

You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).

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

That doesn't help you much.

这对你没有多大帮助。

But not to worry....

但不要担心....

(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.

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

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

See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.

请看小提琴的例子。由于OP是特定于Chrome的,因此仅在Chrome中进行了测试。

(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:

(2)由于(1)的改进,这不再是必要的,但是为了完整性,我在这里留下了第二种技术,并且因为不能保证在所有浏览器上都能使用错误。您有时也会看到以下内容:

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...

FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.

仅供参考,所有这些与谷歌闭包编译器库中的goog.debug非常类似,它是在Gmail开发期间创建的,旨在实现这一目的。特别感兴趣的是goog.debug.ErrorHandler和goog.debug.ErrorReporter。