JavaRush /Blog Java /Random-PL /Logowanie w Javie: co, jak, gdzie i czym?
Roman Beekeeper
Poziom 35

Logowanie w Javie: co, jak, gdzie i czym?

Opublikowano w grupie Random-PL
Witam wszystkich, społeczność JavaRush! Dzisiaj porozmawiamy o logowaniu w Javie:
  1. Co to jest, dlaczego to jest. W jakich przypadkach lepiej jest używać, w jakich przypadkach nie?
  2. Jakie rodzaje implementacji rejestrowania występują w Javie i co powinniśmy zrobić z tą różnorodnością?
  3. Poziomy rejestrowania. Porozmawiajmy, czym jest appender i jak go poprawnie skonfigurować.
  4. Logowanie węzłów i jak je poprawnie skonfigurować, aby wszystko działało tak jak chcemy.
Materiał ten przeznaczony jest dla szerokiego grona odbiorców. Będzie to jasne zarówno dla tych, którzy dopiero zapoznają się z Javą, jak i dla tych, którzy już pracują, ale zorientowali się dopiero w logger.info(“log something”); Let's Go!

Dlaczego rejestrowanie jest potrzebne?

Przyjrzyjmy się rzeczywistym przypadkom, w których logowanie rozwiązałoby problem. Oto przykład z mojej pracy. Istnieją punkty aplikacji, które integrują się z innymi usługami. Rejestrowanie tych punktów traktuję jako „alibi” : jeśli integracja nie zadziała, łatwo będzie ustalić, po której stronie powstał problem. Wskazane jest także rejestrowanie ważnych informacji zapisywanych w bazie danych. Na przykład utworzenie użytkownika administratora. To jest dokładnie to, co dobrze byłoby zarejestrować.

Narzędzia do rejestrowania Java

Rejestrowanie: co, jak, gdzie i czym?  - 2Do znanych rozwiązań logowania w Javie zaliczają się:
  • log4j
  • LIPIEC - java.util.logging
  • JCL – logowanie w Dżakarcie Commons
  • Logowanie
  • SLF4J - prosta fasada logowania dla Java
Rzućmy okiem na każdy z nich, a w praktycznej części materiału za podstawę przyjmiemy połączenie Slf4j - log4j . Może się to teraz wydawać dziwne, ale nie martw się: pod koniec artykułu wszystko będzie jasne.

System.err.println

Początkowo oczywiście był System.err.println (wyjście zapisu do konsoli). Nadal jest używany do szybkiego uzyskania dziennika podczas debugowania. Oczywiście nie ma co tu mówić o jakichkolwiek ustawieniach, więc po prostu o tym pamiętajmy i przejdźmy dalej.

Log4j

Było to już pełnoprawne rozwiązanie, które powstało na potrzeby deweloperów. Okazało się, że jest to naprawdę ciekawe narzędzie. Rozwiązanie to z różnych powodów nigdy nie trafiło do JDK, co bardzo zdenerwowało całą społeczność. log4j miał opcje konfiguracyjne, dzięki którym rejestrowanie mogło być włączone w pakiecie com.example.typei wyłączone w podpakietze com.example.type.generic. Umożliwiło to szybkie oddzielenie tego, co należało zarejestrować, od tego, co nie było potrzebne. Należy tutaj zauważyć, że istnieją dwie wersje log4j: 1.2.x i 2.x.x, które nie są ze sobą kompatybilne . log4j dodał taką koncepcję jak appender , czyli narzędzie za pomocą którego zapisywane są logi i układ - formatowanie logów. Dzięki temu możesz nagrywać tylko to, czego potrzebujesz i jak tego potrzebujesz. Porozmawiamy więcej o programie dołączającym nieco później.

LIPIEC - java.util.logging

