Logging in software development – it’s really unnecessary ?

O słuszności logowania zdarzeń  i wyjątków nie trzeba nikomu mówić, ale jak robić to dobrze ? Najprostsze logowanie błędów w javie można obsłużyć za pomocą strumieni out i err, ale czy to wszystko ? Czy wypisanie na standardowe wyjście stack trace jest dobrym i wystarczającym rozwiązaniem ? Otóż… czasami tak. Ale można to zrobić lepiej

I tu z pomocą przychodzą nam biblioteki takie jak java.util.logging(Java API),  log4j, logback, log4j2 czy fasada SLF4J. Wszystkie biblioteki ułatwiają znacznie pracę z logami, poprzez dostarczenie implementacji. Dla przeciętnego programisty oznacza to jedynie dodanie zależności do projektu oraz odpowiedniej konfiguracji. Większość loggerów umożliwia konfigurację przy pomocy xml, pliku properties lub programową w javie lub groovy.

Na wyjątkową uwagę zasługuje SLF4J będący fasadą. Co to oznacza ? Korzystając z tego podejścia uniezależniamy się od wykorzystywanej implementacji.  To jaki logger zostanie użyty do logowania uzależnione jest od dostępnych w runtime’ie zależności. Oznacza to że ta sama aplikacja korzystająca z fasady, ale zdeployowana na różnych serwerach aplikacyjnych może logować za pomocą zupełnie innej implementacji. Ot taka abstrakcja.

Istotnym do poruszenia jest temat poziomów logowania, każda z implementacji posiada zadeklarowaną własną hierarchię poziomów, aczkolwiek wszystkie są dość podobne. Np w logback’u możemy wyróżnić:

  • ERROR – logowanie tylko błędów i zdarzeń którą są krytyczne z punktu widzenia aplikacji
  • WARN – logowanie potencjalnie szkodliwych zdarzeń
  • INFO – logowanie informacji o przepływie sterowania w aplikacji
  • DEBUG – szczegółowe logowanie informacji o zdarzeniach, logowanie mało istotnych danych, przydatne do debugowania
  • TRACE – bardzo szczegółowe logowanie zdarzeń o niskim priorytecie

Stosując odpowiedni poziom logowania, możemy kontrolować jakie dane oraz jak szczegółowo mają zostać zapisane. A gdzie możemy zapisać logi ?

Gdzie tylko dusza zapragnie. Większość loggerów udostępnia wiele implementacji appenderów. Co to jest ? Appender jest to implementacja metody zapisu logów. Logi mogą być wyświetlane na konsoli:

 

Lub trafiać do pliku. Logi w plikach mogą znajdować się w pojedynczym pliku, ale wyobraźmy sobie sytuacje gdy nasza aplikacja produkuje naprawdę ogromne ilości zdarzeń. Jak poradzić sobie z plikiem który ma kilkaset megabajtów ? Czytelność jest praktycznie zerowa. W takiej sytuacji na ratunek przychodzi appender który zapisuje do osobnych plików zdarzenia z każdego dnia:

To są tylko dwa przykłady dostępnych appenderów z logbacka. Dostępne są implementacje które zapisują do bazy, sysloga, wysyłają logi na zdefiniowany socket itd. A gdy i to za mało zawsze możesz zaimplementować własny appender. Definiując appender możemy zdefiniować pattern, który jest odpowiedzialny za sposób formatowania logów.

Gdy już mamy gotowy appender wystarczy podpiąć go do loggera oraz zdefiniować poziom logowania:

Zdarzenia logujemy w bardzo intuicyjny sposób:

Logger.<poziom_logowania>(logowany obiekt)

Dobrą praktyką jest definiowanie loggerów jako pola statyczne w klasie gdzie będzie wykorzystywany:

I na koniec taki mały antypattern na który jestem uczulony:

Jeśli używasz nowych loggerów to nie musisz sprawdzać poziomu logowania gdy przekazujesz obiekt jako parametr do loggera:

Metoda toString() zostanie wywołana na obiekcie tylko jeśli wiadomość ma zostać zapisana (tylko jeśli jest ustawiony wybrany poziom logowania). Nie zaciemniajmy kodu takimi kwiatkami 😉

Na koniec parę linków do dokumentacji z którymi warto się zapoznać 😉

4 Replies to “Logging in software development – it’s really unnecessary ?”

  1. Cześć Łukasz, fajnie, że piszesz blog 😉
    Przykład z log.debug jest chyba błędny.
    W przypadku, który podałeś String zostanie zbudowany niezależnie od poziomu logowania – więc może dojść do problemów wydajnościowych itd. Operator “+” jest normalnie obsługiwany jak zawsze w Javie.
    Żeby dostać lazy evaluation musisz korzystać z innego mechanizmu formatowania wiadomości, tj:

    logger.debug(“Logging in user {} with birthday {}”, user.getName(), user.getBirthdayCalendar());

    Więcej info tutaj: https://logging.apache.org/log4j/2.0/manual/api.html

    Pozdrawiam, Darek!

Leave a Reply

Your email address will not be published. Required fields are marked *