JavaScript глобальная обработка ошибок

Я хотел бы поймать каждую неопределенную ошибку в функции. Есть ли глобальная возможность обработки ошибок в JavaScript? Вариант использования - перехват вызовов функций из флэш-памяти, которые не определены.

10 ответов

Решение

Помогает ли это вам:

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

xxx();
</script>

Я не уверен, как он обрабатывает ошибки Flash, хотя...

Обновление: в Opera это не работает, но я сейчас взламываю Dragonfly, чтобы посмотреть, что получится. Предложение о взломе Dragonfly пришло из этого вопроса:

Мимика Окно. Ошибка в Opera с использованием JavaScript

Как поймать необработанные ошибки 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+
  • Опера 11.60+
  • Safari 5.1+

Скриншот:

Пример приведенного выше кода ошибки в действии после добавления этого на тестовую страницу:

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

Предупреждение Javascript, показывающее информацию об ошибке, детализированную событием window.onerror

JSFiddle:

https://jsfiddle.net/nzfvm44d/

Рекомендации:

сложная обработка ошибок

Если ваша обработка ошибок очень сложна и, следовательно, может выдать саму ошибку, полезно добавить флаг, указывающий, что вы уже находитесь в "errorHandling-Mode". Вот так:

var appIsHandlingError = false;

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

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

В противном случае вы можете оказаться в бесконечном цикле.

Кажется, что window.onerror не предоставляет доступ ко всем возможным ошибкам. В частности, он игнорирует:

  1. <img> ошибки загрузки (ответ>= 400).
  2. <script> ошибки загрузки (ответ>= 400).
  3. глобальные ошибки, если у вас есть много других библиотек в вашем приложении, также манипулирующих window.onerror неизвестным образом (jquery, угловой и т. д.).
  4. вероятно, во многих случаях, с которыми я не сталкивался после изучения этого сейчас (iframes, переполнение стека и т. д.).

Вот начало сценария, который улавливает многие из этих ошибок, так что вы можете добавить более надежную отладку в ваше приложение во время разработки.

(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://stackru.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://stackru.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 объектов ошибок, поэтому вы можете просмотреть их в веб-консоли после того, как они произошли, например:

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

Примечание. Это работает путем переопределения методов в нескольких браузерах / собственных конструкторах. Это может иметь непредвиденные побочные эффекты. Тем не менее, это было полезно использовать во время разработки, чтобы выяснить, где происходят ошибки, для отправки журналов в сервисы, такие как NewRelic или Sentry, во время разработки, чтобы мы могли измерять ошибки во время разработки и при подготовке, чтобы мы могли отлаживать то, что происходит в более глубокий уровень. Затем он может быть отключен в производстве.

Надеюсь это поможет.

Попробуйте Atatus, который обеспечивает расширенное отслеживание ошибок и мониторинг реальных пользователей для современных веб-приложений.

https://www.atatus.com/

Позвольте мне объяснить, как получить трассировки стека, которые достаточно полны во всех браузерах.

Обработка ошибок в JavaScript

Современные Chrome и Opera полностью поддерживают проект спецификации HTML 5 для ErrorEvent и window.onerror, В обоих этих браузерах вы можете использовать window.onerrorили правильно связать с событием error:

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

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

Следует также сохранить ранее связанный обратный вызов onerror

<script type="text/javascript">

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

</script>

Если вам нужен унифицированный способ обработки как необработанных ошибок, так и необработанных отклонений обещаний, вы можете взглянуть на необработанную библиотеку.

РЕДАКТИРОВАТЬ

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

Я бы порекомендовал дать Trackjs попробовать.

Это регистрация ошибок как сервис.

Это удивительно просто настроить. Просто добавьте одну строку