Jedną z kluczowych zalet jest rozwiązanie - JUL znajduje się w pakiecie JDK (Java Development Kit). Niestety, w trakcie jego rozwoju za podstawę przyjęto nie popularny log4j, a rozwiązanie IBM, które miało wpływ na jego rozwój. Właściwie w tej chwili jest JUL, ale nikt z niego nie korzysta. Od „tak sobie”: w JUL poziomy logowania różnią się od tego, co jest w Logback, Log4j, Slf4j, co pogarsza zrozumienie między nimi. Tworzenie rejestratora przebiega mniej więcej podobnie. Aby to zrobić, musisz zaimportować:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Nazwa klasy jest przekazywana specjalnie, aby wiedzieć, skąd pochodzi rejestrowanie. Od wersji Java 8 możliwe jest przekazywanie plików Supplier<String>. Pomaga to policzyć i utworzyć ciąg tylko w momencie, gdy jest to naprawdę potrzebne, a nie za każdym razem, jak to było wcześniej. Dopiero wraz z wydaniem Java 8 programiści rozwiązali ważne problemy, po czym JUL naprawdę stał się użyteczny. Mianowicie metody z argumentami, Supplier<String> msgSupplierjak pokazano poniżej:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL – logowanie w Dżakarcie Commons

W związku z tym, że przez długi czas nie było branżowego standardu w logowaniu i był okres, kiedy wiele osób tworzyło swój własny, niestandardowy rejestrator, zdecydowali się wypuścić JCL - wspólne opakowanie, które będzie używane nad innymi. Dlaczego? Kiedy do projektu dodano pewne zależności, mogły one użyć innego rejestratora niż rejestrator w projekcie. Z tego powodu dodano je do projektu przejściowo, co stwarzało poważne problemy przy próbie złożenia tego wszystkiego w jedną całość. Niestety wrapper był bardzo ubogi pod względem funkcjonalnym i nie wprowadzał żadnych dodatków. Prawdopodobnie byłoby wygodnie, gdyby wszyscy używali JCL do wykonywania swojej pracy. Ale w rzeczywistości tak nie było, więc używanie JCL nie jest obecnie dobrym pomysłem.

Logowanie

Jak trudna jest ścieżka open source... Logback został napisany przez tego samego programistę co log4j, aby stworzyć jego następcę. Pomysł był taki sam jak log4j. Różnice polegały na tym, że w logbacku:
  • poprawiona wydajność;
  • dodano natywną obsługę slf4j;
  • Rozszerzono opcję filtrowania.
Domyślnie logback nie wymaga żadnych ustawień i rejestruje wszystkie logi z poziomu DEBUG wzwyż. Jeśli konieczna jest konfiguracja, można to zrobić za pomocą konfiguracji XML:
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <encoder>
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
        </encoder>
    </appender>
    <logger name="org.hibernate.SQL" level="DEBUG" />
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
    <root level="info">
        <appender-ref ref="FILE" />
    </root>
</configuration>

SLF4J - prosta fasada logowania dla Java

Około 2006 roku jeden z założycieli log4j opuścił projekt i stworzył slf4j – Simple Logging Facade for Java – opakowanie wokół log4j, JUL, common-loggins i logback. Jak widać postęp doszedł do tego, że na opakowaniu utworzono wrapper... Co więcej, jest on podzielony na dwie części: API, które jest wykorzystywane w aplikacji oraz implementację, która jest dodawana jako oddzielne zależności dla każdego typu rejestrowania. Na przykład, slf4j-log4j12.jar, slf4j-jdk14.jar. Wystarczy podłączyć odpowiednią implementację i gotowe – cały projekt będzie z nią współpracował. Slf4j obsługuje wszystkie nowe funkcje, takie jak formatowanie ciągów do rejestrowania. Wcześniej był taki problem. Załóżmy, że istnieje wpis w dzienniku:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
W obiekcie usernastępuje niejawna konwersja user.toString()wynikająca z łączenia ciągów znaków, co wymaga czasu i spowalnia system. I wszystko jest ok jeśli odbugujemy aplikację. Problemy zaczynają się, jeśli poziom logowania dla tej klasy to INFO i wyższy. Oznacza to, że nie należy zapisywać tego dziennika i nie należy wykonywać łączenia ciągów znaków. Teoretycznie o tym powinna decydować sama biblioteka rejestrująca. Co więcej, okazało się to największym problemem pierwszej wersji log4j. Nie dostarczyli normalnego rozwiązania, ale zasugerowali zrobienie tego w ten sposób:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Oznacza to, że zamiast jednej linii logowania zasugerowali napisanie 3(!). Logowanie powinno minimalizować zmiany w kodzie, a trzy linijki wyraźnie zaprzeczały ogólnemu podejściu. slf4j nie miał problemów ze zgodnością z JDK i API, więc natychmiast pojawiło się piękne rozwiązanie:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
gdzie {}oznaczają wstawki argumentów przekazanych w metodzie. Oznacza to, że pierwszy {}odpowiada user, drugi {}- request.getRemoteAddr(). Z tego powodu, tylko jeśli poziom logowania pozwala na logowanie, komunikat ten można połączyć w jeden. Po tym, SJF4J szybko zyskał na popularności i jest obecnie najlepszym rozwiązaniem. Dlatego logowanie rozważymy na przykładzie pakietu slf4j-log4j12.

