Obsługa wyjątków JavaScript

82

Jaka jest najlepsza technika wychwytywania WSZYSTKICH wyjątków generowanych w JavaScript?

Oczywiście najlepszą techniką jest użycie try ... catch. Ale w przypadku oddzwaniania asynchronicznego i tak dalej może to być trudne.

Wiem, że przeglądarki IE i Gecko obsługują window.onerror, ale co z Operą i Safari?

Oto kilka przypadków testowych, dla których chciałbym mieć centralne rozwiązanie do obsługi wyjątków:

// ErrorHandler-Test1
var test = null;
test.arg = 5;
// ErrorHandler-Test2
throw (new Error("Hello"));
// ErrorHandler-Test3
throw "Hello again";
// ErrorHandler-Test4
throw {
    myMessage: "stuff",
    customProperty: 5,
    anArray: [1, 2, 3]
};
// ErrorHandler-Test5
try {
    var test2 = null;
    test2.arg = 5;
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test6
try {
    throw (new Error("Goodbye"));
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test7
try {
    throw "Goodbye again";
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test8
try {
    throw {
        myMessage: "stuff",
        customProperty: 5,
        anArray: [1, 2, 3]
    };
} catch(e) {
    ErrorHandler.handleError(e);
}

Jeśli myślisz o innych przypadkach testowych, wspomnij o nich. Kilka z tych przypadków wspomina o metodzie ErrorHandler.handleError. To tylko sugerowana wskazówka podczas korzystania z try ... catch.

harley.333
źródło
Tylko dlatego, że jest jasne, że nie zauważyłeś komentarzy do mojej odpowiedzi: wygląda na to, że zagłosowałeś w dół za „stwierdzone w pytaniu”, kiedy brzmi: „ale co z Operą i Safari?” Odpowiedziałem konkretnie na postawione tutaj pytanie. WTF?
powiek
1
W WebKit window.onerrorzaginięcie jest problemem od 2003 roku , ale wygląda na to, że w końcu został rozwiązany . Oczywiście Opera pozostanie uparta.
josh3736
FYI window.onerror działa dobrze w - Firefox 7.0.1 - IE 8 - Chrome 16 - Opera 11.60
Vitaliy Kaplich
11
Nie wiem, dlaczego to zostało zamknięte. Zagłosowano na ponowne otwarcie.
ripper234
1
Zgoda, to przydatne pytanie. Jedyne, o czym przychodzi mi do głowy, to to, że CasperOne obawiał się, że zamiast tego stanie się to czymś w rodzaju dyskusji o wojnie w przeglądarce?
Jordan Reiter

Odpowiedzi:

23

Jeśli używasz biblioteki takiej jak jQuery do przypisywania wszystkich programów obsługi zdarzeń, możesz użyć kombinacji window.onerrori zawinąć kod modułu obsługi zdarzeń jQuery i gotową funkcję funkcją obsługi błędów (zobacz: Śledzenie błędów JavaScript: Dlaczego window.onerror Is Not Enough ).

  • window.onerror: przechwytuje wszystkie błędy w IE (i większość błędów w Firefoksie), ale nic nie robi w Safari i Opera.
  • Programy obsługi zdarzeń jQuery: przechwytuje błędy zdarzeń jQuery we wszystkich przeglądarkach.
  • Funkcja gotowa do jQuery: wyłapuje błędy inicjalizacji we wszystkich przeglądarkach.
Karl
źródło
Przeczytałem już artykuł, który nie oferuje żadnego rozwiązania. Nie używam jQuery, ale martwię się tym, co sugerujesz. Chcesz powiedzieć, że jQuery zjada wyjątki ?! A może po prostu zapewniają funkcję logowania (która jest przydatna dopiero po udzieleniu odpowiedzi na moje pierwotne pytanie)?
harley.333
jQuery nie robi nic specjalnego z wyjątkami. Ale jeśli używasz jQuery do dodawania wszystkich swoich zdarzeń, daje to jedno miejsce na dodanie instrukcji try catch i kodu obsługi błędów, jak wyjaśniono w drugim linku w mojej odpowiedzi.
Karl
2
Podany link został przeniesiony: blogs.cozi.com/tech/2008/04/…
natacado
1
Czy to nadal prawda? Właśnie wypróbowałem normalne onerrorpodejście do obsługi w Safari + Chrome i wydaje się, że działa poprawnie.
Juri
Link, który podałeś, dlaczego „to nie wystarczy”, jest dość starą informacją i obecnie nie jest.
vsync,
21

Wydaje się, że WebKit (Safari, Chrome itp.) Obsługuje teraz onerror.

Oryginalny post: O ile wiem, WebKit / Safari nie obsługuje onerrorwydarzenia. Co jest cholernym wstydem.

bez powiek
źródło
19
Nie, nie jest. Zapytany w pytaniu: „ale co z Operą i Safari?”
powiek
Tak jak w przypadku, odpowiedziałem konkretnie na pytanie postawione w pytaniu.
powiek
8
Myślę, że miał na myśli "co [mam zrobić] z Operą i Safari [które nie mają window.onerror]?"
nickf
10
Może @nickf, ale jeśli tak, to jest bardzo źle sformułowany, a moja interpretacja jest w najgorszym razie zrozumiała, aw najlepszym razie najbardziej prawdopodobna. Jest to dość słaba powodem ludzi do głosowania moją odpowiedź w dół, zwłaszcza biorąc pod uwagę, że kwestia nie podać go, a inni czytelnicy mogą korzystać z wyciągu pozytywny lub negatywny.
powiek
Wydaje się, że teraz Chrome go obsługuje.
Daniel X Moore
7

Właściwie podejście jquery nie jest takie złe. Widzieć:

http://docs.jquery.com/Events/error#fn

i:

$(window).error(function(msg, url, line){
  $.post("js_error_log.php", { msg: msg, url: url, line: line });
});
adamfisk
źródło
11
dla każdego, kto znajdzie to teraz - jquery faktycznie zaleca, aby nie wiązać zdarzenia ze zdarzeniem window.error - zobacz link do dokumentacji powyżej. Wyodrębnij: moduł obsługi zdarzeń błędu jQuery nie powinien być dołączony do obiektu okna. [...] Zamiast tego użyj window.onerror.
zcrar70
ofc to nie powinno być związane w ten sposób, to nielogiczne! wiele błędów może się wydarzyć do czasu złożenia tej oferty (jeśli w ogóle się udało ...)
vsync
6

Złap wszystkie wyjątki za pomocą własnej obsługi wyjątków i użyj instanceof.

$("inuput").live({
    click : function (event) {
        try {
            if (somethingGoesWrong) {
                throw new MyException();
            }
        } catch (Exception) {
            new MyExceptionHandler(Exception);
        }

    }
});

function MyExceptionHandler(Exception) {
    if (Exception instanceof TypeError || 
        Exception instanceof ReferenceError || 
        Exception instanceof RangeError ||  
        Exception instanceof SyntaxError ||     
        Exception instanceof URIError ) {
        throw Exception; // native error
     } else {
         // handle exception
     }
}

MyExcetpionHandler zgłosi błąd natywny, ponieważ nie ma bloku try-catch.

Odwiedź http://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/

MKatleast3
źródło
4

try-catchnie zawsze jest najlepszym rozwiązaniem. Na przykład w Chrome 7.0 tracisz ładny ślad stosu w oknie konsoli. Ponowne zgłoszenie wyjątku nie pomaga. Nie znam żadnego rozwiązania, które zachowuje ślady stosu i pozwala reagować na wyjątek.

wolfgang
źródło
2

Przy odrobinie pracy możliwe jest uzyskanie śladów stosu, które są w miarę kompletne we wszystkich przeglądarkach.

Nowoczesne Chrome i Opera (czyli wszystko oparte na silniku renderującym Blink) w pełni obsługują wersję roboczą specyfikacji HTML 5 dla ErrorEvent i window.onerror . W obu tych przeglądarkach możesz window.onerrorpoprawnie użyć lub (zadziwiająco!) Powiązać ze zdarzeniem „błąd”:

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

Niestety Firefox, Safari i IE są nadal dostępne i musimy je również wspierać. Ponieważ śledzenie stosu nie jest dostępne w programie, window.onerrormusimy wykonać trochę więcej pracy.

Okazuje się, że jedyną rzeczą, jaką możemy zrobić, aby uzyskać ślady stosu z błędów, jest zawinięcie całego kodu w plik try{ }catch(e){ } blok, a następnie sprawdzenie pliku e.stack. Możemy nieco uprościć ten proces za pomocą funkcji zwanej wrap, która pobiera funkcję i zwraca nową funkcję z dobrą obsługą błędów.

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
                // sendData(data);
                throw e;
            }
        }
    }
    return func._wrapped;
};

