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

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

Ответ 1

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

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

xxx();
</script>

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

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

Mimic Window. onerror в Opera с помощью javascript

Ответ 2

Как поймать необработанные ошибки 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 Event Fire?

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

неперехваченные исключения

  • бросить "некоторые сообщения"
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document;, исключение безопасности

ошибка компиляции

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);, он попытается скомпилировать первый аргумент как a script

Браузеры, поддерживающие window.onerror

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

Снимок экрана:

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

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

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

JSFiddle:

https://jsfiddle.net/nzfvm44d/

Литература:

Ответ 3

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

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

var appIsHandlingError = false;

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

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

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

Ответ 4

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

https://www.atatus.com/

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

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

Современные Chrome и Opera полностью поддерживают спецификацию HTML 5 для ErrorEvent и window.onerror. В обоих этих браузерах вы можете использовать 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 все еще существуют, и мы также должны их поддерживать. Поскольку stacktrace недоступен в window.onerror, нам нужно немного поработать.

Оказывается, единственное, что мы можем сделать, чтобы получить stacktraces от ошибок, - это обернуть весь наш код в блок 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/.

Ответ 5

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

  1. <img> ошибки загрузки (ответ> = 400).
  2. <script> ошибки загрузки (ответ> = 400).
  3. глобальные ошибки, если у вас есть много других библиотек в вашем приложении, которые также управляют window.onerror неизвестным образом (jquery, angular и т.д.).
  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://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 объектов ошибки, поэтому вы можете проверить их в веб-консоли после их появления, например:

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

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

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

Ответ 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;

Ответ 7

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

<script type="text/javascript">

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

</script>

Ответ 8

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

Это регистрация ошибок как службы.

Это удивительно просто настроить. Просто добавьте один <script> линии на каждую страницу и что это. Это также означает, что это будет удивительно просто удалить, если вы решите, что вам это не нравится.

Существуют и другие сервисы, такие как Sentry (который является открытым исходным кодом, если вы можете разместить свой собственный сервер), но он не делайте то, что делает Trackjs. Trackjs регистрирует взаимодействие пользователя между браузером и веб-сервером, чтобы вы могли фактически отслеживать шаги пользователя, которые привели к ошибке, а не только ссылку на номер файла и строки (и, возможно, трассировку стека).

Ответ 9

Если вам нужен унифицированный способ обработки как неперехваченных ошибок, так и необработанных обещаний, вы можете взглянуть на 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>

Он прослушивает окно unhandledrejection в дополнение к window.onerror.

Ответ 10

Вы слушаете событие onerror, назначая функцию window.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; 
  };

Ответ 11

У меня были случаи в коде Javascript, когда я сталкивался с исключением, и в этой функции не было оператора try/catch. Затем, кажется, существует неявная обработка исключений, которая ищет другие обработчики try/catch в монолите производственного кода и начинает выполнять, по-видимому, первый такой обработчик, который "находит". Это может создать ложное представление о том, что в этом компоненте произошла ошибка, хотя в действительности она произошла в куске кода, в котором отсутствовала обработка исключений. Есть ли какая-либо документация по этому механизму, которая в конечном итоге запускает какой-то полусвязанный оператор catch? Я хотел бы понять, как это работает; Как мой код может начать выполнение оператора catch, расположенного вне блока кода, где произошла ошибка?