Co należy zarejestrować

Oczywiście nie należy rejestrować wszystkiego. Czasami jest to niepotrzebne, a nawet niebezpieczne. Jeśli np. zastawisz czyjeś dane osobowe i w jakiś sposób wyjdzie to na jaw, pojawią się realne problemy, szczególnie w przypadku projektów skierowanych na Zachód. Ale jest też coś, co jest obowiązkowe do logowania :
  1. Początek/koniec aplikacji. Musimy wiedzieć, że aplikacja faktycznie uruchomiła się zgodnie z oczekiwaniami i zakończyła się zgodnie z oczekiwaniami.
  2. Pytania bezpieczeństwa. Tutaj dobrze byłoby rejestrować próby odgadnięcia hasła, rejestrować logowania ważnych użytkowników itp.
  3. Niektóre stany aplikacji . Na przykład przejście z jednego stanu do drugiego w procesie biznesowym.
  4. Kilka informacji do debugowania , z odpowiednim poziomem logowania.
  5. Niektóre skrypty SQL. Istnieją realne przypadki, gdy jest to potrzebne. Ponownie, umiejętnie dostosowując poziomy, można osiągnąć doskonałe rezultaty.
  6. Wykonane wątki (Thread) mogą być logowane w przypadkach sprawdzenia poprawności działania.

Popularne błędy w logowaniu

Istnieje wiele niuansów, ale oto kilka typowych błędów:
  1. Nadmierne rejestrowanie. Nie powinieneś rejestrować każdego kroku, który teoretycznie mógłby być ważny. Istnieje zasada: logi mogą obciążać wydajność o nie więcej niż 10%. W przeciwnym razie wystąpią problemy z wydajnością.
  2. Logowanie wszystkich danych do jednego pliku. To w pewnym momencie sprawi, że odczyt/zapis będzie bardzo trudny, nie wspominając o ograniczeniach rozmiaru plików w niektórych systemach.
  3. Używanie nieprawidłowych poziomów rejestrowania. Każdy poziom rejestrowania ma wyraźne granice i należy ich przestrzegać. Jeśli granica jest niejasna, możesz uzgodnić, którego poziomu użyć.

Poziomy rejestrowania

