15

Aby ułatwić sobie debugowanie, przechwytuję wszystkie dzienniki konsoli w przeglądarce Chrome, aby użytkownicy, którzy przesyłają opinię, również przekażą wszystkie dzienniki na nasz serwer. Kiedy ktoś napotyka problem w produkcji, mogę przede wszystkim przywrócić go do pracy, abym mógł usiąść i dokładniej przeglądać wszystkie dzienniki, aby określić podstawową przyczynę problemu, który napotkał użytkownik podczas produkcji.Jak mogę przesłonić/rozszerzyć ReferenceError w JavaScript w przeglądarce Chrome?

Technika, której używam do przechwytywania dzienników, powoduje przesłonięcie pliku console.log, dzięki czemu cały tekst wpisany w pierwszym argumencie zostaje zapisany w tablicy, jednocześnie wywołując starszą funkcję, dzięki czemu nadal mogę wyświetlać dzienniki w konsoli.

Problem polega na tym, że od czasu do czasu występuje nieprzechwycony wyjątek. Nie są one uwzględniane w przesyłanych dziennikach, więc nie zawsze jest jasne, co spowodowało problem. Próbowałem więc przesłonić ReferenceError, pisząc funkcję JavaScript, która przyjmuje funkcję jako argument, a następnie zwraca nową funkcję, która zajmuje się nim, np. Przechowywanie danych w zmiennej, a następnie wywołanie funkcji starszej wersji jako ostatniego kroku:

function overrideException(legacyFn) { 

    /** arguments for original fn **/ 
    return function() { 

     var args = []; 

     args[0] = arguments[0]; 

     // pass in as arguments to original function and store result to 
      // prove we overrode the ReferenceError 
     output = ">> " + legacyFn.apply(this, args).stack; 

     return legacyFn.apply(this, arguments); 
    }   

} 

przetestować funkcję overrideException, uruchomiony po kodu na konsoli:

ReferenceError = overrideException(ReferenceError); 

Następnie przetestowane zwrócony funkcję nowego ReferenceError, ręcznie wyrzuceniem ReferenceError:

throw new ReferenceError("YES!! IT WORKS! HAHAHA!"); 

Wynikiem na konsoli to:

ReferenceError: YES!! IT WORKS! HAHAHA!

i sprawdzanie zmienną globalną output z funkcji overrideException pokazuje, że rzeczywiście uruchomić:

output 
    ">> ReferenceError: YES!! IT WORKS! HAHAHA! 
    at ReferenceError (<anonymous>) 
    at new <anonymous> (<anonymous>:18:35) 
    at <anonymous>:2:7 
    at Object.InjectedScript._evaluateOn (<anonymous>:562:39) 
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52) 
    at Object.InjectedScript.evaluate (<anonymous>:440:21)" 

Teraz, tutaj, gdzie wszystko zaczyna spadać niezależnie. W naszym kodzie, nie będziemy wiedzieć, kiedy wystąpi przechwycony wyjątek, więc testowałem go, próbując uruchomić funkcję, która nie istnieje:

ttt(); 

co skutkuje:

ReferenceError: ttt is not defined

Jednak w przeciwieństwie do przypadku, w którym jawnie zgłasza się błąd, w tym przypadku funkcja nie uruchamia się, a pozostaje nam tylko starsza funkcja. Zawartość zmiennej output jest taka sama jak w pierwszym teście.

Pytanie wydaje się następujące: W jaki sposób nadpisujemy funkcję ReferenceError używaną przez silnik JavaScript do zgłaszania błędów, tak aby była taka sama, jaką używamy, gdy wyrzucamy błąd ReferenceError?

Należy pamiętać, że mój problem jest obecnie ograniczony tylko do przeglądarki Chrome; Buduję aplikację Chrome Packaged.

+0

Zgadnij, że nie chcesz owijać swojego kodu w 'try/catch' i przetworzyć obiekt błędu, a następnie wrzucić tam ponownie? –

+0

@CrazyTrain - Mogę to zrobić, i prawdopodobnie to zrobię. Jest to jednak żmudne i możliwe, że coś przeoczysz. Lubię rozwiązania, które są obojętne i zawierają wszystko, ponieważ generalnie są również "Jestem zbyt zajęty" :) Moim planem jest oczywiście użycie większej ilości prób/chwytów, ale wydawało mi się to tak doskonałym rozwiązaniem, więc Zacząłem w to zaglądać. – jmort253

+0

Mam na myśli jedno monolityczne 'try/catch', które owija cały twój kod. Ponieważ wydaje się, że celem jest nie tyle radzenie sobie z błędami w miejscu, ale raczej jakoś ich sformatowanie, a następnie zawijanie całego kodu w pojedynczym "try/catch" wydaje się trochę inne niż obecne podejście do zastępowania konstruktora. Musisz tylko mieć pewność, że ponownie rzucisz wszystkie błędy, które otrzymujesz. –

Odpowiedz

12

Zrobiłem sporo badań z tego samego powodu: chciałem rejestrować błędy i zgłaszać je.

"Zastąpienie" natywnego typu (niezależnie od tego, czy ReferenceError, String, czy Array) nie jest możliwe.

Chrome łączy je przed uruchomieniem dowolnej wersji Javascript, więc ponowne zdefiniowanie window.ReferenceError nie daje żadnego efektu.

Możesz przedłużyć ReferenceError z czymś takim jak ReferenceError.prototype.extension = function() { return 0; }, lub nawet przesłonić toString (dla spójności, spróbuj na stronie, a nie na narzędziach Dev).

To niewiele pomoże.

Ale nie martw się ....

(1) Użyj window.onerror uzyskać nazwę pliku, 1-indeksowane numer wiersza oraz 0 indeksowane pozycję Uncaught błędów, jak również sam błąd.

var errorData = []; 
onerror = function(message, file, line, position, error) { 
    errorData.push({message:message, file:file, line:line, position:position, error:error}); 
}; 

Zobacz przykład fiddle. Ponieważ OP był specyficzny dla Chrome, został przetestowany tylko pod kątem współpracy z Chrome.

(2) Z powodu ulepszeń do (1), nie jest to już konieczne, ale pozostawiam tę drugą technikę tutaj dla kompletności, a ponieważ onerror jest not guaranteed to work for all errors on all browsers. Będzie również czasem zobaczyć, co następuje:

var errors = []; 
function protectedFunction(f) { 
    return function() { 
     try { 
      f.apply(this, arguments); 
     } catch(e) { 
      errors.push(e); 
      throw e; 
     } 
    }; 
} 
setTimeout = protectedFunction(setTimeout); 
setInterval = protectedFunction(setInterval); 
etc... 

FYI, to wszystko jest bardzo podobne do tego, co zostało zrobione w bibliotece Google Closure Compiler, w goog.debug, utworzony w trakcie rozwoju Gmail z zamiarem robi dokładnie to. Szczególnie interesujące jest goog.debug.ErrorHandler i goog.debug.ErrorReporter.

+1

Jesteście, jesteście geniuszem! Wypróbowałem to i faktycznie miałem niezakłócony wyjątek w moim kodzie, o którym nie wiedziałem, i został on zalogowany do mojego logu! Będę musiał grać dalej z tym nieco więcej, ponieważ zauważyłem, że nie działało to w pewnych callbackach. Mam nadzieję, że twoje podejście setTimeout/setInterval będzie działać w tych przypadkach. +1 – jmort253

+0

Dzięki. (1) powinien działać dla wszystkich niezatrzymanych wyjątków (czy to z oceny skryptu, detektorów zdarzeń itp.). Niech będzie wiadomo, jeśli znajdziesz przypadki, w których nie działa. –

+0

Będę musiał się z nim więcej bawić jutro, a może właśnie popełniłem błąd, ale przypominam sobie, że inny program obsługi zdarzeń rzuca błędy wygenerowane w chrome.app.window.create, zamiast obsługiwać je przez window.onerror. Próbuję tego w funkcji wywołania zwrotnego metody "create" w aplikacji pakietu w Chrome. – jmort253