JAVA exPress > Archiwum > Numer 6 (2009-12-09) > Log4j - czyli jak skutecznie tworzyć logi w aplikacjach javowych

Log4j - czyli jak skutecznie tworzyć logi w aplikacjach javowych

W życiu każdego programisty JAVA prędzej czy później pojawia się etap, w którym dochodzi on do wniosku, że korzystanie z System.out.print w przypadku chęci sprawdzenia wartości danych zmiennych, stanu w którym obecnie znajduje się aplikacja itp. nie jest ani efektywne, ani efektowne. Oczywiście można powiedzieć, że korzystanie ze standardowego wyjścia na konsole jest łatwe i z pozoru szybkie. No tak, ale co w przypadku jeśli chcemy, by zamiast na konsole wynik był zapisywany do pliku? Wówczas trzeba wszystkie te elementy zastąpić odpowiednio innym kodem. Świetnie, ale jeśli chcemy aby wyniki były zapisywane już nie tylko do pliku, ale ponownie na konsole? Uff... no wymaga to wiele niepotrzebnej pracy. Czy można sobie ułatwić życie? Odpowiedź na to pytanie jest prosta: Log4j! Jest to wieloplatformowa biblioteka napisana w całości w Javie, której autorem jest Ceki Gülcü, a jej początki sięgają jeszcze ubiegłego wieku. Bez obawy, chodzi o lata 90-te :) Umożliwia ona zapisywanie logów w aplikacjach Javovych. Obecnie wspierana jest przez fundację Apache i wchodzi w skład projektu Jakarta. Wyróżnia się trzy wersje: 1.2 która jest uznawana za tę stabilną, 1.3 która została z pewnych względów porzucona oraz 2.0 która jest, nazwijmy to, eksperymentalna.

Co może być zapisywane w logach ?

Przede wszystkim szeroko rozumiany przepływ aplikacji. Możemy zapisywać jakie są aktualne wartości poszczególnych zmiennych i obiektów, a także wypisywać komunikaty o różnego typu błędach bądź informować o zdarzeniach biznesowych mających miejsce w trakcie działania aplikacji.

A w jaki sposób ?

To już zależy od naszej wyobraźni. Ale od początku. W log4j wyróżniamy trzy typy obiektów:

  • Logger'y,

  • Appender'y,

  • Layout'y.

Loggery posiadają metody, które tworzą logi i ustawiają im odpowiedni priorytet. Miejsca, do których mogą trafić logi są definiowane za pomocą Appender'ów. O tym jaką postać mają mieć komunikaty decydują obiekty typu Layout.

Wyróżniamy następujące Logger'y:

  • NOPLogger,

  • RootCategory,

  • RootLogger.

Najważniejszy jest RootLogger i to właśnie z niego będę korzystać w dalej omówionych przykładach. Pozostałe możemy pominąć. Zwłaszcza, że RootCategory uznawany jest za przestarzały.

Jeśli chodzi o ustawienie priorytetów, to wyróżniamy następujące poziomy:

  • FATAL: Poważne błędy powodujące przedwczesne zakończenie działania aplikacji;

  • ERROR: Błędy wykonania;

  • WARN: Przy użyciu przestarzałych komponentów, w sytuacjach niespodziewanych nie wpływających na wadliwe działanie;

  • INFO: W celu śledzenia wykonania;

  • DEBUG: Szczegółowe info. dotyczące przepływu w działaniu aplikacji, w celach diagnostycznych;

  • TRACE: Bardziej szczegółowe info;

  • ALL / OFF: Wszystkie poziomy / wyłączenie logów.

Przy wyborze danego poziomu musimy wziąć pod uwagę to, że wszystkie logi wypisywane na poziomach niższych od tego który wybraliśmy nie będą wyświetlane. Przykładowo, jeśli poziom ustawimy na DEBUG (domyślny) to wówczas wszystkie logi wypisywane na poziomie TRACE nie ujrzą światła dziennego.

Jeśli chodzi o różnego rodzaju Appendery, to jest ich dużo zdefiniowanych, choć nikt nam nie zabrania by tworzyć własne w oparciu o klasę AppenderSkeleton. Wymienię najciekawsze z nich:

ConsoleAppender wypisuje logi na konsolę, domyślnie działa jak System.out. , ale może także jako System.Err.