x: Widoczne
FATALNY BŁĄD OSTRZEGAĆ INFORMACJE ODPLUSKWIĆ NAMIERZAĆ WSZYSTKO
WYŁĄCZONY
FATALNY X
BŁĄD X X
OSTRZEGAĆ X X X
INFORMACJE X X X X
ODPLUSKWIĆ X X X X X
NAMIERZAĆ X X X X X X
WSZYSTKO X X X X X X X
Jakie są poziomy rejestrowania? Aby w jakiś sposób uszeregować kłody, konieczne było nadanie im pewnych oznaczeń i wyróżnień. W tym celu wprowadzono poziomy logowania. Poziom ustawia się w aplikacji. Jeżeli wpis należy do poziomu niższego od wyznaczonego, nie jest wprowadzany do dziennika. Na przykład mamy logi, które służą do debugowania aplikacji. W normalnej pracy produkcyjnej (kiedy aplikacja jest wykorzystywana zgodnie z jej przeznaczeniem) takie logi nie są potrzebne. Dlatego poziom rejestrowania będzie wyższy niż w przypadku debugowania. Przyjrzyjmy się poziomom na przykładzie log4j. Inne rozwiązania, z wyjątkiem JUL, wykorzystują te same poziomy. Oto one w kolejności malejącej:
  • WYŁ.: nie są zapisywane żadne logi, wszystkie zostaną zignorowane;
  • FATAL: błąd, po którym aplikacja nie będzie mogła już działać i zostanie zatrzymana, np. błąd braku pamięci JVM;
  • BŁĄD: Poziom błędu, gdy występują problemy wymagające rozwiązania. Błąd nie powoduje zatrzymania aplikacji jako całości. Inne zapytania mogą działać poprawnie;
  • WARN: Wskazuje dzienniki zawierające ostrzeżenie. Wystąpiła nieoczekiwana akcja, mimo to system oparł się i zrealizował żądanie;
  • INFO: dziennik rejestrujący ważne akcje w aplikacji. To nie są błędy, to nie są ostrzeżenia, to są oczekiwane działania systemu;
  • DEBUG: logi potrzebne do debugowania aplikacji. Aby upewnić się, że system robi dokładnie to, czego się od niego oczekuje, lub opisać działanie systemu: „metoda 1 zaczęła działać”;
  • TRACE: dzienniki o niższym priorytecie do debugowania, z najniższym poziomem rejestrowania;
  • WSZYSTKIE: poziom, na którym będą zapisywane wszystkie logi z systemu.
Okazuje się, że jeśli w jakimś miejscu aplikacji zostanie włączony poziom logowania INFO, rejestrowane będą wszystkie poziomy, począwszy od INFO, aż do FATAL. Jeśli poziom logowania jest FATAL, rejestrowane będą tylko logi z tym poziomem.

Rejestrowanie i wysyłanie logów: Appender

Rozważymy ten proces na przykładzie log4j: zapewnia on szerokie możliwości nagrywania/wysyłania logów:
  • do zapisu do pliku - rozwiązanie DailyRollingFileAppender ;
  • do odbioru danych do konsoli aplikacji - ConsoleAppender ;
  • do zapisywania logów do bazy danych - JDBCAppender ;
  • do sterowania transmisją poprzez TCP/IP - TelnetAppender ;
  • aby upewnić się, że rejestrowanie nie wpływa na wydajność - AsyncAppender .
Istnieje kilka innych implementacji: pełną listę można znaleźć tutaj . Nawiasem mówiąc, jeśli wymagany dodatek nie jest dostępny, nie stanowi to problemu. Możesz napisać własny program dołączający, implementując interfejs Appender , który akceptuje tylko log4j.

Węzły rejestrujące

Do demonstracji użyjemy interfejsu slf4j i implementacji z log4j. Utworzenie rejestratora jest bardzo proste: należy napisać w klasie o nazwie MainDemo, w której będzie wykonywane logowanie:
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Spowoduje to utworzenie dla nas rejestratora. Aby dokonać wpisu do dziennika, można skorzystać z wielu metod, które wskazują, na jakim poziomie będą dokonywane wpisy. Na przykład:
logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find log4j.properties. Please, provide them");
logger.error("Connection refused to host = {}", host);
Mimo że przekazujemy klasę, ostatecznie zapisywana jest pełna nazwa klasy wraz z pakietami. Dzieje się tak, aby można było następnie podzielić rejestrowanie na węzły i skonfigurować poziom rejestrowania oraz moduł dołączający dla każdego węzła. Przykładowo nazwa klasy: com.github.romankh3.logginglecture.MainDemo- utworzono w niej rejestrator. I tak można to podzielić na węzły rejestrujące. Głównym węzłem jest null RootLogger . Jest to węzeł, który odbiera wszystkie logi całej aplikacji. Resztę można przedstawić w sposób pokazany poniżej: Rejestrowanie: co, jak, gdzie i czym?  - 4Appendery konfigurują swoją pracę specjalnie na węzłach logowania. Teraz, na przykładzie log4j.properties , przyjrzymy się, jak je skonfigurować.

Konfiguracja krok po kroku Log4j.properties

