Dlaczego „loguj i rzucaj” jest uważane za anty-wzór? [Zamknięte]

85

To pytanie wywołała dyskusja wokół tego artykułu , w której nie otrzymałem żadnych dobrych odpowiedzi.

Dlaczego rejestrowanie wyjątku, a następnie ponowne jego zgłoszenie (oczywiście z zachowaniem oryginalnego śladu stosu) powinno być złym pomysłem, jeśli nie możesz sobie z tym poradzić w inny sposób?

Manu
źródło
Spróbuj poszukać odpowiedzi na softwareengineering.stackexchange.com
chharvey

Odpowiedzi:

121

Zakładam, że odpowiedź brzmi głównie dlatego, że dlaczego to łapiesz, jeśli nie możesz sobie z tym poradzić? Dlaczego nie pozwolić, aby ktokolwiek mógł sobie z tym poradzić (lub komukolwiek, kto nie ma innego wyboru, jak tylko zająć się tym), zarejestrował to, jeśli czuje, że jest to warte kłódki?

Jeśli go złapiesz, zarejestrujesz i wyrzucisz ponownie, kod nadrzędny nie będzie wiedział, że wyjątek został już zarejestrowany, więc ten sam wyjątek może zostać zarejestrowany dwukrotnie. Lub, co gorsza, jeśli cały nadrzędny kod jest zgodny z tym samym wzorcem, wyjątek może zostać zarejestrowany dowolną liczbę razy, raz dla każdego poziomu w kodzie, który zdecyduje się go złapać, zarejestrować, a następnie ponownie wyrzucić.

Niektórzy mogą również twierdzić, że skoro wyrzucanie i przechwytywanie wyjątków jest operacjami stosunkowo kosztownymi, całe to przechwytywanie i ponowne wrzucanie nie poprawia wydajności środowiska wykonawczego. Nie pomaga też twojemu kodowi pod względem zwięzłości lub łatwości utrzymania.

aroth
źródło
dalsze omówienie znajduje się w komentarzach do odpowiedzi Jeffa
Manu,
8
Powodem rejestrowania i ponownego zgłaszania wyjątku może być zawężenie przyczyny i zarejestrowanie określonej wiadomości.
Mike Argyriou
11
Odpowiedź: mogą istnieć przydatne informacje debugowania dostępne w bieżącym punkcie stosu, które nie będą dostępne w innym punkcie śledzenia stosu
rtconner
Łapanie i ponowne wrzucanie jest czasami pomocne, jeśli nowy wyjątek jest bardziej pomocny i zawiera więcej informacji lub jest bardziej szczegółowy.
borjab
Aby wydać na to, co skomentował @rtconner: w kodzie asynchronicznym może się zdarzyć, że catcher nie ma kontekstu, który jest dostępny dla rzucającego.
Nir Alfasi
53

Log-and-throw to dobry wzorzec, jeśli jednostka przechwytująca i ponownie zgłaszająca wyjątek ma powód, by sądzić, że zawiera informacje, które nie zostaną zarejestrowane wyżej na stosie wywołań - przynajmniej nie w najbardziej pożądany sposób. Może się tak zdarzyć z kilku powodów:

  1. Wyjątek może zostać przechwycony i ponownie zgłoszony na granicy warstwy aplikacji i może zawierać uprzywilejowane informacje. Byłoby źle, gdyby warstwa bazy danych zezwalała na wyjątek mówiący, np. „Próba dodania zduplikowanego klucza 'fnord' do pola 'użytkowników'”, aby dotrzeć do zewnętrznej warstwy aplikacji (co z kolei może ujawnić ją użytkownikowi), ale może być przydatne dla wewnętrznych części bazy danych, aby zgłosić taki wyjątek, a interfejs aplikacji, aby go przechwycić, bezpiecznie zarejestrować i ponownie zgłosić nieco mniej opisowy wyjątek.
  2. Wyjątkiem może być taki, który warstwa zewnętrzna prawdopodobnie spodziewałaby się obsługi bez logowania, ale warstwa wewnętrzna może wiedzieć coś, czego warstwa zewnętrzna nie wie, co sugerowałoby, że rejestrowanie może być przydatne. Jako prymitywny przykład, środkowa warstwa aplikacji może być zaprogramowana tak, aby próbowała połączyć się z jednym serwerem, a jeśli to nie zadziała, spróbuj innego. Zapełnianie dziennika aplikacji komunikatami o niepowodzeniu połączenia, gdy serwer jest wyłączony z powodu konserwacji, może nie być pomocne, zwłaszcza że - z punktu widzenia aplikacji, wszystko działało dobrze. Przydatne może być przekazanie informacji o niepowodzeniu połączenia do zasobu rejestrującego skojarzonego z serwerami, który mógłby następnie filtrować dzienniki, aby wygenerować raport o błędach serwera, w przeciwieństwie do dziennika każdej pojedynczej próby połączenia .