To działa. Każda funkcja, którą zawiniesz ręcznie, będzie miała dobrą obsługę błędów.

Możesz wysyłać dane za pomocą znacznika obrazu w następujący sposób

function sendData(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;
}

Jednak musisz zrobić zaplecze, aby zebrać dane i front-end, aby zwizualizować dane.

W Atatus pracujemy nad rozwiązaniem tego problemu. Atatus to nie tylko śledzenie błędów, ale także monitorowanie rzeczywistych użytkowników.

Spróbuj https://www.atatus.com/

Zastrzeżenie: jestem programistą internetowym w Atatus.

Fizer Khan
źródło
1

Prawdą jest, że w nowoczesnych przeglądarkach podpięcie window.onerror pod kątem błędów, które pojawiają się na samej górze, wraz z dodaniem modułów obsługi zdarzeń jQuery dla błędów Ajax, wyłapuje praktycznie wszystkie obiekty Error wrzucane do kodu klienta. Jeśli ręcznie konfigurujesz program obsługi dla window.onerror, w nowoczesnych przeglądarkach odbywa się to window.addEventListener('error', callback), podczas gdy w IE8 / 9 musisz wywołać window.attachEvent('onerror', callback).

Zauważ, że powinieneś następnie rozważyć środowisko, w którym te błędy są obsługiwane, i powód tego. Wyłapywanie jak największej liczby błędów za pomocą ich śledzenia stosu to jedno, ale pojawienie się nowoczesnych narzędzi programistycznych F12 rozwiązuje ten przypadek użycia podczas lokalnego wdrażania i debugowania. Punkty przerwania itp. Dają więcej danych niż są dostępne w programach obsługi, szczególnie w przypadku błędów zgłaszanych przez biblioteki innych firm, które zostały załadowane z żądań CORS. Musisz podjąć dodatkowe kroki, aby poinstruować przeglądarkę, aby udostępniła te dane.