Teraz skonfigurujemy wszystko krok po kroku i zobaczymy, co da się zrobić:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Ta linia mówi, że rejestrujemy program dołączający CONSOLE, który korzysta z implementacji org.apache.log4j.ConsoleAppender. Ten program dołączający zapisuje dane do konsoli. Następnie zarejestrujmy kolejny appender, który będzie zapisywał do pliku:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Należy pamiętać, że dodatki nadal będą musiały zostać skonfigurowane. Kiedy mamy już zarejestrowane programy dołączające, możemy określić, jaki poziom rejestrowania będzie w węzłach i które programy dołączające będą używane.

log4j.rootLogger=DEBUGOWANIE, KONSOLA, PLIK

  • log4j.rootLogger oznacza, że ​​skonfigurujemy główny węzeł, który będzie zawierał wszystkie logi;
  • po znaku równości pierwsze słowo wskazuje na jakim poziomie i wyższym będą rejestrowane logi (w naszym przypadku jest to DEBUG);
  • następnie po przecinku wskazane są wszystkie dodatki, które zostaną użyte.
Aby skonfigurować konkretny węzeł logowania, należy użyć następującego wpisu:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
gdzie log4j.logger.służy do konfiguracji konkretnego węzła, w naszym przypadku tak. com.github.romankh3.logginglecture. Porozmawiajmy teraz o skonfigurowaniu appendera CONSOLE:
# CONSOLE appender customisation
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
Widzimy tutaj, że możemy ustawić poziom, z którego będzie przetwarzany program dołączający. Rzeczywista sytuacja: komunikat o poziomie info został odebrany przez węzeł logujący i przekazany do przypisanego do niego programu dołączającego, ale program dołączający o poziomie warn i wyższym zaakceptował ten log, ale nic z nim nie zrobił. Następnie musisz zdecydować, jaki szablon będzie w wiadomości. W tym przykładzie używam PatternLayout, ale istnieje wiele rozwiązań. Nie zostaną one ujawnione w tym artykule. Przykład konfiguracji modułu dołączającego FILE:
# File appender customisation
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
Tutaj możesz skonfigurować, do którego pliku będą zapisywane logi, jak widać
log4j.appender.FILE.File=./target/logging/logging.log
Nagranie trafia do pliku logging.log. Aby uniknąć problemów z rozmiarem pliku, możesz ustawić maksimum: w tym przypadku 1 MB. MaxBackupIndex - mówi, ile będzie takich plików. Jeżeli utworzonych zostanie więcej niż ta liczba, pierwszy plik zostanie usunięty. Aby zobaczyć prawdziwy przykład konfiguracji rejestrowania, możesz przejść do otwartego repozytorium w serwisie GitHub.

Skonsolidujmy wynik

Spróbuj zrobić wszystko samodzielnie:
  • Stwórz własny projekt podobny do tego z powyższego przykładu.
  • Jeśli masz wiedzę na temat korzystania z Mavena, skorzystamy z niej; jeśli nie, to tutaj znajduje się link do artykułu opisującego, jak podłączyć bibliotekę.

Podsumujmy to

  1. Rozmawialiśmy o tym, jakie rozwiązania istnieją w Javie.
  2. Prawie wszystkie znane biblioteki logujące zostały napisane pod kontrolą jednej osoby :D
  3. Dowiedzieliśmy się, co należy rejestrować, a co nie.
  4. Ustaliliśmy poziomy rejestrowania.
  5. Zapoznaliśmy się z węzłami rejestrującymi.
  6. Przyjrzeliśmy się, czym jest dodatek i do czego służy.
  7. Krok po kroku skonfigurowaliśmy plik log4j.proterties.

Dodatkowe materiały

  1. JavaRush: Rejestrowanie. Rozwiń kulkę stectrasu
  2. JavaRush: Wykład dotyczący rejestratora
  3. Habr: Rejestrowanie w Javie. Witaj świecie
  4. Habr: Logowanie w Javie: historia koszmaru
  5. Youtube: Kursy Golovach. Logowanie. Część 1 , Część 2 , Część 3 , Część 4
  6. Log4j: dodatek
  7. Log4j: układ
Zobacz także inne moje artykuły:
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION