JavaScript global etkinlik mekanizması


351

Atılan her tanımsız fonksiyon hatasını yakalamak istiyorum. JavaScript'te genel bir hata işleme tesisi var mı? Kullanım durumu, tanımlanmamış flaştan işlev çağrılarını yakalamaktır.


Yakaladıktan sonra bir hatayla ne yapmak istiyorsunuz? Kayıp işlevi oluşturabilmeniz için oturum açmanız mı gerekiyor yoksa istisnaların kodunuzu kırmasını durdurmak mı istiyorsunuz?
Dan Herbert

2
Ben denilen ve bazı dize çağrı benim işlevine göre eksik fonksiyonun adını almak istiyorum. 'Close' dizesiyle bir işleve yapılan herhangi bir çağrı, örneğin close () yöntemini çağırır. Ben de bu noktada hatayı yakalamak istiyorum.

2
exceptionsjs.com bu işlevselliği sağlar ve yalnızca "koruma" işleviyle tanımlanmamış işlevsellik ile ilgili hataları yakalamak için kullanılabilir.
Steven Wexler

Yanıtlar:


193

Bu size yardımcı olur mu?

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

xxx();
</script>

Flash hatalarını nasıl işlediğinden emin değilim ...

Güncelleme: Opera'da çalışmıyor, ama şimdi ne olduğunu görmek için Dragonfly'yi hack ediyorum. Dragonfly'i hackleme önerisi bu sorudan geldi:

Mimik Pencere. javascript kullanarak Opera'da onerror


3
Msg, file_loc, line_no parametrelerini ekleyerek bunu benim için yapmalı. Teşekkürler!

1
Bu kodun varolan hata işleyicilerini geçersiz kıldığını doğru anladım mı?
Mars Robertson

@MarsRobertson sayılı
atılkan

@atilkan window.onerror = function() { alert(42) };şimdi cevap kodu: window.onerror = function() { alert("Error caught"); };geçersiz değil, hala emin değilim ..
Mars Robertson

@MarsRobertson anlıyorum. Muhtemelen üzerine yazar. Evet, sadece test edildi. AddEventListener kullanmak daha iyi olur.
atilkan

647

İşlenmemiş Javascript Hataları Nasıl Yakalanır

window.onerrorEtkinliği aşağıdaki gibi bir olay işleyicisine atayın :

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

Dönüş değeri ise, kodda yorumladığı gibi window.onerrorolduğunu trueardından tarayıcı bir uyarı iletişim kutusu göstermeden bastırmak gerekir.

Window.onerror Olayı ne zaman tetiklenir?

Özetle, olay 1) yakalanmamış bir istisna olduğunda veya 2.) bir derleme zamanı hatası oluştuğunda ortaya çıkar.

yakalanmamış istisnalar

  • "bazı mesajlar" atın
  • ) (Call_something_undefined;
  • cross_origin_iframe.contentWindow.document; bir güvenlik istisnası

Derleme hatası

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);, ilk argümanı bir komut dosyası olarak derlemeye çalışacaktır

Window.onerror destekleyen tarayıcılar

  • Chrome 13+
  • Firefox 6.0 ve sonraki sürümleri
  • Internet Explorer 5.5 ve sonraki sürümleri
  • Opera 11.60+
  • Safari 5.1 ve üzeri

Ekran görüntüsü:

Bunu bir test sayfasına ekledikten sonra yukarıdaki eylem kodu örneği:

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

Window.onerror olayı tarafından ayrıntılı olarak açıklanan hata bilgilerini gösteren Javascript uyarısı

AJAX hata raporlaması örneği

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/

Referanslar:


3
firefox, throwmanuel olarak yapıldığında hata mesajını geri vermez . stackoverflow.com/questions/15036165/…
Sebas

2
Mükemmel cevap. "Bu hatayı ajax ile bildir" seçeneğini, sizin için ajax ve sunucu tarafı günlük kaydını yapan JSNLog gibi bir paketle uygulayabilirsiniz.
user1147862

4
Bu yanıta ek olarak, yığın izlemesini almak için err nesnesini ekledim. stackoverflow.com/a/20972210/511438 . Artık geliştirici hatalarım sayfanın üst kısmında bir kutu olarak göründüğü için (daha önce oluşturduğum gibi) daha fazla geri bildirim ile gelişebilirim.
Valamas

Hata uygulamanızı bir try-catch (yoksayma) bloğunda paketlemek, hata () 'nın daha fazla hata atmasını önlemek için daha iyi olmaz mı? (durum bu değil, ama sadece emin olmak için)
Pieter De Bie