Kluczowym problemem jest dostarczenie tych danych w środowisku produkcyjnym, ponieważ Twoi użytkownicy mają gwarancję, że będą uruchamiać znacznie szerszą gamę przeglądarek i wersji, niż możesz przetestować, a Twoja witryna / aplikacja będzie się łamać w nieoczekiwany sposób, niezależnie od tego, ile kontroli jakości rzucisz to.

Aby sobie z tym poradzić, potrzebujesz narzędzia do śledzenia błędów produkcyjnych, które wychwytuje każdy błąd zgłaszany w przeglądarkach użytkownika, gdy używają Twojego kodu, i wysyła je do punktu końcowego, gdzie dane mogą być przeglądane przez Ciebie i używane do naprawiania błędów w miarę ich występowania . W Raygun (zrzeczenie się: pracuję w Raygun) włożyliśmy sporo wysiłku, aby zapewnić doskonałe wrażenia, ponieważ istnieje wiele pułapek i problemów, które należy wziąć pod uwagę, których naiwna implementacja zostanie pominięta.

Na przykład jest szansa, że ​​będziesz łączyć i minimalizować swoje zasoby JS, co oznacza, że ​​błędy wyrzucane ze zminimalizowanego kodu będą zawierały niepotrzebne ślady stosu z zniekształconymi nazwami zmiennych. W tym celu potrzebujesz narzędzia do kompilacji do generowania map źródłowych (zalecamy UglifyJS2 dla tej części potoku), a moduł śledzenia błędów do akceptowania i przetwarzania tych map, zmieniając zniekształcone ścieżki stosu z powrotem w czytelne dla człowieka. Raygun robi to wszystko po wyjęciu z pudełka i zawiera punkt końcowy interfejsu API do akceptowania map źródłowych w miarę ich generowania przez proces kompilacji. Jest to kluczowe, ponieważ muszą być niepubliczne, w przeciwnym razie każdy mógłby unieszkodliwić Twój kod, negując jego cel.

Raygun4js biblioteki klienta jest również wyposażony window.onerrorzarówno dla nowoczesnych i starszych przeglądarek, jak również jQuery haki out-of-the-box, tak aby ustawić to trzeba tylko dodać:

<script type="text/javascript" src="//cdn.raygun.io/raygun4js/raygun.min.js" </script> <script> Raygun.init('yourApiKey').attach(); </script>

Istnieje również wiele wbudowanych funkcji, w tym możliwość mutowania ładunku błędu przed jego wysłaniem, dodawanie tagów i niestandardowych danych, metadanych użytkownika, który widział błąd. Eliminuje to również ból związany z uzyskiwaniem dobrych śladów stosu z wyżej wymienionych skryptów CORS innych firm, co rozwiązuje przerażający „błąd skryptu” (który nie zawiera komunikatu o błędzie ani śladu stosu).

Bardziej istotnym problemem jest to, że ze względu na ogromną liczbę odbiorców w sieci witryna wygeneruje wiele tysięcy zduplikowanych wystąpień każdego błędu. Usługa śledzenia błędów, taka jak Raygun, ma sprytne rozwiązania, które pozwalają łączyć je w grupy błędów, dzięki czemu nie utoniesz w zalewie powiadomień i możesz zobaczyć każdy faktyczny błąd gotowy do naprawienia.

K. Craven
źródło
0

Szukałem również obsługi błędów i śledzenia stosu oraz rejestrowania działań użytkowników. To jest to, co znalazłem, mam nadzieję, że to również pomoże ci https://github.com/jefferyto/glitchjs

RamIndani
źródło