如何在Chrome扩展程序内容脚本中获取错误堆栈跟踪?

时间:2013-12-02 08:16:26

标签: javascript google-chrome google-chrome-extension

有一个Google Chrome extension content script来处理所有标签页上出现的JS错误。但问题是没有一种常见的获取错误堆栈跟踪的方法不起作用。

例如,Chrome扩展程序content script中有一段代码:

window.addEventListener('error', function(event) {
    console.log(event.error.stack); // event.error will be null
}, false);

如果我在网页中调用此代码,那么event.error将包含Error属性stack的对象。

尝试使用以下方法获取堆栈跟踪时遇到同样的问题:

console.log((new Error()).stack));

是否有人知道在Chrome扩展程序的content script内获取错误堆栈跟踪的一些工作问题?

错误堆栈跟踪必须以stringArray接收,这意味着不仅仅是通过调用console.trace()来获取JS控制台中的某些输出。

如何重现

  1. 下载https://mega.co.nz/#!ENw00YAC!92gBZEoLCO9jPsWyKht4dbjYyo0Zk-PU5YAj0h88-3Q
  2. jzen.zip解压缩到某个/jsen文件夹
  3. 在您的Google Chrome中打开chrome://extensions,启用Developer mode http://i.imgur.com/5x5D6NP.png
  4. 点击Load unpacked extension按钮,然后选择/jsen文件夹
  5. 的路径
  6. 打开/jsen/content.js文件,在console.log('JSEN', e.error.stack);
  7. 中添加window.addEventListener('error', function(e) {
  8. 转到http://xpart.ru/_share/js.htm并在JS控制台中看到结果(Ctrl + Shift + J)
  9. 尝试编辑/jsen/content.js以获取正确的错误跟踪
  10. 要重新初始化Chrome扩展程序源代码,请点击http://i.imgur.com/SjFgkHA.png

2 个答案:

答案 0 :(得分:12)

正如您所提到的,在内容脚本上下文中捕获事件时,事件对象的error属性为null,但在网页上下文中捕获时,它具有所需的信息。因此,解决方案是在网页上下文中捕获事件并使用消息传递将其传递到内容脚本。

// This code will be injected to run in webpage context
function codeToInject() {
    window.addEventListener('error', function(e) {
        var error = {
            stack: e.error.stack
            // Add here any other properties you need, like e.filename, etc...
        };
        document.dispatchEvent(new CustomEvent('ReportError', {detail:error}));
    });
}

document.addEventListener('ReportError', function(e) {
    console.log('CONTENT SCRIPT', e.detail.stack);
});

//Inject code
var script = document.createElement('script');
script.textContent = '(' + codeToInject + '())';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

使用的技术描述于:

答案 1 :(得分:3)

问题

主要问题是 JS context isolation ,即“内容脚本在称为孤立世界的特殊环境中执行”。

  

每个孤立的世界都会看到自己的(窗口)对象版本。分配给对象会影响对象的独立副本...

     

......任何人都无法读取对方的事件处理程序。事件处理程序按照分配顺序调用。


解决方案

关于可行的解决方案(a.k.a.hack)包括以下步骤:

  1. 将一些代码注入网页本身(并尝试尽可能不突兀地避免冲突)。
  2. 在该代码中注册错误的事件监听器并将错误数据存储在DOM节点的 dataset 中。
  3. 最后,在内容脚本中,使用 MutationObserver 来监视此DOM节点属性的更改。
  4. 一旦检测到指定的 data-< ...> 属性发生更改,请抓住其新值并在内容脚本中找到错误信息:)

  5. 源代码

    以下是完全相同的示例扩展的源代码。

    <强>的manifest.json:

    {
        "manifest_version": 2,
    
        "name":    "Test Extension",
        "version": "0.0",
    
        "content_scripts": [{
            "matches":    ["*://*/*"],
            "js":         ["content.js"],
            "run_at":     "document_start",
            "all_frames": true
        }],
    }
    

    <强> content.js

    /* This <script> element will function as an "error-proxy" 
     * for the content-script */
    var errorProxy = document.createElement('script');
    errorProxy.id = 'myErrorProxyScriptID';
    errorProxy.dataset.lastError = '';
    
    /* Make the content as non-obtrusive as possible */
    errorProxy.textContent = [
        '',
        '(function() {',
        '    var script = document.querySelector("script#' + errorProxy.id + '");',
        '    window.addEventListener("error", function(evt) {',
        '        script.dataset.lastError = evt.error.stack;',
        '    }, true);',
        '})();',
        ''].join('\n');
    
    /* Add the <script> element to the DOM */
    document.documentElement.appendChild(errorProxy);
    
    /* Create an observer for `errorProxy`'s attributes
     * (the `data-last-error` attribute is of interest) */
    var errorObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if ((mutation.type === 'attributes')
                    && (mutation.attributeName === 'data-last-error')) {
                console.log('Content script detected new error:\n',
                            errorProxy.dataset.lastError);
            }
        });
    });
    errorObserver.observe(errorProxy, { attributes: true });