【问题标题】:JavaScript global event mechanismJavaScript 全局事件机制
【发布时间】:2023-03-10 13:06:01
【问题描述】:

我想捕获每个抛出的未定义函数错误。 JavaScript 中是否有全局错误处理工具?用例是从 flash 中捕获未定义的函数调用。

【问题讨论】:

  • 一旦发现错误,您想如何处理?您只需要记录它以便创建缺少的函数,还是希望阻止异常破坏您的代码?
  • 我想获取被调用的缺失函数的名称,并根据某些字符串的存在调用我自己的函数。例如,对带有字符串“close”的函数的任何调用都会调用我的 close()。我也想在那时捕获错误。
  • exceptionsjs.com 提供了此功能,并且可以定制为仅通过其“保护”功能捕获与未定义功能相关的错误。

标签: javascript dom-events


【解决方案1】:

这对您有帮助吗:

<script type="text/javascript">
window.onerror = function() {
    alert("Error caught");
};

xxx();
</script>

我不确定它如何处理 Flash 错误...

更新:它在 Opera 中不起作用,但我现在正在破解 Dragonfly 以查看它得到了什么。关于黑客蜻蜓的建议来自这个问题:

Mimic Window. onerror in Opera using javascript

【讨论】:

  • 加上 msg、file_loc、line_no 参数,这应该对我有用。谢谢!
  • 我是否正确理解此代码会覆盖任何现有的错误处理程序?
  • @MarsRobertson No.
  • @atilkanwindow.onerror = function() { alert(42) }; 现在答案中的代码:window.onerror = function() { alert("Error caught"); }; 没有被覆盖,我仍然不确定..
  • @MarsRobertson 我明白了。它可能会覆盖。是的,确实如此,刚刚测试过。使用 addEventListener 会更好。
【解决方案2】:

复杂的错误处理

如果您的错误处理非常复杂,因此本身可能会引发错误,那么添加一个标志来指示您是否已经处于“errorHandling-Mode”是很有用的。像这样:

var appIsHandlingError = false;

window.onerror = function() {
    if (!appIsHandlingError) {
        appIsHandlingError = true;
        handleError();
    }
};

function handleError() {
    // graceful error handling
    // if successful: appIsHandlingError = false;
}

否则你可能会陷入无限循环。

【讨论】:

  • 或者更安全的方法是在 handleError 方法周围使用 try-catch。
  • 如果在错误处理中有异步调用,则不能使用 try catch。所以他的解决方案仍然是很好的解决方案
  • @EmrysMyrooin 仍然会通过错误处理导致同步或异步循环,并且可能在没有可用堆栈信息的情况下崩溃。
  • 我认为标志应该在某个时候重置,对吧?我相信我们需要一个 try/catch if big if,并确保在离开 onerror 方法之前重置标志。否则,只会处理一个错误。
【解决方案3】:

如何捕获未处理的 Javascript 错误

window.onerror 事件分配给事件处理程序,例如:

<script type="text/javascript">
window.onerror = function(msg, url, line, col, error) {
   // Note that col & error are new to the HTML 5 spec and may not be 
   // supported in every browser.  It worked for me in Chrome.
   var extra = !col ? '' : '\ncolumn: ' + col;
   extra += !error ? '' : '\nerror: ' + error;

   // You can view the information in an alert to see things working like this:
   alert("Error: " + msg + "\nurl: " + url + "\nline: " + line + extra);

   // TODO: Report this error via ajax so you can keep track
   //       of what pages have JS issues

   var suppressErrorAlert = true;
   // If you return true, then error alerts (like in older versions of 
   // Internet Explorer) will be suppressed.
   return suppressErrorAlert;
};
</script>

如代码中所述,如果window.onerror 的返回值为true,则浏览器应禁止显示警告对话框。

window.onerror 事件何时触发?

简而言之,当 1.) 存在未捕获的异常或 2.) 发生编译时错误时,会引发该事件。

未捕获的异常

  • 抛出“一些消息”
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document;,安全异常

