JavaScript 글로벌 이벤트 메커니즘


350

모든 정의되지 않은 함수 오류가 발생하고 싶습니다. JavaScript에 전역 오류 처리 기능이 있습니까? 유스 케이스가 정의되지 않은 플래시에서 함수 호출을 포착하고 있습니다.


일단 잡으면 오류로 무엇을하고 싶습니까? 누락 된 함수를 만들 수 있도록 로그를 작성해야합니까, 아니면 예외가 코드를 위반하지 않도록 하시겠습니까?
Dan Herbert

2
누락 된 함수의 이름을 호출하고 일부 문자열 호출의 존재에 따라 내 함수를 얻고 싶습니다. 문자열이 'close'인 함수를 호출하면 예를 들어 close ()가 호출됩니다. 또한 그 시점에서 오류를 포착하고 싶습니다.

2
exceptionsjs.com 은이 기능을 제공하며 "guard"기능으로 정의되지 않은 기능과 관련된 오류 만 포착 할 수 있습니다.
Steven Wexler

답변:


192

도움이 되나요?

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

xxx();
</script>

그래도 플래시 오류를 어떻게 처리하는지 잘 모르겠습니다 ...

업데이트 : Opera에서는 작동하지 않지만 지금은 잠자리를 해킹하여 얻을 수있는 것을 확인합니다. 잠자리 해킹에 대한 제안은이 질문에서 나왔습니다.

모방 창. 자바 스크립트를 사용하는 Opera의 오류


3
msg, file_loc, line_no params를 추가하면 나를 위해해야합니다. 감사!

1
이 코드가 기존 오류 처리기를 재정의한다는 것을 올바르게 이해하고 있습니까?
화성 로버트슨

@MarsRobertson No.
atilkan

@atilkan window.onerror = function() { alert(42) };지금 대답의 코드는 : window.onerror = function() { alert("Error caught"); };난 아직 확실 해요, 오버라이드 (override)하지 ..
화성 로버트슨에게

@MarsRobertson 이해합니다. 아마도 덮어 씁니다. 예, 테스트를 마쳤습니다. addEventListener를 사용하는 것이 좋습니다.
atilkan

646

처리되지 않은 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를 지원하는 브라우저

  • 크롬 13+
  • Firefox 6.0 이상
  • Internet Explorer 5.5 이상
  • 오페라 11.60+
  • 사파리 5.1 이상

스크린 샷 :

이것을 테스트 페이지에 추가 한 후 실행되는 위의 오류 코드 예제 :

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

window.onerror 이벤트에 의해 자세한 오류 정보를 표시하는 Javascript 경고

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/

참고 문헌 :


3
파이어 폭스는 throw수동으로 만들 때 오류 메시지를 표시하지 않습니다 . stackoverflow.com/questions/15036165/…
Sebas

2
좋은 대답입니다. JSNLog와 같은 패키지로 "아약스를 통해이 오류보고"를 구현하면 아약스 및 서버 측 로깅이 수행됩니다.
user1147862 10

4
이 답변 외에도 스택 추적을 얻을 수 있도록 err 객체를 추가했습니다. stackoverflow.com/a/20972210/511438 . 이제 개발자 오류가 페이지 상단에 상자로 표시되므로 더 많은 피드백으로 개발할 수 있습니다 (생성 한대로).
Valamas

onerror ()가 더 많은 오류를 발생시키지 않도록 try-catch (ignore) 블록에 onerror 구현을 포장하는 것이 낫지 않습니까? (여기서는 사실이 아니라, 확실합니다)
Pieter De Bie

1
@ super1ha1 jsfiddle을 제공 할 수 있습니까? 브라우저가 onerror 이벤트 핸들러가 이벤트 발생을 멈추는 주요 변경 사항을 도입하지는 않을 것이라고 생각합니다. 이 jsfiddle을 사용하여 onerror 핸들러가 작동하는지 확인할 수 있습니다. jsfiddle.net/nzfvm44d Chrome 버전 62.0.3202.94 (공식 빌드) (64 비트)에서 여전히 작동합니다.
Sam

38

정교한 오류 처리

오류 처리가 매우 정교하여 오류 자체가 발생할 수있는 경우 이미 "errorHandling-Mode"에 있는지 여부를 나타내는 플래그를 추가하면 유용합니다. 이렇게 :

var appIsHandlingError = false;

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

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

그렇지 않으면 무한 루프에서 자신을 찾을 수 있습니다.


29
또는 더 안전한 방법은 handleError메서드 주위에 try-catch를 사용하는 것입니다.
Aidiakapi

8
오류 처리에 비동기 호출이 있으면 try catch를 사용할 수 없습니다. 그래서 그는 솔루션을 전자 솔루션으로 유지
Emrys Myrooin

@EmrysMyrooin 여전히 오류 처리로 인해 동기화 또는 비동기 루프가 발생하고 사용 가능한 스택 정보가 없으면 충돌이 발생합니다.
inf3rno

