JavaScript全局错误处理

时间:2009-06-04 16:53:06

标签: javascript javascript-events

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

10 个答案:

答案 0 :(得分:611)

如何捕获未处理的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 ;,安全例外
  •   
     

编译错误

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

支持window.onerror的浏览器

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

截图:

将以上代码添加到测试页后的上述错误代码示例:

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

Javascript alert showing error information detailed by the window.onerror event

的jsfiddle:

https://jsfiddle.net/nzfvm44d/

参考文献:

答案 1 :(得分:180)

这对您有帮助吗?

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

xxx();
</script>

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

更新:它在Opera中不起作用,但我现在正在攻击Dragonfly以查看它的内容。关于黑客攻击蜻蜓的建议来自这个问题:

Mimic Window. onerror in Opera using javascript

答案 2 :(得分:35)

复杂的错误处理

如果您的错误处理非常复杂,因此可能会抛出错误,那么添加一个标志表明您是否已经处于“errorHandling-Mode”状态是很有用的。像这样:

var appIsHandlingError = false;

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

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

否则你会发现自己陷入了无限循环。

答案 3 :(得分:21)

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

  

https://www.atatus.com/

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

JavaScript中的错误处理

Modern 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/的网络开发人员。

答案 4 :(得分:16)

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

  1. <img>加载错误(响应&gt; = 400)。
  2. <script>加载错误(响应&gt; = 400)。
  3. 如果您的应用中有许多其他库也以未知方式操作window.onerror(jquery,angular等),则会出现全局错误。
  4. 可能很多情况下我现在探讨之后还没有遇到过(iframe,堆栈溢出等)。
  5. 以下是捕获许多错误的脚本的开头,以便您可以在开发过程中为应用添加更强大的调试功能。

    (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等服务以便我们可以在开发期间测量错误以及在分段时进行调试以便我们可以调试正在进行的操作更深层次。然后可以在生产中关闭它。

    希望这有帮助。

答案 5 :(得分:6)

// 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;

答案 6 :(得分:6)

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

<script type="text/javascript">

(function() {
    var errorCallback = window.onerror;
    window.onerror = function () {
        // handle error condition
        errorCallback && errorCallback.apply(this, arguments);
    };
})();

</script>

答案 7 :(得分:3)

我建议您尝试Trackjs

将错误记录为服务。

设置起来非常简单。只需添加一个&lt; script&gt;排到每个页面,然后就是这样。这也意味着如果您决定不喜欢它,将会非常简单地删除。

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

答案 8 :(得分:3)

如果您想要统一的方式处理未捕获的错误和未处理的拒绝承诺,您可以查看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之外,它还会监听窗口。unhandledrejection

答案 9 :(得分:1)

通过为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; 
  };