1
@ super1ha1 Bir jsfiddle sağlayabilir misiniz? Tarayıcıların, olay olayı işleyicisinin olayları tetiklemeyi durdurduğu bir değişiklik getireceğini düşünmüyorum. Hata işleyicinin çalıştığını görmek için bu jsfiddle'ı deneyebilirsiniz: jsfiddle.net/nzfvm44d Bu benim için hala Chrome 62.0.3202.94 (Resmi Derleme) (64 bit) sürümünde çalışıyor.
Sam

39

gelişmiş hata yönetimi

Hata işlemeniz çok karmaşıksa ve bu nedenle bir hata kendisi atabiliyorsa, zaten "errorHandling-Mode" modunda olup olmadığınızı belirten bir işaret eklemek yararlıdır. Şöyle ki:

var appIsHandlingError = false;

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

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

Aksi takdirde kendinizi sonsuz bir döngüde bulabilirsiniz.


29
Ya da daha güvenli bir handleErroryöntem yöntemde try-catch kullanmak olacaktır .
Aidiakapi

8
Hata işlemede senkronize olmayan bir çağrınız varsa try catch'i kullanamazsınız. He çözümü Yani e iyi bir çözüm kalır
Emrys Myrooin

@EmrysMyrooin Yine de hata işleme ile senkronizasyona veya zaman uyumsuz döngüye neden olacak ve muhtemelen kullanılabilir yığın bilgisi olmadan çökecektir.
inf3rno

1
Bayrağın bir noktada sıfırlanması gerektiğini düşünüyorum, değil mi? Eğer büyükse denemeye / yakalamaya ihtiyacımız olduğuna inanıyorum ve onerror yönteminden ayrılmadan önce bayrağın sıfırlandığından emin olun. Aksi takdirde, yalnızca bir hata ele alınacaktır.
Seb

23

Modern web uygulamaları için Gelişmiş Hata İzleme ve Gerçek Kullanıcı İzleme sağlayan Atatus'u deneyin .

https://www.atatus.com/

Tüm tarayıcılarda makul şekilde tamamlanmış yığın izlemelerini nasıl alacağımı açıklayayım.

JavaScript'te hata işleme

Modern Chrome ve Opera, ErrorEvent ve için HTML 5 taslak spesifikasyonunu tam olarak desteklemektedir window.onerror. Bu tarayıcıların her ikisinde window.onerrorde 'error' olayını düzgün kullanabilir veya bağlayabilirsiniz:

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

Ne yazık ki Firefox, Safari ve IE hala var ve onları da desteklemeliyiz. Yığın izinin mevcut olmadığı içinwindow.onerror biraz daha fazla iş yapmalıyız.

Hatalardan yığın izleri almak için yapabileceğimiz tek şey, tüm kodumuzu bir try{ }catch(e){ }blokta sarmak ve sonra bakmaktır e.stack. Fonksiyonu alan ve iyi hata işleme ile yeni bir fonksiyon döndüren wrap adı verilen bir fonksiyonla süreci biraz daha kolaylaştırabiliriz.

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

Bu çalışıyor. Elle sardığınız herhangi bir işlevin iyi bir hata yönetimi olacaktır, ancak çoğu durumda bunu sizin için otomatik olarak yapabileceğimiz ortaya çıkıyor.

Genel tanımını addEventListenergeri aramayı otomatik olarak satacak şekilde değiştirerek try{ }catch(e){ }çoğu kodu otomatik olarak ekleyebiliriz . Bu, mevcut kodun çalışmaya devam etmesini sağlar, ancak yüksek kaliteli istisna izleme ekler.

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

Bunun removeEventListenerçalışmaya devam etmesini de sağlamalıyız . Şu anda olmayacak çünkü argümanı addEventListenerdeğişti. Yine bunu sadece prototypenesne üzerinde düzeltmemiz gerekiyor :

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

Hata verilerini arka ucunuza iletme

Aşağıdaki gibi resim etiketini kullanarak hata verileri gönderebilirsiniz

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

Yasal Uyarı: Ben https://www.atatus.com/ adresinde bir web geliştiricisiyim .


Nedir yourserver.com/jserror ? REST, Web Hizmeti, Wcf Hizmeti ? Arka uç hakkında basit mi?
Kiquenet

Kullanıcı tarayıcısından sunucunuza js hataları göndermek istiyorsanız. http://yourserver.comalmak ve saklamak için arka ucunuzu ( ) yazmanız gerekir . Atatus.com'u seçerseniz , hiçbir şey yapmanıza gerek yoktur. Sayfanıza iki komut satırı ekleyin.
Fizer Khan

18