1
플래그가 어느 시점에서 재설정되어야한다고 생각합니까? 큰 경우 if / catch가 필요하고 onerror 메소드를 떠나기 전에 플래그가 재설정되었는지 확인하십시오. 그렇지 않으면 하나의 오류 만 처리됩니다.
Seb

23

최신 웹 앱에 고급 오류 추적 및 실제 사용자 모니터링을 제공하는 Atatus 를 사용해보십시오 .

https://www.atatus.com/

모든 브라우저에서 합리적으로 완전한 스택 추적을 얻는 방법을 설명하겠습니다.

JavaScript에서 오류 처리

최신 Chrome 및 Opera는 ErrorEvent 및에 대한 HTML 5 초안 사양을 완벽하게 지원합니다 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는 여전히 사용 중이며 우리도이를 지원해야합니다. 스택 트레이스를 사용할 수 없으므로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/jserror 는 무엇입니까 ? REST, 웹 서비스, Wcf 서비스 ? 백엔드에 대한 간단한?
Kiquenet

사용자 브라우저에서 서버로 js 오류를 보내려면 http://yourserver.com받고 저장하려면 백엔드 ( ) 를 작성해야합니다 . atatus.com 을 선택 하면 아무것도 할 필요가 없습니다. 페이지에 두 줄의 스크립트를 포함하십시오.
Fizer Khan

18

window.onerror가능한 모든 오류에 대한 액세스를 제공하지 않는 것 같습니다 . 특히 다음을 무시합니다.

  1. <img> 로딩 오류 (응답> = 400).
  2. <script> 로딩 오류 (응답> = 400).
  3. 앱에 많은 다른 라이브러리가 있으면 전역 오류가 발생합니다. window.onerror 알 수없는 방식으로 하는 (jquery, angular 등).
  4. 아마도 이것을 탐색 한 후에도 많은 사례가 발생했을 것입니다 (iframe, stack overflow 등).

다음은 이러한 많은 오류를 포착하는 스크립트의 시작입니다. 따라서 개발 중에 앱에보다 강력한 디버깅을 추가 할 수 있습니다.

(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와 같은 서비스에 로그를 전송하여 개발 중에 오류를 측정하고 준비 중 오류를 측정 할 수 있도록하는 것이 유용했습니다. 더 깊은 수준. 그런 다음 프로덕션에서 끌 수 있습니다.

도움이 되었기를 바랍니다.


1
분명히 이미지가 오류 이벤트를 트리거합니다. stackoverflow.com/a/18152753/607033
inf3rno

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

이전에 연결된 onerror 콜백도 보존해야합니다.

<script type="text/javascript">

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

</script>

3

잡히지 않은 오류와 처리되지 않은 약속 거부를 모두 처리하는 통일 된 방법을 원한다면 잡히지 않은 라이브러리를 살펴보십시오 .

편집하다

<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 이외에 처리되지 않은 거부.


2
랜스 폴라드 (Lance Pollard) 는 위의 답변 에서 정상적인 오류가 처리하지 못하는 몇 가지 오류를 언급했습니다. 도서관에서 처리합니까? 그들 중 일부는 어떤 것을 지정하십시오?
마이클 Freidgeim

@ MiFreidgeimSO-stopbeingevil 방금 소스 코드를 확인했습니다. 불행히도 그렇지 않습니다. Aleksandr Oleynikov : 당신이 이것을 지원할 수 있다면 굉장 할 것입니다 – 나는 나의 downvote를 upvote로 바꿀 것입니다 ;-)
brillout

2

Trackjs를 제공하는 것이 좋습니다 를 사용해 .

서비스로 로깅 오류입니다.

설정이 매우 간단합니다. 각 페이지에 하나의 <script> 줄을 추가하면됩니다. 또한 마음에 들지 않으면 간단하게 제거 할 수 있습니다.

Sentry ( 다른 서버를 호스팅 할 수있는 경우 오픈 소스) 와 같은 다른 서비스 가 있지만 Trackjs의 기능은 수행하지 않습니다. Trackjs는 브라우저와 웹 서버 간의 사용자 상호 작용을 기록하므로 파일 및 줄 번호 참조 (및 스택 추적)와 달리 실제로 오류를 일으킨 사용자 단계를 추적 할 수 있습니다.


2
무료 평가판이 있지만 TrackJS 에는 더 이상 프리 티어가없는 것 같습니다 .
pkaeding

이것은 오류를 추적하고 경고하는 데는 좋지만 처리 부분을 실제로 해결하지는 않습니다. 이상적으로는 asker가 나머지 코드를 계속 실행할 수 있도록 이러한 오류를 처리하는 방법을 찾고 있다고 생각합니다.
wgp

오류 이벤트를 포착하고 스택 추적 및 애플리케이션 상태로 로거에 xhr 호출을 추가하면 어떻게됩니까? trackjs가 더 나은가요?
inf3rno

2
@ inf3rno 스택 추적 이상이며 오류의 시작 부분 만 추적합니다. TrackJS는 전체 사용자 세션을 추적하여 세션의 원인을 확인할 수 있습니다. 다음은 스크린 샷입니다. trackjs.com/assets/images/screenshot.png
kane

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; 
  };
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.