Obecnie refaktoryzuję duży podsystem o architekturze wielowarstwowej i staram się zaprojektować skuteczną strategię rejestrowania błędów / obsługi.
Powiedzmy, że moja architektura składa się z następujących trzech warstw:
- Interfejs publiczny (IE i kontroler MVC)
- Warstwa Domeny
- Warstwa dostępu do danych
Moim źródłem zamieszania jest to, gdzie powinienem zaimplementować rejestrowanie błędów / obsługę:
Najłatwiejszym rozwiązaniem byłoby zaimplementowanie rejestrowania na najwyższym poziomie (np. Sterownik interfejsu publicznego \ MVC). Jednak wydaje się to niewłaściwe, ponieważ oznacza przepuszczenie wyjątku przez różne warstwy, a następnie zalogowanie go; zamiast rejestrować wyjątek u źródła.
Rejestrowanie wyjątku u źródła jest oczywiście najlepszym rozwiązaniem, ponieważ mam najwięcej informacji. Mój problem polega na tym, że nie mogę wychwycić każdego wyjątku u źródła bez przechwytywania WSZYSTKICH wyjątków, aw warstwie domena / interfejs publiczny doprowadzi to do wychwycenia wyjątków, które zostały już wychwycone, zarejestrowane i ponownie rzucone przez warstwę poniżej .
Inną możliwą strategią jest połączenie # 1 i # 2; dzięki czemu wychwytuję określone wyjątki na warstwie, które najprawdopodobniej zostaną wyrzucone (IE Łapanie, rejestrowanie i ponowne rzucanie
SqlExceptions
w warstwie dostępu do danych), a następnie rejestrowanie dalszych nieprzechwyconych wyjątków na najwyższym poziomie. Wymagałoby to jednak ode mnie złapania i ponownego zarejestrowania każdego wyjątku na najwyższym poziomie, ponieważ nie mogę odróżnić błędów, które już zostały zarejestrowane \ obsługiwane od tych, które nie zostały zarejestrowane.
Oczywiście jest to problem występujący w większości aplikacji, dlatego musi istnieć standardowe rozwiązanie tego problemu, w wyniku którego wyjątki są wychwytywane u źródła i logowane raz; jednak po prostu nie widzę, jak to zrobić.
Uwaga: tytuł tego pytania jest bardzo podobny do „ Rejestrowanie wyjątków w aplikacji wielowarstwowej” ”, jednak odpowiedzi w tym poście nie zawierają szczegółów i nie są wystarczające, aby odpowiedzieć na moje pytanie.
źródło
The easiest solution would be to implement the logging at the top level
- Zrób to. Rejestrowanie wyjątków u źródła nie jest dobrym pomysłem, a każda aplikacja, z którą się zetknąłem, była PITA do debugowania. Osoba odpowiedzialna za wywołanie wyjątku powinna ponosić odpowiedzialność.try{ ... } catch(Exception ex) { Log(ex); }
spowoduje zapisanie tego samego wyjątku w każdej warstwie. (Wydaje się również, że złym zwyczajem jest wychwytywanie każdego wyjątku na każdej warstwie w bazie kodu.)Odpowiedzi:
Na twoje pytania:
Posiadanie wyjątkowej bańki na najwyższym poziomie jest absolutnie poprawnym i wiarygodnym podejściem. Żadna z metod wyższej warstwy nie próbuje kontynuować jakiegoś procesu po awarii, który zwykle nie może zakończyć się sukcesem. Dobrze wyposażony wyjątek zawiera wszystkie informacje niezbędne do logowania. A nie robienie niczego z wyjątkami pomaga utrzymać kod w czystości i skupić się na głównym zadaniu zamiast na błędach.
To w połowie poprawne. Tak, najbardziej przydatne informacje są tam dostępne. Ale zaleciłbym umieszczenie tego wszystkiego w obiekcie wyjątku (jeśli jeszcze go nie ma) zamiast natychmiastowego logowania. Jeśli logujesz się na niskim poziomie, nadal musisz zgłosić wyjątek, aby poinformować dzwoniących, że nie ukończyłeś pracy. To kończy się w wielu dziennikach tego samego zdarzenia.
Wyjątki
Moją główną wskazówką jest wychwytywanie i rejestrowanie wyjątków tylko na najwyższym poziomie. Wszystkie poniższe warstwy powinny zapewnić, że wszystkie niezbędne informacje o awariach zostaną przeniesione na najwyższy poziom. W aplikacji jednoprocesowej, np. W Javie, oznacza to w większości przypadków, że nie próbuj / nie łapaj ani nie loguj się poza najwyższym poziomem.
Czasami chcesz, aby niektóre informacje kontekstowe były zawarte w dzienniku wyjątków, które nie są dostępne w oryginalnym wyjątku, np. Instrukcja SQL i parametry, które zostały wykonane, gdy wyjątek został zgłoszony. Następnie możesz złapać oryginalny wyjątek i ponownie rzucić nowy, zawierający oryginalny plus kontekst.
Oczywiście prawdziwe życie czasami przeszkadza:
W Javie czasami trzeba złapać wyjątek i zawinąć go w inny typ wyjątku, aby zastosować się do niektórych podpisów ustalonych metod. Ale jeśli ponownie rzucisz wyjątek, upewnij się, że ponownie zgłoszony wyjątek zawiera wszystkie informacje potrzebne do późniejszego logowania.
Jeśli przekraczasz granicę między procesami, technicznie często nie możesz przenieść pełnego obiektu wyjątku, w tym śledzenia stosu. I oczywiście połączenie może zostać utracone. Oto punkt, w którym usługa powinna rejestrować wyjątki, a następnie dołożyć wszelkich starań, aby przekazać jak najwięcej informacji o awarii przez linię do swojego klienta. Usługa musi upewnić się, że klient otrzyma powiadomienie o awarii, albo otrzymując odpowiedź na awarię, albo uruchamiając limit czasu w przypadku zerwania połączenia. Zwykle spowoduje to, że ten sam błąd nie zostanie zarejestrowany dwukrotnie, raz w usłudze (bardziej szczegółowo) i raz na najwyższym poziomie klienta.
Logowanie
Dodam kilka zdań o logowaniu ogólnie, nie tylko logowaniu wyjątków.
Oprócz wyjątkowych sytuacji, chcesz, aby ważne działania aplikacji również były zapisywane w dzienniku. Tak więc użyj struktury rejestrowania.
Uważaj na poziomy dzienników (czytanie dzienników, w których informacje debugowania i poważne błędy nie są oznaczane odpowiednio, jest uciążliwe!). Typowe poziomy dziennika to:
W produkcji ustaw poziom dziennika na INFO. Wyniki powinny być przydatne dla administratora systemu, aby wiedział, co się dzieje. Spodziewaj się, że zadzwoni do ciebie po pomoc lub naprawę błędu dla każdego BŁĘDU w dzienniku i połowy OSTRZEŻEŃ.
Włącz poziom DEBUG tylko podczas prawdziwych sesji debugowania.
Pogrupuj wpisy dziennika w odpowiednie kategorie (np. Według w pełni kwalifikowanej nazwy klasy kodu, który generuje wpis), umożliwiając włączenie dzienników debugowania dla określonych części programu.
źródło
Przygotowuję się na przegłosowanie, ale zamierzam wyjść na całość i powiedzieć, że nie jestem pewien, czy mogę się z tym zgodzić.
Spieniężanie wyjątków, a tym bardziej rejestrowanie ich ponownie, to dodatkowy wysiłek przynoszący niewielkie korzyści. Złap wyjątek u źródła (tak, najłatwiej), zaloguj go, ale nie rzucaj ponownie wyjątku, po prostu zgłoś dzwoniącemu „błąd”. „-1”, null, pusty ciąg, trochę wyliczenia, cokolwiek. Dzwoniący musi tylko wiedzieć, że połączenie nie powiodło się, prawie nigdy makabryczne szczegóły. A te będą w twoim dzienniku, prawda? W rzadkim przypadku, gdy osoba dzwoniąca potrzebuje szczegółów, śmiało i bąbelkuj, ale nie jako automatyczne, bezmyślne domyślne.
źródło
null
czy to pusty ciąg? Czy to -1, czy jakaś liczba ujemna? 2. Jeśli rozmówca nie dba (tj. Nie sprawdza), prowadzi to do błędów następczych niezwiązanych z pierwotną przyczyną, npNullPointerException
. Lub gorzej: obliczenia trwają z nieprawidłowymi wartościami. 3. Jeśli rozmówca by się przejmował, ale programista nie uważa, że ta metoda zawodzi, kompilator nie przypomina mu. Wyjątki nie mają tego problemu, albo łapiesz, albo rzucasz.