Przewodnik dla programistów Java

Jedną z najważniejszych części procesu tworzenia oprogramowania jest odpowiednie logowanie. Ponieważ dostępnych jest wiele różnych struktur rejestrowania Java, ważne jest, aby wybrać taką, która jest łatwa w użyciu. Jednocześnie wybrana platforma powinna charakteryzować się wysoką wydajnością, rozszerzalnymi funkcjami i umożliwiać dostosowywanie. Log4j2 to bezpłatna biblioteka rejestrowania Java, która spełnia wszystkie wymagania.

Integracja Log4j2 z dowolną aplikacją odblokowuje opcje, takie jak zaawansowane filtrowanie, obsługa lambda Java 8, wyszukiwanie właściwości i niestandardowe poziomy dziennika. Przyjrzyjmy się, jak możesz dodać Log4j2 do swoich projektów i jakie funkcje mogą pomóc Ci pozostać na szczycie gry.

Co to jest Log4j2?

Rejestrowanie to metoda przechwytywania przydatnych informacji, zwanych dziennikami, do których można się później odwoływać i analizować. Możesz użyć dzienników do szybkiego debugowania kodu aplikacji. Dzienniki aplikacji pomagają zrozumieć przepływ kodu i rozwiązywać problemy i błędy produkcyjne.

Poza diagnostycznymi przypadkami użycia, dzienniki są również wykorzystywane do celów audytu — na przykład do śledzenia, czy wiadomość z powiadomieniem została pomyślnie wysłana do użytkownika.

Log4j2 to jedna z najpopularniejszych bibliotek rejestrowania w Javie. Jest następcą bardzo wpływowej biblioteki Log4j. Opracowany przez Apache Software Foundation i część Apache Logging Services, Log4j2 jest darmowym i otwartym oprogramowaniem (FOSS) dystrybuowanym na licencji Apache w wersji 2.0.

Log4j2 jest zbudowany na solidnych podstawach oryginalnego Log4j. Korzystanie z programu Logger ma przewagę nad prostymi instrukcjami drukowania w System.out.println(). Obejmuje to kontrolę nad tym, które komunikaty mają się pojawiać, przy jednoczesnym unikaniu innych komunikatów dziennika. Posiadanie odpowiednich dzienników ma kluczowe znaczenie w środowisku produkcyjnym, w którym debuggery nie są dostępne.

Jak dodać Log4j2 do swojego projektu?

Istnieje kilka sposobów dodania Log4j2 do projektu Java. Wskazane jest, aby być na Javie 8 lub nowszej, aby móc korzystać ze wszystkich funkcji Log4j2.

Omówmy różne metody dodawania Log4j2 w zależności od wymagań, jakie możesz mieć.

Dodawanie Log4j2 do projektów przy użyciu Apache Maven

Jeśli Twój projekt używa Apache Maven jako systemu kompilacji, zależności Log4j2 należy dodać do pliku pom.xml.

<dependencies>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.20.0</version>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.20.0</version>
  </dependency>
</dependencies>

Aby ułatwić utrzymanie tej samej wersji w różnych artefaktach, Log4j2 ma plik zestawienia materiałów (BOM) pom.xml. Jeśli dodasz go w ramach zarządzania zależnościami, nie musisz dodawać poszczególnych wersji.

<!-- Add the BOM to the dependencyManagement -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-bom</artifactId>
      <version>2.20.0</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

<!-- Once the BOM is added, the versions are not required -->
<dependencies>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
  </dependency>
</dependencies>

Dodawanie Log4j2 do projektów przy użyciu Apache Gradle

Jeśli używasz Apache Gradle jako narzędzia do budowania, możesz dodać zależności Log4j2 do pliku build.gradle.

dependencies {
  implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
  implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
}

Jeśli korzystasz z Gradle w wersji 5.0 lub nowszej, masz możliwość zaimportowania zestawienia materiałów (BOM) Log4j2 Maven w celu zachowania spójnych wersji zależności. Można to osiągnąć, dodając następujący element do pliku build.gradle.

dependencies {
  implementation platform('org.apache.logging.log4j:log4j-bom:2.20.0')

  implementation 'org.apache.logging.log4j:log4j-api'
  runtimeOnly 'org.apache.logging.log4j:log4j-core'
}

W Gradle w wersjach 2.8-4.10 nie ma opcji bezpośredniego importu BOM Maven. Musisz dodać dodatkową wtyczkę dla funkcji zarządzania zależnościami.

plugins {
  id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

dependencyManagement {
  imports {
    mavenBom 'org.apache.logging.log4j:log4j-bom:2.20.0'
  }
}

dependencies {
  implementation 'org.apache.logging.log4j:log4j-api'
  runtimeOnly 'org.apache.logging.log4j:log4j-core'
}

Dodawanie Log4j2 do samodzielnych aplikacji bez narzędzia do budowania

Jeśli Twój projekt nie ma narzędzia do budowania, możesz pobrać wymaganą wersję artefaktu Log4j2 z oficjalnej strony pobierania Log4j2.

Po ich pobraniu musisz się upewnić, że ścieżka klas Twojej aplikacji zawiera następujące słoiki.

  • log4j-api-2.20.0.jar
  • log4j-core-2.20.0.jar

Jakie są komponenty w Log4j2?

Aby zrozumieć funkcje Log4j2 i w pełni wykorzystać jego możliwości, ważne jest, aby zrozumieć, jak działa Log4j2. Pod powierzchnią kilka bloków budulcowych tworzy Log4j2. Porozmawiajmy o nich jeden po drugim.

# 1. LoggerContext

LoggerContext jest jednostką centralną systemu rejestrowania. Zawiera wszystkie rejestratory żądane w aplikacji. Zawiera również odniesienie do konfiguracji.

#2. Konfiguracja

Konfiguracja zawiera wszystkie informacje wymagane przez system logowania. Obejmuje to rejestratory, programy dołączające, filtry i inne. W Log4j2 możesz zdefiniować konfigurację przy użyciu różnych formatów plików, takich jak XML, JSON i YAML, a także programowo za pośrednictwem interfejsu API Log4j2.

Automatyczne przeładowanie odbywa się za każdym razem, gdy zmienia się jakakolwiek właściwość w konfiguracji. W związku z tym nie ma wymogu ponownego uruchomienia aplikacji.

#3. Rejestrator

Głównym elementem systemu Log4j2 jest Logger. Loggery są uzyskiwane wewnątrz kodu aplikacji za pomocą instrukcji LogManager.getLogger() i służą do generowania logów. Komunikaty dziennika mogą być generowane na różnych poziomach istotności, takich jak debugowanie, informacje, ostrzeżenie, błąd i fatalne.

#4. LoggerConfig

LoggerConfig jest odpowiedzialny za zachowanie określonego Loggera. Definiuje zachowanie i ustawienia rejestrowania zdarzeń generowanych przez ten konkretny rejestrator. Pozwala na konfigurację różnych poziomów rejestrowania, konfigurowanie dołączaczy i stosowanie filtrów.

#5. Filtr

Możesz selektywnie przetwarzać zdarzenia dziennika w Log4j2 za pomocą filtrów. Filtry są stosowane na podstawie określonych kryteriów. Filtry te można zastosować do rejestratorów lub programów dołączających. Filtry kontrolują, które zdarzenia dziennika mogą przechodzić przez potok rejestrowania w celu dalszego przetwarzania. Za pomocą filtrów zachowanie rejestrowania można precyzyjnie dostosować, zapewniając, że przetwarzane są tylko odpowiednie dzienniki.

#6. Dodatek

Miejsce docelowe każdego komunikatu dziennika jest określane przez program dołączający. Pojedynczy rejestrator może mieć wielu Appenderów. Zdarzenie dziennika zostanie wysłane do wszystkich Appenderów dla danego rejestratora. Log4j2 ma wiele wstępnie skonfigurowanych programów dołączających. Na przykład ConsoleAppender służy do rejestrowania komunikatów w konsoli, a FileAppender służy do wysyłania komunikatów do pliku. Każdy Appender potrzebuje własnego układu, który określa, jak będzie wyglądać końcowy komunikat dziennika.

#7. Układ

W Log4j2 Layout służy do określenia, jak będzie wyglądać końcowy komunikat dziennika. Layout jest powiązany z Appenderem. Podczas gdy Appender określa miejsce docelowe danych wyjściowych, Layout opisuje, w jaki sposób wiadomość zostanie wydrukowana.

5 najważniejszych cech Log4j2

Log4j2 jest bogaty w funkcje i właśnie to odróżnia go od innych dostępnych platform rejestrowania Java. Od posiadania asynchronicznych rejestratorów do obsługi lambda Java 8, Log4j2 ma przewagę nad innymi. Omówmy niektóre z godnych uwagi cech tego frameworka.

# 1. Rozszerzanie funkcjonalności za pomocą wtyczek

W Log4j 1.x tworzenie rozszerzeń wymagało wielu modyfikacji kodu. Log4j2 rozwiązuje problem rozszerzalności, wprowadzając system wtyczek.

Możesz zadeklarować nową wtyczkę, używając adnotacji @Plugin w swojej klasie. Korzystając z mocy wtyczek, możesz tworzyć własne komponenty, takie jak filtry i dodatki. Do biblioteki można również łatwo dodawać komponenty innych firm.

#2. Wsparcie dla Javy 8 Lambda

Wraz z wydaniem Log4j2 w wersji 2.4 wprowadzono obsługę wyrażeń lambda języka Java 8. Za pomocą wyrażeń lambda można zdefiniować logikę rejestrowania bezpośrednio. Zmniejsza to potrzebę sprawdzania wielu wierszy lub anonimowych klas wewnętrznych. Zapewnia to również, że drogie metody nie są wykonywane niepotrzebnie. W ten sposób kod jest nie tylko czystszy i łatwiejszy do odczytania, ale także zmniejsza się obciążenie systemu.

Rozważmy przykład, w którym rejestrujesz wynik kosztownej operacji, ale tylko wtedy, gdy włączony jest poziom debugowania. Przed obsługą lambd byłoby to wykonywane przy użyciu poniższego kodu:

if (logger.isDebugEnabled()) {
    logger.debug("The output of the given operation is: {}", expensiveOperation());
}

Posiadanie wielu takich przypadków użycia niepotrzebnie wprowadziłoby kontrole warunkowe. Jednak w przypadku Log42 to samo działanie można wykonać w następujący sposób:

logger.debug("The output of the given operation is: {}", () -> expensiveOperation()

Metoda exprensiveOperation() jest oceniana tylko wtedy, gdy włączony jest poziom debugowania. Nie ma potrzeby przeprowadzania żadnych wyraźnych kontroli.

#3. Rejestratory asynchroniczne

Każde zdarzenie dziennika jest operacją we/wy, co zwiększa obciążenie systemu. Aby temu zaradzić, Log4j2 wprowadza rejestratory asynchroniczne, które działają w oddzielnym wątku od wątku aplikacji. Podczas korzystania z rejestratorów asynchronicznych wątek wywołujący natychmiast odzyskuje kontrolę po wywołaniu metody logger.log() .

Dzięki temu może kontynuować logikę aplikacji zamiast czekać na zakończenie zdarzenia rejestrowania. Wykorzystanie tego zachowania asynchronicznego zapewnia większą przepływność rejestrowania. Możesz wybrać, aby wszystkie rejestratory były domyślnie asynchroniczne lub mieć połączenie zachowania synchronicznego i asynchronicznego.

#4. Logowanie bez śmieci

W Javie wyrzucanie elementów bezużytecznych to proces, w którym nieużywane obiekty w aplikacji są automatycznie czyszczone. Chociaż nie musisz ręcznie zajmować się tą operacją, wyrzucanie elementów bezużytecznych ma swój własny narzut.

Jeśli aplikacja utworzy zbyt wiele obiektów w krótkim czasie, proces wyrzucania elementów bezużytecznych może zająć więcej zasobów systemowych niż to konieczne. Kilka bibliotek rejestrowania, w tym poprzednie wersje Log4j, tworzy wiele obiektów tymczasowych podczas procesu rejestrowania. Następnie zwiększone ciśnienie na moduł wyrzucania elementów bezużytecznych wpływa na wydajność systemu.

Od wersji 2.6 Log4j2 działa w trybie „wolnym od śmieci”. To jest zachowanie domyślne. Stąd przedmioty są ponownie wykorzystywane, a tworzenie tymczasowych jest znacznie ograniczone.

Poniższe obrazy pokazują, jak wersja 2.6 oprogramowania Log4j2 ogranicza problem zbędnych obiektów w porównaniu z wersją 2.5 programu Log4j2.

W Log4j2 w wersji 2.5 podczas procesu logowania tworzonych jest wiele obiektów tymczasowych; źródło: apache.org

W Log4j2.6 nie ma obiektów tymczasowych tworzonych podczas procesu logowania; źródło: apache.org

#5. Wyszukiwania

W log4j2 możesz dodawać informacje kontekstowe do swoich dzienników za pomocą odnośników. Korzystając z nich, możesz dodawać dane z różnych źródeł, takich jak właściwości systemu, zmienne środowiskowe lub wartości zdefiniowane przez użytkownika. W ten sposób możesz dołączyć istotne informacje, które są pobierane dynamicznie, dzięki czemu dzienniki są bardziej przydatne.

Rozważmy przykład, w którym chcesz rejestrować identyfikator sesji użytkownika ze wszystkimi wierszami dziennika. Umożliwiłoby to wyszukanie wszystkich dzienników odpowiadających identyfikatorowi sesji.

Brutalnym sposobem na zrobienie tego byłoby jawne dodanie identyfikatora sesji indywidualnie, co staje się trudne do utrzymania. Wkrótce możesz zapomnieć go dodać, tracąc w ten sposób cenne informacje.

logger.info("The user data has been fetched for session id {}", sessionId);
...
logger.info("The transaction has been processed for session id {}", sessionId);
...
logger.info("Request has been successfully processed for session id {}", sessionId);

Lepszym sposobem na to byłoby użycie wyszukiwania mapy kontekstowej. Identyfikator sesji można dodać do kontekstu wątku w kodzie aplikacji. Wartość może być następnie użyta w konfiguracji Log4j2. W ten sposób wyeliminowana jest potrzeba wyraźnego wspominania o tym w komunikatach dziennika.

ThreadContext.put("sessionId", sessionId);

Po dodaniu wartości można jej użyć w wyszukiwaniu za pomocą słowa kluczowego ctx.

<File name="Application" fileName="application.log">
  <PatternLayout>
    <pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern>
  </PatternLayout>
</File>

Jak tworzyć niestandardowe poziomy dziennika w Log4j2?

Poziomy rejestrowania w Log4j2 służą do kategoryzowania zdarzeń dziennika na podstawie ich ważności lub ważności. Możesz kontrolować poziom rejestrowania podczas rejestrowania komunikatu w kodzie aplikacji.

Na przykład logger.debug() dodaje poziom DEBUG. Odpowiednio, logger.error() dodaje poziom ERROR. To określa, które komunikaty ostatecznie pojawią się w danych wyjściowych. Poziom rejestrowania można skonfigurować w pliku konfiguracyjnym.

Wstępnie skonfigurowane poziomy dziennika w Log4j2 i odpowiadające im wartości są wymienione poniżej.

OFF0FATAL100ERROR200WARN300INFO400DEBUG500TRACE600ALLMAX WARTOŚĆ

Jeśli Poziom dziennika jest ustawiony na określony poziom, wyprowadzane są wszystkie wiersze dziennika dla tej odpowiedniej wartości i te powyżej (z mniejszą wartością). Pozostałe są ignorowane.

Na przykład, jeśli ustawisz poziom rejestrowania na OSTRZEŻENIE, zostaną wyświetlone komunikaty OSTRZEŻENIE, BŁĄD i KRYTYCZNY. Każda linia dziennika z innym poziomem zostanie zignorowana. Jest to szczególnie przydatne, gdy uruchamiasz ten sam kod w różnych środowiskach.

Możesz chcieć ustawić poziom dziennika na INFO lub DEBUG podczas uruchamiania kodu w swoim środowisku programistycznym. Umożliwi to zobaczenie większej liczby logów i pomoże w procesie programowania. Jednak podczas uruchamiania w środowisku produkcyjnym chciałbyś ustawić go na BŁĄD. W ten sposób będziesz mógł skupić się na znalezieniu problemu w przypadku wystąpienia jakiejkolwiek anomalii i nie będziesz musiał przechodzić przez niepotrzebne wiersze dziennika.

Może się zdarzyć, że oprócz wstępnie skonfigurowanych poziomów będziesz chciał dodać swój własny poziom dziennika niestandardowego. Log4j2 pozwala to łatwo zrobić. Zobaczmy, jak możesz dodawać własne poziomy dziennika i używać ich w swojej aplikacji.

# 1. Dodawanie niestandardowego poziomu dziennika przy użyciu pliku konfiguracyjnego

Możesz dodać niestandardowe poziomy rejestrowania, deklarując je w pliku konfiguracyjnym.

W poniższym przykładzie zdefiniowano niestandardowy poziom dziennika o nazwie NOTYFIKACJA z wartością 450. Spowoduje to umieszczenie go między INFO (z wartością 400) a DEBUG (z wartością 500). Oznacza to, że jeśli poziom jest ustawiony na NOTYFIKACJA, to komunikaty INFO będą rejestrowane, ale komunikaty DEBUG będą pomijane.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <CustomLevels>
    <CustomLevel name="NOTICE" intLevel="450" />
  </CustomLevels>
 
  <Appenders>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
    </File>
  </Appenders>
  <Loggers>
    <Root level="trace">
      <AppenderRef ref="MyFile" level="NOTICE" />
    </Root>
  </Loggers>
</Configuration>

#2. Dodanie niestandardowego poziomu dziennika w kodzie

Oprócz zadeklarowania ich w pliku konfiguracyjnym możesz zdefiniować własne niestandardowe poziomy dziennika w swoim kodzie.

final Level VERBOSE = Level.forName("VERBOSE", 550);

Spowoduje to utworzenie nowego poziomu dziennika o nazwie VERBOSE. Ten poziom dziennika będzie mieścił się między DEBUG (z wartością 500) a TRACE (z wartością 600). Jeśli rejestrator jest ustawiony na poziom VERBOSE, wszystkie komunikaty dziennika VERBOSE i wyższe będą rejestrowane, w tym DEBUG. Jednak komunikaty TRACE zostaną pominięte.

#3. Używanie niestandardowego poziomu dziennika w kodzie

Niestandardowe poziomy rejestrowania muszą być najpierw zadeklarowane przed ich użyciem. Możesz je zadeklarować w pliku konfiguracyjnym lub w swoim kodzie. Po zadeklarowaniu możesz z nich swobodnie korzystać.

Ten przykład kodu pokazuje, jak można zadeklarować niestandardowy poziom o nazwie UWAGA, a następnie użyć tego samego.

final Level NOTICE = Level.forName("NOTICE", 550);

final Logger logger = LogManager.getLogger();
logger.log(NOTICE, "a notice level message");

Chociaż spowoduje to wygenerowanie wymaganego komunikatu z nowo utworzonym poziomem, jawne przekazywanie poziomu zawsze może być kłopotliwe. Na szczęście możesz wygenerować kod źródłowy, aby uzyskać metody pomocnicze do rejestrowania niestandardowych poziomów. Używając tego samego, będziesz mógł użyć własnej metody logger.notice() podobnie jak w przypadku logger.debug() lub logger.error().

Log4j2 jest dostarczany z narzędziem, które pomaga tworzyć własne rozszerzone rejestratory. Następujące polecenie tworzy plik Java o nazwie CustomLogger.java. Ten plik zawiera istniejące metody dziennika wraz z nowo wygenerowanymi metodami dla poziomu NOTYFIKACJA.

java -cp log4j-core-2.20.0.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator \
        com.example.CustomLogger NOTICE=450 > com/example/CustomLogger.java

Po wygenerowaniu pliku możesz użyć klasy w swoim kodzie do tworzenia nowych rejestratorów. Te rejestratory będą zawierać dodatkowe metody dla niestandardowego poziomu dziennika. W ten sposób możesz rozszerzyć funkcjonalność swoich rejestratorów.

final Logger logger = CustomLogger.create(ValueFirstSmsSender.class);

//this new method is similar to using logger.debug()
logger.notice("a notice level message");

Wniosek

Log4j2 to bardzo wydajna platforma rejestrowania Java, która oferuje szeroki zakres funkcji, konfiguracji, ulepszeń wydajności i nie tylko. Ponieważ logi są bardzo ważną częścią procesu tworzenia oprogramowania, posiadanie solidnej struktury, takiej jak Log4j2, zwiększa możliwości aplikacji.

Elastyczność i rozszerzalność Log4j2 pozwala na właściwe przechwytywanie zdarzeń zachodzących w Twojej aplikacji. Następnie umożliwia myślenie o dziennikach jako o potężnym narzędziu do debugowania i audytu. Dzięki wszystkim swoim funkcjom i ulepszeniom Log4j2 wyróżnia się i jest preferowanym wyborem w różnorodnych projektach oprogramowania.

Mogą Cię również zainteresować te środowiska Java IDE i kompilatory online.