【问题标题】:How to get errors stack trace in Chrome extension content script?如何在 Chrome 扩展内容脚本中获取错误堆栈跟踪?
【发布时间】:2013-12-02 08:16:25
【问题描述】:

有一个Google Chrome extensioncontent script 可以处理所有标签页上发生的JS 错误。但问题是,没有一种常用的获取错误堆栈跟踪的方法不起作用。

比如Chrome扩展的content script中有一段代码:

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

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

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

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. 在您的谷歌浏览器中打开chrome://extensions,启用Developer mode http://i.imgur.com/5x5D6NP.png
  4. 点击Load unpacked extension按钮并选择/jsen文件夹的路径
  5. 打开/jsen/content.js文件并在window.addEventListener('error', function(e) {中添加console.log('JSEN', e.error.stack);
  6. 转到http://xpart.ru/_share/js.htm 并在 JS 控制台中查看结果(Ctrl+Shift+J)
  7. 尝试编辑/jsen/content.js 以获得正确的错误跟踪
  8. 要重新初始化Chrome扩展源代码点击http://i.imgur.com/SjFgkHA.png

【问题讨论】:

  • 您应该提到您希望将堆栈跟踪作为字符串,将其显示在控制台中是不够的。这个问题有点误导。
  • @Tibos 感谢您的评论。我已经更新了问题。

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


【解决方案1】:

正如您所提到的,在内容脚本上下文中捕获事件时,事件对象的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 你用一个可能更可靠的答案击败了我 6 分钟。令人印象深刻的是,Rob 已经就任何 future Chrome 扩展相关问题发布了详细的详细答案 :)
  • 这个答案在 Chrome 中仍然有效吗?在尝试了 T 的两种解决方案后,event.error 对象仍然为空。
  • @jsutton 是的,我刚刚在 42.0.2311.152 m 上测试过它,它可以工作。
  • 我有一个稍微具体的问题:这个实现适用于我的本地html文件,我的脚本在<script>标签之间,尽管当我将我的js分成一个单独的本地文件并使用@987654327 @,我不能再监听错误,而是在我的控制台中得到这个:Uncaught TypeError: Cannot read property 'stack' of null。但是,当我通过 ftp 将我的 html 和 js 文件上传到我的网络服务器时,该实现在我的网站上在线运行。知道为什么我不能在本地读取堆栈吗?
  • 实际上,看起来一旦在我的服务器上,我的外部脚本的内容被添加到网页加载中,而在本地,它们不是。即本地开发时javascript不渲染到html中
【解决方案2】:

问题

主要问题是JS context isolation,即“内容脚本在称为孤立世界的特殊环境中执行”这一事实。当然,这是一件好事,因为它避免了冲突并增强了安全性,但如果您想捕获错误,这也是一个问题。

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

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


解决方案

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

  1. 在网页本身中注入一些代码(并尝试使其尽可能不显眼以避免冲突)。
  2. 在该代码中注册错误事件侦听器并将错误数据存储在 DOM 节点的 dataset 中。
  3. 最后,在内容脚本中,使用 MutationObserver 观察此 DOM 节点属性的变化。
  4. 一旦检测到指定的 data-<...> 属性发生更改,获取它的新值,您的内容脚本中就会有错误信息:)

源代码

下面是一个完全执行此操作的示例扩展的源代码。

ma​​nifest.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 });

【讨论】:

  • 感谢您提供如此详细描述的又一个可行的解决方案。看来你真的是个不错的专家! :)
猜你喜欢
  • 2019-03-05
  • 2014-08-20
  • 2023-03-14
  • 2015-08-04
  • 1970-01-01
  • 2013-04-18
  • 1970-01-01
  • 2011-09-14
  • 2010-10-11
相关资源
最近更新 更多