supercat
źródło
4
# 1 to rzeczywiście rozsądny ogólny przypadek użycia, jednak OP wyraźnie zapytał o „ponowne przesłanie (oczywiście z zachowaniem oryginalnego śladu stosu)”, więc numer 1 nie jest właściwą odpowiedzią.
Geoffrey Zheng
@GeoffreyZheng: To zależy od tego, czy bezpieczne rejestrowanie oryginalnego śladu stosu liczyłoby się jako „zachowywanie”.
supercat
2
Odnośnie # 1: ujawnianie zawartości wyjątku (jak wyświetlanie e.getMessage()/ śledzenie stosu w interfejsie użytkownika, a także wysyłanie go jako odpowiedź REST) ​​powinno być traktowane jako luka sama w sobie, ponieważ wyjątek środowiska uruchomieniowego może zawierać wszelkiego rodzaju poufne informacje. Odnośnie # 2: możesz ponownie zgłosić wyjątek, dodając wszystkie informacje, które klient ma znać (+ główna przyczyna), bez potrzeby rejestrowania czegokolwiek.
Nikita Bosik
@NikitaBosik: Niezależnie od tego, czy wyjątki powinny być ujawniane po stronie klienta, aplikacje, które to robią, są na tyle powszechne, że zasada „dogłębnego bezpieczeństwa” sugeruje, że komunikaty o wyjątkach powinny być usuwane z poufnych informacji. Ponadto, jeśli warstwa zewnętrzna nie spodziewa się porzucić określonego typu wyjątku bez rejestrowania go ani przekazywania informacji do podmiotu zewnętrznego, który może być zainteresowany, dodanie informacji przez warstwę środkową, które warstwa zewnętrzna zignoruje, nie pomoże byle co.
supercat
20

Wydaje mi się, że najprostszym powodem jest to, że zazwyczaj masz jeden program obsługi najwyższego poziomu, który robi to za Ciebie, więc nie ma potrzeby zanieczyszczania kodu tą obsługą wyjątków.

Argument dotyczący przekrojowych obaw jest taki, że jest to strata czasu na obsługę błędów, które Cię nie dotyczą. Znacznie lepiej jest pozwolić, aby błąd wypełnił stos wywołań, dopóki nie zostanie znaleziony odpowiedni program obsługi.

Moim zdaniem jedyny przypadek, w którym powinieneś złapać wyjątek, to kiedy możesz zrobić coś pożytecznego z wynikami. Łapanie po prostu w celu zarejestrowania nie jest przydatne, ponieważ można by scentralizować tę pracę dalej.

Jeff Foster
źródło
1
Zgadzam się, że powinieneś mieć jedną osobę obsługującą najwyższego poziomu, która zrobi to za Ciebie. Ale moim zdaniem, ten program obsługi najwyższego poziomu POWINIEN wykonywać "logowanie i wyrzucanie". Tak więc argument dotyczy W KTÓRYM PUNKCIE należy „logować i rzucać”, a nie czy w ogóle tego robić?!?
Manu
@Manu, myślę, że chodzi o to, że jeśli jest to pojedynczy program obsługi (scentralizowany), to w porządku. Jeśli powielasz kod, to nie jest w porządku. Myślę, że nie ma w tym nic więcej!
Jeff Foster,
5
@Manu - do czego w takim przypadku rzuca program obsługi najwyższego poziomu? Wydaje mi się, że jeśli jest coś, do czego może rzucić, to nie jest to tak naprawdę program obsługi najwyższego poziomu. I na pewno nie chcesz ponownie zgłaszać wyjątku do samego środowiska wykonawczego. Spowoduje to awarię większości aplikacji.
aroth
1
@aroth: Widzę, że mówisz albo „zaloguj i obsłuż” (jeśli jesteś operatorem najwyższego poziomu), albo „nie rejestruj i nie wyrzucaj (lub obsługuj inaczej)”, jeśli nie jesteś. Dziękuję za zwrócenie uwagi.
Manu,
9

Dziennik i rzut IMO stanowią wyraźne naruszenie zasady najmniejszej niespodzianki.

Jeśli wyjątek jest obsługiwany prawidłowo na wyższych poziomach stosu wywołań, wpis w dzienniku błędów może w ogóle nie być wart. A potem mylące jest znalezienie wpisu w dzienniku błędów.

Bastian Voigt
źródło
A co z dziennikami bez błędów?
Su Zhang
6
@SuZhang Nie do końca rozumiem twoje pytanie. Jeśli nie ma błędu, nie ma nic do rzucenia. Oczywiście możesz i powinieneś zapisywać dzienniki bez błędów.
Bastian Voigt