编译错误

  • &lt;script&gt;{&lt;/script&gt;
  • &lt;script&gt;for(;)&lt;/script&gt;
  • &lt;script&gt;"oops&lt;/script&gt;
  • setTimeout("{", 10);,它将尝试将第一个参数编译为 一个脚本

支持 window.onerror 的浏览器

  • Chrome 13+
  • 火狐6.0+
  • Internet Explorer 5.5+
  • Opera 11.60+
  • Safari 5.1+

截图:

将上面的 onerror 代码添加到测试页面后的示例:

<script type="text/javascript">
call_something_undefined();
</script>

AJAX 错误报告示例

var error_data = {
    url: document.location.href,
};

if(error != null) {
    error_data['name'] = error.name; // e.g. ReferenceError
    error_data['message'] = error.line;
    error_data['stack'] = error.stack;
} else {
    error_data['msg'] = msg;
    error_data['filename'] = filename;
    error_data['line'] = line;
    error_data['col'] = col;
}

var xhr = new XMLHttpRequest();

xhr.open('POST', '/ajax/log_javascript_error');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
    if (xhr.status === 200) {
        console.log('JS error logged');
    } else if (xhr.status !== 200) {
        console.error('Failed to log JS error.');
        console.error(xhr);
        console.error(xhr.status);
        console.error(xhr.responseText);
    }
};
xhr.send(JSON.stringify(error_data));

JSFiddle:

https://jsfiddle.net/nzfvm44d/

参考资料:

【讨论】:

  • 值得一提的是,当手动创建throw 时,firefox 不会返回错误消息。 stackoverflow.com/questions/15036165/…
  • 很好的答案。您可以使用 JSNLog 之类的包来实现“通过 ajax 报告此错误”,它会为您执行 ajax 和服务器端日志记录。
  • 除了这个答案之外,我还添加了 err 对象,以便获取堆栈跟踪。 stackoverflow.com/a/20972210/511438。现在我可以通过更多反馈进行开发,因为我的开发错误在页面顶部显示为一个框(正如我创建的那样)。
  • 将 onerror 实现打包在 try-catch(ignore) 块中,防止 onerror() 抛出更多错误不是更好吗? (不是这里的情况,只是为了确定)
  • @super1ha1 你能提供一个jsfiddle吗?我不认为浏览器会在 onerror 事件处理程序停止触发事件时引入重大更改。你可以试试这个 jsfiddle 来查看 onerror 处理程序是否正常工作:jsfiddle.net/nzfvm44d 在 Chrome 版本 62.0.3202.94(官方构建)(64 位)中这仍然适用于我。
【解决方案4】:
// display error messages for a page, but never more than 3 errors
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "\n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;

【讨论】:

    【解决方案5】:

    我建议尝试Trackjs

    这是作为服务的错误日志记录。

    设置起来非常简单。只需在每一页添加一行

    还有其他服务,例如Sentry(如果您可以托管自己的服务器,它是开源的),但它没有 Trackjs 做的事情。 Trackjs 记录用户在他们的浏览器和您的网络服务器之间的交互,以便您可以实际跟踪导致错误的用户步骤,而不仅仅是文件和行号引用(可能还有堆栈跟踪)。

    【讨论】:

    • TrackJS doesn't seem to have a free tier 了,虽然有免费试用。
    • 这对于跟踪和警告错误很好,但并不能真正解决处理部分。理想情况下,我认为提问者正在寻找一种方法来处理这些错误,以便其余代码仍然可以运行。
    • 如果你捕捉到错误事件并添加一个 xhr 调用到带有堆栈跟踪和应用程序状态的记录器怎么办? trackjs 怎么样?
    • @inf3rno 它不仅仅是一个堆栈跟踪,它只是跟踪错误的开始。 TrackJS 将跟踪整个用户会话,以便您查看导致它的原因。这里以截图为例trackjs.com/assets/images/screenshot.png
    【解决方案6】:

    试试Atatus,它为现代网络应用提供高级错误跟踪和真实用户监控。

    https://www.atatus.com/

    让我解释一下如何在所有浏览器中获得相当完整的堆栈跟踪。

    JavaScript 中的错误处理

    现代 Chrome 和 Opera 完全支持 ErrorEvent 和 window.onerror 的 HTML 5 草案规范。在这两种浏览器中,您都可以使用window.onerror,或正确绑定到“错误”事件:

    // Only Chrome & Opera pass the error object.
    window.onerror = function (message, file, line, col, error) {
        console.log(message, "from", error.stack);
        // You can send data to your server
        // sendError(data);
    };
    // Only Chrome & Opera have an error attribute on the event.
    window.addEventListener("error", function (e) {
        console.log(e.error.message, "from", e.error.stack);
        // You can send data to your server
        // sendError(data);
    })
    

    不幸的是,Firefox、Safari 和 IE 仍然存在,我们也必须支持它们。由于堆栈跟踪在 window.onerror 中不可用,我们必须做更多的工作。

    事实证明,要从错误中获取堆栈跟踪,我们唯一能做的就是将所有代码包装在 try{ }catch(e){ } 块中,然后查看 e.stack。我们可以使用一个名为 wrap 的函数来简化这个过程,该函数接受一个函数并返回一个具有良好错误处理能力的新函数。

    function wrap(func) {
        // Ensure we only wrap the function once.
        if (!func._wrapped) {
            func._wrapped = function () {
                try{
                    func.apply(this, arguments);
                } catch(e) {
                    console.log(e.message, "from", e.stack);
                    // You can send data to your server
                    // sendError(data);
                    throw e;
                }
            }
        }
        return func._wrapped;
    };
    

    这行得通。您手动包装的任何函数都会有很好的错误处理,但事实证明,在大多数情况下,我们实际上可以自动为您完成。

    通过更改addEventListener 的全局定义,使其自动包装回调,我们可以在大多数代码周围自动插入try{ }catch(e){ }。这让现有代码继续工作,但增加了高质量的异常跟踪。

    var addEventListener = window.EventTarget.prototype.addEventListener;
    window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
        addEventListener.call(this, event, wrap(callback), bubble);
    }
    

    我们还需要确保removeEventListener 继续工作。目前不会,因为addEventListener 的参数已更改。同样,我们只需要在 prototype 对象上解决这个问题:

    var removeEventListener = window.EventTarget.prototype.removeEventListener;
    window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
        removeEventListener.call(this, event, callback._wrapped || callback, bubble);
    }
    

    将错误数据传输到您的后端

    您可以使用图像标签发送错误数据,如下所示

    function sendError(data) {
        var img = newImage(),
            src = 'http://yourserver.com/jserror&data=' + encodeURIComponent(JSON.stringify(data));
    
        img.crossOrigin = 'anonymous';
        img.onload = function success() {
            console.log('success', data);
        };
        img.onerror = img.onabort = function failure() {
            console.error('failure', data);
        };
        img.src = src;
    }
    

    免责声明:我是https://www.atatus.com/ 的网络开发人员。

    【讨论】:

    • 什么是 yourserver.com/jserrorREST、Web 服务、Wcf 服务 ?后端有什么简单的吗?
    • 如果你想将 js 错误从用户浏览器发送到你的服务器。您必须编写后端(http://yourserver.com)才能接收和存储。如果您选择atatus.com,则无需执行任何操作。只需在页面中包含两行脚本即可。
    【解决方案7】:

    window.onerror 似乎无法访问所有可能的错误。具体来说,它忽略了:

    1. &lt;img&gt; 加载错误(响应 >= 400)。
    2. &lt;script&gt; 加载错误(响应 >= 400)。
    3. 如果您的应用中有许多其他库也在以未知方式(jquery、angular 等)操纵window.onerror,则会出现全局错误。
    4. 现在可能很多情况下我都没有遇到过(iframe、堆栈溢出等)。

    这里是捕获许多此类错误的脚本的开头,以便您可以在开发过程中为您的应用添加更可靠的调试。

    (function(){
    
    /**
     * Capture error data for debugging in web console.
     */
    
    var captures = [];
    
    /**
     * Wait until `window.onload`, so any external scripts
     * you might load have a chance to set their own error handlers,
     * which we don't want to override.
     */
    
    window.addEventListener('load', onload);
    
    /**
     * Custom global function to standardize 
     * window.onerror so it works like you'd think.
     *
     * @see http://www.quirksmode.org/dom/events/error.html
     */
    
    window.onanyerror = window.onanyerror || onanyerrorx;
    
    /**
     * Hook up all error handlers after window loads.
     */
    
    function onload() {
      handleGlobal();
      handleXMLHttp();
      handleImage();
      handleScript();
      handleEvents();
    }
    
    /**
     * Handle global window events.
     */
    
    function handleGlobal() {
      var onerrorx = window.onerror;
      window.addEventListener('error', onerror);
    
      function onerror(msg, url, line, col, error) {
        window.onanyerror.apply(this, arguments);
        if (onerrorx) return onerrorx.apply(null, arguments);
      }
    }
    
    /**
     * Handle ajax request errors.
     */
    
    function handleXMLHttp() {
      var sendx = XMLHttpRequest.prototype.send;
      window.XMLHttpRequest.prototype.send = function(){
        handleAsync(this);
        return sendx.apply(this, arguments);
      };
    }
    
    /**
     * Handle image errors.
     */
    
    function handleImage() {
      var ImageOriginal = window.Image;
      window.Image = ImageOverride;
    
      /**
       * New `Image` constructor. Might cause some problems,
       * but not sure yet. This is at least a start, and works on chrome.
       */
    
      function ImageOverride() {
        var img = new ImageOriginal;
        onnext(function(){ handleAsync(img); });
        return img;
      }
    }
    
    /**
     * Handle script errors.
     */
    
    function handleScript() {
      var HTMLScriptElementOriginal = window.HTMLScriptElement;
      window.HTMLScriptElement = HTMLScriptElementOverride;
    
      /**
       * New `HTMLScriptElement` constructor.
       *
       * Allows us to globally override onload.
       * Not ideal to override stuff, but it helps with debugging.
       */
    
      function HTMLScriptElementOverride() {
        var script = new HTMLScriptElement;
        onnext(function(){ handleAsync(script); });
        return script;
      }
    }
    
    /**
     * Handle errors in events.
     *
     * @see http://stackoverflow.com/questions/951791/javascript-global-error-handling/31750604#31750604
     */
    
    function handleEvents() {
      var addEventListenerx = window.EventTarget.prototype.addEventListener;
      window.EventTarget.prototype.addEventListener = addEventListener;
      var removeEventListenerx = window.EventTarget.prototype.removeEventListener;
      window.EventTarget.prototype.removeEventListener = removeEventListener;
    
      function addEventListener(event, handler, bubble) {
        var handlerx = wrap(handler);
        return addEventListenerx.call(this, event, handlerx, bubble);
      }
    
      function removeEventListener(event, handler, bubble) {
        handler = handler._witherror || handler;
        removeEventListenerx.call(this, event, handler, bubble);
      }
    
      function wrap(fn) {
        fn._witherror = witherror;
    
        function witherror() {
          try {
            fn.apply(this, arguments);
          } catch(e) {
            window.onanyerror.apply(this, e);
            throw e;
          }
        }
        return fn;
      }
    }
    
    /**
     * Handle image/ajax request errors generically.
     */
    
    function handleAsync(obj) {
      var onerrorx = obj.onerror;
      obj.onerror = onerror;
      var onabortx = obj.onabort;
      obj.onabort = onabort;
      var onloadx = obj.onload;
      obj.onload = onload;
    
      /**
       * Handle `onerror`.
       */
    
      function onerror(error) {
        window.onanyerror.call(this, error);
        if (onerrorx) return onerrorx.apply(this, arguments);
      };
    
      /**
       * Handle `onabort`.
       */
    
      function onabort(error) {
        window.onanyerror.call(this, error);
        if (onabortx) return onabortx.apply(this, arguments);
      };
    
      /**
       * Handle `onload`.
       *
       * For images, you can get a 403 response error,
       * but this isn't triggered as a global on error.
       * This sort of standardizes it.
       *
       * "there is no way to get the HTTP status from a 
       * request made by an img tag in JavaScript."
       * @see http://stackoverflow.com/questions/8108636/how-to-get-http-status-code-of-img-tags/8108646#8108646
       */
    
      function onload(request) {
        if (request.status && request.status >= 400) {
          window.onanyerror.call(this, request);
        }
        if (onloadx) return onloadx.apply(this, arguments);
      }
    }
    
    /**
     * Generic error handler.
     *
     * This shows the basic implementation, 
     * which you could override in your app.
     */
    
    function onanyerrorx(entity) {
      var display = entity;
    
      // ajax request
      if (entity instanceof XMLHttpRequest) {
        // 400: http://example.com/image.png
        display = entity.status + ' ' + entity.responseURL;
      } else if (entity instanceof Event) {
        // global window events, or image events
        var target = entity.currentTarget;
        display = target;
      } else {
        // not sure if there are others
      }
    
      capture(entity);
      console.log('[onanyerror]', display, entity);
    }
    
    /**
     * Capture stuff for debugging purposes.
     *
     * Keep them in memory so you can reference them
     * in the chrome debugger as `onanyerror0` up to `onanyerror99`.
     */
    
    function capture(entity) {
      captures.push(entity);
      if (captures.length > 100) captures.unshift();
    
      // keep the last ones around
      var i = captures.length;
      while (--i) {
        var x = captures[i];
        window['onanyerror' + i] = x;
      }
    }
    
    /**
     * Wait til next code execution cycle as fast as possible.
     */
    
    function onnext(fn) {
      setTimeout(fn, 0);
    }
    
    })();
    

    可以这样使用:

    window.onanyerror = function(entity){
      console.log('some error', entity);
    };
    

    完整的脚本有一个默认实现,它尝试打印出它接收到的实体/错误的半可读“显示”版本。可用于为特定于应用程序的错误处理程序提供灵感。默认实现还保留对最后 100 个错误实体的引用,因此您可以在它们发生后在 Web 控制台中检查它们,如下所示:

    window.onanyerror0
    window.onanyerror1
    ...
    window.onanyerror99
    

    注意:这可以通过覆盖多个浏览器/本机构造函数的方法来实现。这可能会产生意想不到的副作用。但是,在开发过程中使用它非常有用,可以找出错误发生的位置,在开发过程中将日志发送到 NewRelic 或 Sentry 等服务,以便我们可以在开发过程中测量错误,并在暂存时调试正在发生的事情更深层次。然后可以在生产中关闭它。

    希望这会有所帮助。

    【讨论】:

    【解决方案8】:

    还应该保留之前关联的 onerror 回调

    <script type="text/javascript">
    
    (function() {
        var errorCallback = window.onerror;
        window.onerror = function () {
            // handle error condition
            errorCallback && errorCallback.apply(this, arguments);
        };
    })();
    
    </script>
    

    【讨论】:

      【解决方案9】:

      你通过给 window.onerror 分配一个函数来监听 onerror 事件:

       window.onerror = function (msg, url, lineNo, columnNo, error) {
              var string = msg.toLowerCase();
              var substring = "script error";
              if (string.indexOf(substring) > -1){
                  alert('Script Error: See Browser Console for Detail');
              } else {
                  alert(msg, url, lineNo, columnNo, error);
              }   
            return false; 
        };
      

      【讨论】:

        【解决方案10】:

        如果您想要统一的方式来处理未捕获的错误和未处理的承诺拒绝,您可以查看uncaught library

        编辑

        <script type="text/javascript" src=".../uncaught/lib/index.js"></script>
        
        <script type="text/javascript">
            uncaught.start();
            uncaught.addListener(function (error) {
                console.log('Uncaught error or rejection: ', error.message);
            });
        </script>
        

        除了 window.onerror 之外,它还监听 window.unhandledrejection

        【讨论】:

        • Lance Pollard 在the answer above 中提到了一些错误,正常的 onerror 无法处理。你图书馆处理它们吗?如果有,请说明是哪一个?
        • @MiFreidgeimSO-stopbeingevil 刚刚检查了源代码 - 不,不幸的是它没有。 Aleksandr Oleynikov:如果你能支持这一点,那就太棒了 - 我会把我的反对票变成赞成票;-)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-23
        • 1970-01-01
        • 1970-01-01
        • 2021-07-05
        • 2011-09-01
        • 1970-01-01
        相关资源
        最近更新 更多