window.onerrorTüm olası hatalara erişim sağlamadığı anlaşılıyor . Özellikle:

  1. <img> yükleme hataları (yanıt> = 400).
  2. <script> yükleme hataları (yanıt> = 400).
  3. uygulamanızda başka birçok kitaplık varsa genel hatalar window.onerror bilinmeyen bir şekilde (jquery, açısal, vb.) işlem yapan .
  4. Muhtemelen bunu şimdi araştırdıktan sonra karşılaşmadığım birçok vaka (iframe'ler, yığın taşması, vb.).

İşte bu hataların çoğunu yakalayan bir komut dosyası başlıyor, böylece geliştirme sırasında uygulamanıza daha sağlam hata ayıklama ekleyebilirsiniz.

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

})();

Bu şekilde kullanılabilir:

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

Komut dosyasının tamamı, aldığı varlığın / hatanın yarı okunabilir "görüntüleme" sürümünü yazdırmaya çalışan varsayılan bir uygulamaya sahiptir. Uygulamaya özgü bir hata gidericiden ilham almak için kullanılabilir. Varsayılan uygulama ayrıca son 100 hata varlığına bir referans tutar, böylece bunları aşağıdaki gibi gerçekleştikten sonra web konsolunda inceleyebilirsiniz:

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

Not: Bu, birkaç tarayıcı / yerel kurucudaki yöntemleri geçersiz kılarak çalışır. Bunun istenmeyen yan etkileri olabilir. Bununla birlikte, geliştirme sırasında kullanmak, hataların nerede oluştuğunu bulmak, geliştirme sırasında NewRelic veya Sentry gibi hizmetlere günlükler göndermek, geliştirme sırasında hataları ölçebilmemiz ve aşamalandırma sırasında neler olup bittiğini ayıklayabilmemiz için yararlı olmuştur. daha derin bir seviye. Daha sonra üretimde kapatılabilir.

Bu yardımcı olur umarım.


1
Görünüşe göre resimler hata olayını tetikliyor: 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

Daha önce ilişkilendirilmiş hata geri çağrısı da korunmalıdır

<script type="text/javascript">

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

</script>

3

Hem yakalanmamış hataları hem de işlenmemiş söz reddini ele almak için birleşik bir yol istiyorsanız, yakalanmamış kütüphaneye bir göz atabilirsiniz .

DÜZENLE

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

Pencereyi dinler. window.onerror'a ek olarak işlenmemiş reddetme.


2
Yukarıdaki cevapta Lance Pollard birkaç hatadan bahsetti, normal hata işlemiyor. Kütüphane bunları ele alıyor mu? Bunlardan bazıları, lütfen hangisini belirtin?
Michael Freidgeim

@ MiFreidgeimSO-stopbeingevil Kaynak kodunu kontrol ettim - ne yazık ki hayır. Aleksandr Oleynikov: Eğer bunu destekleyebilirseniz harika olurdu - Sonra benim downvote upvote ;-)
brillout

2

Vermeyi tavsiye ederim Trackjs denemenizi .

Hizmet olarak günlüğe kaydetme hatası.

Kurulumu inanılmaz derecede basit. Her sayfaya bir <script> satırı ekleyin ve işte bu kadar. Bu, beğenmediğinize karar verirseniz kaldırmanın inanılmaz derecede basit olacağı anlamına gelir.

Sentry (kendi sunucunuzu barındırabiliyorsanız açık kaynak kodlu) gibi başka hizmetler de vardır , ancak Trackjs'in yaptığı şeyi yapmaz. Trackjs, kullanıcının tarayıcısı ve web sunucunuz arasındaki etkileşimini kaydeder, böylece yalnızca bir dosya ve satır numarası referansının (ve belki de yığın izinin) aksine, hataya neden olan kullanıcı adımlarını gerçekten izleyebilirsiniz.


2
TrackJS'nin artık ücretsiz bir seviyesi yok gibi görünüyor , ancak ücretsiz bir deneme var.
15'te

Bu, izleme ve hatalara karşı uyarı için iyidir, ancak işleme bölümünü gerçekten çözmez. İdeal olarak asker kodun geri kalanı hala çalışır böylece bu hataları işlemek için bir yol arıyor düşünüyorum.
wgp

Hata olayını yakalar ve yığın izlemesi ve uygulama durumu ile günlükçüye bir xhr çağrısı eklerseniz ne olur? Trackjs nasıl daha iyi?
inf3rno

2
@ inf3rno Hatanın başlangıcını izleyen bir yığın izlemesinden daha fazlası. TrackJS, kullanıcı oturumunun tamamını izler, böylece neyin yol açtığını görebilirsiniz. örnek olarak ekran görüntüsü trackjs.com/assets/images/screenshot.png
kane

1

Onerror olayını window.onerror öğesine bir işlev atayarak dinlersiniz:

 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; 
  };
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.