WriterAppender zapisuje logi do pliku korzystając z klasy Writer bądź OutputStream.

JDBCAppender umożliwia zapis logów do bazy danych.

LF5Appender zapisuje logi do konsoli opartej na Swingu.

SMTPAppender pozwala wysłać logi na e-maila, głównie dotyczące poważnych błędów w działaniu aplikacji.

DailyRollingFileAppender rotuje logi do określonego rozmiaru, umożliwiając przy tym zapis z podziałem na lata, miesiące, dni itd.

Mając już wybrany Appender który wie gdzie zapisywać należy ustalić co będziemy zapisywać, a więc czas na Layouty:

HTMLLayout produkuje tabele HTML.

PatternLayout umożliwia określenie szablonu wpisu.

SimpleLayout powoduje, że logi są wpisywane w postaci: poziom – wiadomość.

XMLLayout formuje wpis do formatu xml'owego

DateLayout ułatwia nam zarządzanie czasem umieszczanym we wpisie.

Z własnego doświadczenia oraz wszelkich rozmów użytkowników opisywanej biblioteki wynika, iż najbardziej popularnymi są PatternLayout i HTMLLayout.

Jeśli zdecydujemy się na wykorzystanie PatternLayoutu, to wówczas możemy określić w jaki sposób ma wyglądać dany wpis. Jak to zrobić? Określamy szablon, w którym stosujemy poniższe elementy:

Z własnego doświadczenia wynika,
iż najbardziej popularnymi są PatternLayout i HTMLLayout.

%% - pojedynczy znak %

%c - kategoria zdarzenia

%C - nazwa klasy, z której został wysłany wpis

%d - data zdarzenia

%m - treść komunikatu

%n - separator linii

%p - priorytet zdarzenia

%r - liczba milisekund, które upłynęły od momentu uruchomienia aplikacji

%t - nazwa wątku, z którego został wysłany wpis

%x - kontekst diagnostyczny powiązany z wątkiem

%M - nazwa metody, z której został wysłany wpis

%L - numer linii, z której pochodzi zdarzenie

%F - nazwa pliku, z którego pochodzi zdarzenie

%l – lokalizacja, z której został wysłany wpis

Zaleca się ograniczenie czterech ostatnich elementów ze względu na spowalnianie aplikacji.

Pozostała nam jeszcze drobna rzecz jaką jest konfiguracja. Możemy tego dokonać na trzy sposoby:

  1. przy użyciu obiektu typu BasicConfigurator,

  2. przy zastosowaniu pliku właściwości,

  3. przy wykorzystaniu pliku XML.

BasicConfiguratora używamy jeśli na szybko chcemy skonfigurować tworzenie log'ów.

Jest to najmniej skomplikowany sposób konfiguracji. Powoduje utworzenie obiektu PatternLayout, którego ConversionPattern ma wartość:

BasicConfiguratora używamy jeśli na szybko
chcemy skonfigurować tworzenie log'ów.

%-4r [%t] %-5p %c %x - %m%n

Klasa BasicConfigurator posiada tylko trzy metody : configure() bezargumentowy i z jednym argumentem ( Appender ) oraz resetConfiguration().

Konfiguracja za pomocą pliku właściwości jest podobna do konfiguracji przy wykorzystaniu pliku XML, lecz składnia jest inna. Oto przykładowy kod wywołany ze statycznej metody main:

    BasicConfigurator.configure();
    Logger logger = Logger.getRootLogger();
    logger.debug("Hello world");

Wynikiem jest:

    2 [main] DEBUG root  - Hello world

Jest to zrozumiałe, gdyż korzystamy z domyślnego szablonu o którym nie tak dawno wspomniałem.

Jeśli chcemy zdefiniować własny szablon, to możemy to zrobić przykładowo:

    Layout lay1 = new PatternLayout("[%p] %c - %m - Data wpisu: %d %n");
    Appender app1 = new ConsoleAppender(lay1);
    BasicConfigurator.configure(app1);
    Logger logger = Logger.getRootLogger();
    logger.debug("Hello world");

Wynik jaki otrzymałem:

    [DEBUG] root - Hello world - Data wpisu: 2009-11-24 15:20:29,049

Dlaczego tak się dzieje? To proste: deklarujemy szablon o danej postaci oraz appender który ma za zadanie wypisać logi na konsole. Następnie umieszczamy appender jako argument funkcji configure i to wszystko.

