Archive for Lipiec, 2009

Logowanie i obsługa wyjątków – czyli jak łatwo można sobie utrudniać życie

Witam ponownie! Przez ostatni czas czułem pewne pohamowanie do pisania bloga, ale dziś stwierdziłem, że nadszedł w końcu czas na przełamanie tej stagnacji. A stało się tak, dlatego, że kolejny (już chyba 1000) raz napotkałem się na bezsensowne rozwiązanie w kodzie. Rozwiązanie to dotyczy przechwytywania wyjątków w celu ich zalogowania. Na pewno każdy z was się spotkał z tego typu kodem, który wygląda podobnie do tego poniżej:

try
{
DoSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
throw new Exception(“DoSomething failed”);
}

Co w nim jest tak złego, że aż skusiłem się napisać na ten temat?

Krótko mówiąc wszystko oprócz wywołania metody DoSomething

Po pierwsze: Nie ma najmniejszego sensu, by przechwytywać przy każdym wywołaniu kodu wszelki możliwy wyjątek po to, by zalogować jego wystąpienie, a następnie ponownie wyrzucić ten lub inny wyjątek. Dlaczego nie ma to sensu? Odpowiedź jest prosta. Jedyne uzasadnione przechwytywania wyjątków jest wówczas, gdy musimy zareagować na dany wyjątek wywołaniem odpowiedniej logiki. Logowanie wyjątków jak najbardziej zalicza się do tego typu sytuacji, przy czym najczęściej występuje to na poziomie całej aplikacji, gdzie po zalogowaniu wyjątku zapytanie klienta powodujące wyjątek zostanie zignorowane lub praca aplikacji zostanie przerwana. Oczywiście w obu przypadkach konieczne jest powiadomienie klienta o wystąpieniu błędu.

Po drugie: Zalogowanie tylko i wyłącznie wiadomości (linia 3 – właściwość Message) występującego wyjątku w większości przypadków nie wystarcza. Metoda DoSomething może wywoływać inne metody lub posiadać skomplikowane operacje, które z wielu przyczyn mogą się nie powieść. Skąd w takiej sytuacji dowiemy się, co dokładnie mogło być przyczyną wyrzuconego wyjątku? Oczywiście możemy liczyć na to, że na podstawie wiadomości wyjątku dojdziemy do źródła problemy, ale z mojego doświadczenia wychodzi na to, że to jest raczej rzadki przypadek. W takim razie, co powinniśmy jeszcze zalogować po to by uzyskać więcej szczegółowych informacji? StackTrace! Dopiero StackTrace pozwoli nam zlokalizować linię, , która w kodzie spowodowała wyjątek. Dzięki temu jesteśmy w stanie w szybki sposób dojść do rzeczywistej przyczyny błędu. Oprócz StackTrace’u należy również zalogować informacje o wewnętrznym wyjątku (InnerException) jeżeli taki wystąpił. Na ten temat będzie więcej za chwilę. Ze względu na to, że logowanie powyżej wymienionych rzeczy wydaje się bardzo oczywiste, ktoś mądry w firmie Microsoft wpadł na to, że metoda ToString obiektu Exception zwróci wszystkie tego typu informacje. Co prowadzi to tego, że metoda ToString w całości wygląda mniej więcej tak:


Implementacja metody ToString klasy Exception

Implementacja metody ToString klasy Exception


Po trzecie: Przechwytywanie wyjątku po to by wyrzucić nowy wyjątek innego typu ma sens. Przy czym typ nowo wyrzucanego wyjątku powinien jednoznacznie sygnalizować rodzaj problemu. W wyżej przedstawionym kodzie tak się nie dzieje, dlatego, że jest wyrzucany bazowy typ wyjątku – Exception. W przypadku stosowania opisanej przeze mnie strategii warto się zastanowić nad tym, czy nowo wyrzucany wyjątek uzbroić w InnerException w celu udostępniania bardziej szczegółowych informacji o wyrzucanym wyjątku. W celu zrozumienia tego problemu warto spojrzeć na przykład z życia wzięty.

Korzystając z biblioteki Common Service Locator izolujemy pisany przez nas kod od implementacji specyficznego kontenera Inversion of Control. W celu pozyskania przy pomocy ServiceLocator’a obiektu żądanego typu – przykładowo obiekt typu ApplicationController - wystarczy wywołać poniżej przedstawiony kod.

ServiceLocator.Current.GetInstance();

Wywołanie tego kodu powoduje przekierowanie zapytania do konkretnego kontenera IoC. Jako, że domyślnie większość z dostępnych kontenerów IoC wyrzucają wyjątek w momencie, gdy nie są w stanie pozyskać obiekt żądanego typu, nie trudno natrafić na poniżej przedstawiony przypadek wyjątku. Wyjątek ActivateException, jak sama nazwa wskazuje, mówi o tym, że wystąpił problem aktywacji obiektu przy użyciu kontenera IoC. Wyjątek ten jest specyficzny dla biblioteki CommonServiceLocator, który tak naprawdę został wywołany na wskutek przechwycenia wyjątku wywołanego przez kontener IoC – w tym przypadku StructureMap. Informacje zawarte w tym wyjątku nie wskazują na rodzaj problemu, dopiero informacje wyjątku StructureMap, który został dołączony jako wewnętrzny wyjątek (Inner Exception) pozwalają na to. Rozwiązanie to odciąża nas od przechwytywania wyjątków wyrzucanych przez użyty kontener IoC, dlatego, że jedynym wyjątkiem, którego możemy się spodziewać jest ActivateException biblioteki Common Service Locator. Dzięki temu wprowadzamy luźne sprzężenie pomiędzy kontenerem IoC a naszą aplikacją, nie tracąc możliwości śledzenia przyczyn błędów.


ActivateException]

Wyjątek ActivateException z wyjątkiem wewnętrznym

5 comments Lipiec 23, 2009


Aktualnie czytam

  • Enterprise Integration Patterns

Tagi

About Agile ALT.NET ASP.NET MVC DDD Domain Driven Design Domain Model Exceptions Front Controller Logika biznesowa NHibernate OOP ORM Pair Programming Podstawy Prezentacja Projekty Informatyczne Routing SQL TDD Wroc.NET Wzorce XP

Dodatki

Blogroll

Znajomi

Najnowsze komentarze

zajefajnyx on Czy ty też nadużywasz procedur…
jenrom on Logowanie i obsługa wyjątków …
am on Fluent Nhibernate Rocks!!…
Jarek on Logowanie i obsługa wyjątków …
jenrom on Logowanie i obsługa wyjątków …

Archiwa