Jest wiele z materiału out there co sugeruje, że druk ślad stosu wyjątku jest złą praktyką.
Np. Z sprawdzenia RegexpSingleline w Checkstyle:
Tego sprawdzenia można [...] użyć do znalezienia typowych złych praktyk, takich jak wywoływanie ex.printStacktrace ()
Jednak staram się znaleźć gdziekolwiek, co daje ważny powód, dla którego z pewnością ślad stosu jest bardzo przydatny w śledzeniu, co spowodowało wyjątek. Rzeczy, o których wiem:
Ślad stosu nigdy nie powinien być widoczny dla użytkowników końcowych (ze względu na wygodę użytkownika i ze względów bezpieczeństwa)
Generowanie śladu stosu jest stosunkowo kosztownym procesem (choć jest mało prawdopodobne, aby stanowił problem w większości „wyjątkowych” okoliczności)
Wiele platform rejestrowania wydrukuje ślad stosu za Ciebie (nasz nie i nie, nie możemy go łatwo zmienić)
Drukowanie śladu stosu nie stanowi obsługi błędów. Powinien być połączony z rejestrowaniem innych informacji i obsługą wyjątków.
Jakie są inne powody, dla których warto unikać drukowania śladu stosu w kodzie?
źródło
.printStackTrace()
w kodzie :)Odpowiedzi:
Throwable.printStackTrace()
zapisuje ślad stosu doSystem.err
PrintStream.System.err
Strumień bazowy i norma „error” strumienia wyjściowego procesu JVM może być przekierowana przezSystem.setErr()
które zmienia miejsce docelowe wskazywane przezSystem.err
./dev/null
.Wnioskując z powyższego, wywołanie
Throwable.printStackTrace()
stanowi tylko prawidłowe (nie dobre / świetne) zachowanie obsługi wyjątkówSystem.err
podlegałeś zmianie przydziału przez cały czas trwania aplikacji,System.err
(i standardowy strumień wyjściowy błędów maszyny JVM).W większości przypadków powyższe warunki nie są spełnione. Można nie być świadomym istnienia innego kodu działającego w JVM i nie można przewidzieć rozmiaru pliku dziennika ani czasu trwania procesu w czasie wykonywania, a dobrze zaprojektowana praktyka rejestrowania polegałaby na pisaniu plików dziennika „przetwarzanych maszynowo” (a preferowana, ale opcjonalna funkcja w rejestratorze) w znanym miejscu docelowym, aby pomóc w obsłudze.
Na koniec należy pamiętać, że wynik programu na
Throwable.printStackTrace()
pewno zostałby przepleciony z innymi zapisanymi treściamiSystem.err
(i być może nawetSystem.out
gdyby obie zostały przekierowane do tego samego pliku / urządzenia). Jest to irytacja (w przypadku aplikacji jednowątkowych), z którą należy sobie poradzić, ponieważ w takim przypadku dane wokół wyjątków nie są łatwe do przeanalizowania. Co gorsza, jest wysoce prawdopodobne, że aplikacja wielowątkowa będzie generować bardzo zagmatwane dzienniki, ponieważThrowable.printStackTrace()
nie jest bezpieczna dla wątków .Nie ma mechanizmu synchronizacji do synchronizacji zapisu śladu stosu,
System.err
gdy wiele wątków jest wywoływanychThrowable.printStackTrace()
w tym samym czasie. Rozwiązanie tego w rzeczywistości wymaga synchronizacji kodu na monitorze powiązanym zSystem.err
(a takżeSystem.out
, jeśli docelowy plik / urządzenie jest takie samo), a to jest dość wysoka cena za poprawność pliku dziennika. Na przykład, klasyConsoleHandler
iStreamHandler
są odpowiedzialne za dołączanie rekordów dziennika do konsoli, korzystając z narzędzia do rejestrowania udostępnianego przezjava.util.logging
; faktyczna operacja publikowania rekordów dziennika jest zsynchronizowana - każdy wątek, który próbuje opublikować rekord dziennika, musi również uzyskać blokadę na monitorze związanym zStreamHandler
instancja. Jeśli chcesz mieć taką samą gwarancję posiadania rekordów dziennika bez przeplotu przy użyciuSystem.out
/System.err
, musisz zapewnić to samo - komunikaty są publikowane w tych strumieniach w sposób umożliwiający serializację.Biorąc pod uwagę wszystkie powyższe i bardzo ograniczone scenariusze, w których
Throwable.printStackTrace()
faktycznie się przydają, często okazuje się, że przywoływanie tego jest złą praktyką.Rozszerzając argument w jednym z poprzednich akapitów, jest to również zły wybór
Throwable.printStackTrace
w połączeniu z loggerem, który zapisuje w konsoli. Częściowo jest to spowodowane tym, że rejestrator synchronizuje się na innym monitorze, podczas gdy aplikacja (prawdopodobnie, jeśli nie chcesz, aby rekordy dziennika z przeplotem) synchronizowała się na innym monitorze. Argument ten ma również zastosowanie, gdy używasz dwóch różnych rejestratorów, które piszą do tego samego miejsca docelowego w aplikacji.źródło
System.out.println
iThrowable.printStackTrace
oczywiście wymagana jest ocena programisty. Trochę się martwiłem, że pominięto część dotyczącą bezpieczeństwa nici. Jeśli spojrzysz na większość implementacji rejestratorów, zauważysz, że synchronizują one część, w której zapisywane są rekordy dziennika (nawet do konsoli), chociaż nie pobierają monitorów naSystem.err
lubSystem.out
.Dotykasz tutaj wielu problemów:
Tak, powinien być dostępny do diagnozowania problemów użytkowników końcowych, ale użytkownik końcowy nie powinien ich widzieć z dwóch powodów:
Generowanie śladu stosu ma miejsce, gdy wyjątek jest tworzony / rzucany (dlatego rzucenie wyjątku ma swoją cenę), drukowanie nie jest takie drogie. W rzeczywistości możesz zastąpić
Throwable#fillInStackTrace()
skutecznie wyjątek niestandardowy, dzięki czemu rzucanie wyjątku jest prawie tak tanie, jak proste polecenie GOTO.Bardzo dobra uwaga. Główny problem polega na tym, że jeśli framework rejestruje wyjątek za Ciebie, nie rób nic (ale upewnij się, że tak jest!) Jeśli chcesz sam zarejestrować wyjątek, użyj struktury rejestrowania, takiej jak Logback lub Log4J , aby nie umieszczać ich na surowej konsoli ponieważ bardzo trudno to kontrolować.
Dzięki strukturze logowania możesz łatwo przekierować ślady stosu do pliku, konsoli lub nawet wysłać je na określony adres e-mail. Z zakodowanym
printStackTrace()
na stałe musisz żyć zsysout
.Ponownie: zaloguj się
SQLException
poprawnie (z pełnym śladem stosu, używając struktury rejestrowania) i pokaż miły: „ Przepraszamy, obecnie nie możemy przetworzyć Twojego żądania ”. Czy naprawdę uważasz, że użytkownik jest zainteresowany powodami? Czy widziałeś ekran błędu StackOverflow? Jest bardzo zabawny, ale nie zdradza żadnych szczegółów. Jednak zapewnia użytkownikowi, że problem zostanie zbadany.Ale to zrobi zadzwonić natychmiast i trzeba być w stanie zdiagnozować problem. Potrzebujesz więc obu: odpowiedniego rejestrowania wyjątków i przyjaznych dla użytkownika komunikatów.
Podsumowując: zawsze rejestruj wyjątki (najlepiej przy użyciu struktury rejestrowania ), ale nie ujawniaj ich użytkownikowi końcowemu. Zastanów się dokładnie i nad komunikatami o błędach w swoim GUI, pokazuj ślady stosu tylko w trybie programowania.
źródło
Pierwsza rzecz printStackTrace () nie jest kosztowna, jak twierdzisz, ponieważ ślad stosu jest wypełniany, gdy wyjątek jest tworzony sam.
Chodzi o to, aby wszystko, co trafia do dzienników, było przekazywane przez strukturę rejestratora, aby można było kontrolować rejestrowanie. Dlatego zamiast używać printStackTrace, po prostu użyj czegoś takiego jak
Logger.log(msg, exception);
źródło
Drukowanie śladu stosu wyjątku samo w sobie nie stanowi złej praktyki, a jedynie drukowanie śladu stosu, gdy wystąpi wyjątek, jest tutaj prawdopodobnie problemem - często samo wydrukowanie śladu stosu nie wystarcza.
Istnieje również tendencja do podejrzeń, że właściwa obsługa wyjątków nie jest wykonywana, jeśli wszystko, co jest wykonywane w
catch
bloku, to plike.printStackTrace
. Niewłaściwa obsługa może w najlepszym przypadku oznaczać, że problem jest ignorowany, aw najgorszym program kontynuuje wykonywanie w niezdefiniowanym lub nieoczekiwanym stanie.Przykład
Rozważmy następujący przykład:
Tutaj chcemy wykonać pewne przetwarzanie inicjalizacji, zanim przejdziemy do przetwarzania, które wymaga, aby inicjalizacja miała miejsce.
W powyższym kodzie wyjątek powinien zostać przechwycony i odpowiednio obsłużony, aby uniemożliwić programowi przejście do
continueProcessingAssumingThatTheStateIsCorrect
metody, która mogłaby spowodować problemy.W wielu przypadkach
e.printStackTrace()
oznacza to, że jakiś wyjątek jest połykany i przetwarzanie może przebiegać tak, jakby nie wystąpił każdy problem.Dlaczego stało się to problemem?
Prawdopodobnie jednym z największych powodów, dla których słaba obsługa wyjątków stała się bardziej powszechna, jest sposób, w jaki środowiska IDE, takie jak Eclipse, będą automatycznie generować kod, który wykona
e.printStackTrace
operację obsługi wyjątków:(Powyższe to rzeczywisty
try-catch
automatycznie generowany przez Eclipse do obsługiInterruptedException
wyrzucenia przezThread.sleep
.)W przypadku większości aplikacji samo wydrukowanie śladu stosu na błąd standardowy prawdopodobnie nie będzie wystarczające. Niewłaściwa obsługa wyjątków może w wielu przypadkach doprowadzić do uruchomienia aplikacji w stanie, który jest nieoczekiwany i może prowadzić do nieoczekiwanego i niezdefiniowanego zachowania.
źródło
Myślę, że twoja lista powodów jest dość obszerna.
Jeden szczególnie zły przykład, który napotkałem więcej niż raz, wygląda następująco:
Problem z powyższym kodem polega na tym, że obsługa składa się wyłącznie z
printStackTrace
wywołania: wyjątek nie jest właściwie obsługiwany ani nie ma możliwości ucieczki.Z drugiej strony, z reguły zawsze rejestruję ślad stosu, gdy w moim kodzie jest nieoczekiwany wyjątek. Przez lata ta polityka zaoszczędziła mi wiele czasu na debugowanie.
Wreszcie, w lżejszym tonie, doskonały wyjątek Boga .
źródło
printStackTrace()
drukuje na konsoli. W warunkach produkcyjnych nikt nigdy tego nie ogląda. Suraj ma rację, powinien przekazać te informacje rejestratorowi.źródło
W aplikacjach serwerowych stacktrace wysadza twój plik stdout / stderr. Może stawać się coraz większy i wypełniony bezużytecznymi danymi, ponieważ zwykle nie masz kontekstu, sygnatury czasowej i tak dalej.
np. catalina.out podczas używania tomcat jako kontenera
źródło
Nie jest to zła praktyka, ponieważ coś jest „nie tak” w funkcji PrintStackTrace (), ale ponieważ jest to „zapach kodu”. W większości przypadków wywołanie PrintStackTrace () występuje, ponieważ komuś nie udało się poprawnie obsłużyć wyjątku. Gdy we właściwy sposób poradzisz sobie z wyjątkiem, na ogół nie przejmujesz się już StackTrace.
Ponadto wyświetlanie śladu stosu na stderr jest ogólnie przydatne tylko podczas debugowania, a nie w środowisku produkcyjnym, ponieważ bardzo często stderr prowadzi donikąd. Rejestrowanie ma większy sens. Jednak samo zastąpienie funkcji PrintStackTrace () rejestrowaniem wyjątku nadal pozostawia aplikację, która uległa awarii, ale nadal działa, jakby nic się nie stało.
źródło
Jak niektórzy faceci już tutaj wspomnieli, problem polega na tym, że z wyjątkiem połknięcia na wypadek, gdybyś zadzwonił
e.printStackTrace()
docatch
bloku. Nie zatrzyma wykonywania wątku i będzie kontynuowane po bloku try, jak w normalnych warunkach.Zamiast tego musisz albo spróbować odzyskać z wyjątku (w przypadku, gdy jest możliwy do odzyskania), albo rzucić
RuntimeException
, albo przesłać do wywołującego wyjątek, aby uniknąć cichych awarii (na przykład z powodu niewłaściwej konfiguracji rejestratora).źródło
Aby uniknąć splątanego problemu ze strumieniem wyjściowym, o którym mówi @Vineet Reynolds
możesz wydrukować to na standardowe wyjście:
e.printStackTrace(System.out);
źródło