A co jeśli chcemy zmienić poziom wypisywania logów?

Nie ma problemu, dodajemy na końcu przykładowo:

    logger.setLevel(Level.TRACE);

A jeśli chcemy, aby logi były wypisywane do pliku?

Nie ma problemu, linijkę Appender app1 = new ConsoleAppender(lay1); możemy przykładowo zastąpić w poniższy sposób:

    Appender app1 = null;
    try {
            app1 = new FileAppender(lay1,"C:/log_express.txt");
    } catch(IOException ex) {

    }

No dobrze, wspomniałem o tym, że można dokonać konfiguracji także przy użyciu pliku properties oraz w formacie xml. Zasada działania jest podobna: umieszczamy dany plik o nazwie log4j.properties lub log4j.xml w dowolnym pakiecie (jeśli będzie to domyślny pakiet, wówczas nie będziemy musieli bawić się w określanie gdzie system ma szukać pliku konfiguracji, gdyż odbędzie się to automatycznie).

Przykładowa zawartość pliku właściwości:

    log4j.appender.C=org.apache.log4j.ConsoleAppender
    log4j.appender.C.layout=org.apache.log4j.PatternLayout
    log4j.appender.C.layout.ConversionPattern=[%p] %c - %m - Data wpisu: %d %n
    log4j.rootLogger=DEBUG, C

Decyzja z jakich metod będziecie korzystać zależy od was.

Przykładowa zawartość pliku xml:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
      <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" value="[%p] %c - %m - Data wpisu: %d %n"/>
        </layout>
      </appender>
      <root>
        <priority value ="debug" />
        <appender-ref ref="console" />
      </root>
    </log4j:configuration>

Konfiguracja w obu przypadkach powoduje takie samo działanie. Różnica jest tylko w sposobie zapisu, a decyzja z jakich metod będziecie korzystać zależy od was.

Warto wspomnieć, że jeśli nie dokonamy konfiguracji Log4j pojawi się stosowne ostrzeżenie, a logi nie będą wpisywane.

Jak skorzystać z Log4j w Netbeansie ?

Oto przykładowy sposób:

Log4j versus Java Logging API

Przy pisaniu wielu programów z wykorzystaniem Log4j spotkałem się z opiniami w stylu:

"Przecież istnieje już coś takiego jak Java Logging API, która jest wspierana przez SUN Microsystems."

Zgadza się. Nie oznacza to jednak, że jeśli coś jest standardem to znaczy, że jest lepsze, wręcz przeciwnie. Java Logging API powstała później niż Log4j i w dużej mierze się na niej opiera.

Choć różnic jest niewiele to wśród programistów Javy otarła się opinia, że to co możemy wykonać za pomocą Java Logging API jest także możliwe przy wykorzystaniu Log4j, a nawet więcej.

Tworzenie logów w inteligentny sposób
znacząco wspomaga tworzenie oprogramowania.

Plusy i minusy obu bibliotek zebrałem w poniższej tabelce:

Log4j

Java Logging API

+

Wspierany przez Apache

Dłużej na rynku

Używany przez ogromną społeczność

-

Rzadko aktualizowany

+

Wspierane przez SUN

Nie trzeba dodawać nowych bibliotek

 

-

Wybierane przez mniejszą liczbę programistów

Poza wyraźnymi plusami wspólną wadą może być to, że przy bardzo dużych logach można mieć problemy z dostrzeżeniem tych najistotniejszych dla nas informacji. Na świecie to zjawisko jest określane mianem scrolling-blindness.

Podsumowanie

Możliwości jakie posiada biblioteka Log4j są ogromne. Jest ona łatwa w użyciu i co ważne – na otwartym kodzie. Była optymalizowana pod względem szybkości działania. Nie ma także obaw o to jak będzie funkcjonować w aplikacjach wielowątkowych. Tworzenie logów w inteligentny sposób znacząco wspomaga tworzenie oprogramowania. W następnym numerze umieszczę kilka przykładowych kodów źródłowych pokazujących jak efektownie i efektywnie korzystać z Log4j w różnego typu aplikacjach. Pamiętajcie: nigdy więcej System.out.print :)

Nie ma jeszcze komentarzy.

Tylko zalogowani użytkowincy mogą pisać komentarze

Developers World