JavaScript global error handling

I want to catch every undefined function error thrown. Is there a global error handling tool in JavaScript? The use case is to capture undefined flash function calls.

#1 building

How to catch unhandled Javascript errors

Assign the window.onerror event to the event handler, such as:

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

As mentioned in the code, if the return value of window.onerror is true, the browser should disable the display of the warning dialog box.

When did the fire break out in window.onerror?

In short, this event is raised when 1.) an uncaught exception exists or 2.) a compile time error occurs.

No exceptions to arrest

  • Throw "some messages"
  • call_something_undefined();
  • Cross? Origin? Iframe. Contentwindow. Document;, a security exception

Compile error

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • SetTimeout ('{', 10);, which will attempt to compile the first parameter into a script

Browser supports window.onerror

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

Screenshots:

After adding this to the test page, the above onerror code example:

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

jsfiddle:

https://jsfiddle.net/nzfvm44d/

reference:

#2 building

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

#3 building

I suggest trying Trackjs .

Error logging as service.

It's very simple to set up. Just add a < script > line to each page. It also means that if you decide not to like it, it will be very simple.

And something like that. Sentry Other services like this (open source if you can host your own server), but it won't do what trackjs does. Trackjs records the user's interaction between the browser and your web server, so you can actually trace the user steps that caused the error, not just the file and line number references (possibly stack traces).

#4 building

Try to use Atatus Provides advanced error tracking and real user monitoring for modern Web applications.

https://www.atatus.com/

Let me explain how to get a fairly complete stack trace in all browsers.

Error handling in JavaScript

Modern Chrome and Opera fully support the HTML 5 draft specification for erroreevent and window.onerror. In these two browsers, you can use window.onerror or bind to the 'error' event correctly:

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

Unfortunately Firefox, Safari and IE still exist, and we have to support them. Because stack trace is not available in window.onerror, we have to do more.

As it turns out, the only way we can get e.stack from the error is to wrap all the code in the try {} catch (E) {} block, and then look at e.stack. We can make this process easier with a function called wrap, which takes a function and returns a new function with good error handling.

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

It works. Any function that you manually wrap will have good error handling, but it turns out that we can actually do it for you automatically in most cases.

By changing the global definition of the addEventListener so that it automatically wraps the callback, we can automatically insert try {} catch (E) {} around most code. This allows existing code to continue to work, but adds high-quality exception tracking.

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

We also need to make sure that the removeEventListener continues to work. At present, it will not be changed because of the parameters of addEventListener. Once again, we just need to fix it on the prototype object:

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

Transfer error data to back end

You can use image tags to send error data as follows

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

Disclaimer: I am https://www.atatus.com/ Web developers.

#5 building

It seems that window.onerror does not provide access to all possible errors. Specifically, it ignores:

  1. < img > load error (response > = 400).
  2. < script > load error (response > = 400).
  3. Global error if there are many other libraries in your application that operate window.onerror in unknown ways.
  4. In many cases, I haven't encountered it since I explored it (iframe, stack overflow, etc.).

Here is the beginning of a script that captures many errors so that you can add more powerful debugging capabilities to your application during development.

(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);
}

})();

It can be used like this:

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

The full script has a default implementation that attempts to print out a semi readable "display" version of the entity / error it receives. Inspiration for application specific error handlers. The default implementation also retains references to the last 100 bad entities, so you can check them after the Web console occurs, such as:

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

Note: this can be done by overriding several browser / native constructors. This can have unexpected side effects. However, it is used during development to determine where the error occurred, and during development to send logs to services such as NewRelic or Sentry so that we can measure the error during development and debug during segmentation so that we can debug the operation in progress at a deeper level. You can then turn it off in production.

I hope this can help.

Keywords: Javascript Firefox network Web Server

Added by Lassie on Thu, 05 Mar 2020 12:09:47 +0200