Průvodce pro vývojáře Java

Jednou z nejdůležitějších částí procesu vývoje softwaru je správné protokolování. Se spoustou různých protokolovacích rámců Java je důležité vybrat ten, který se snadno používá. Zároveň by váš vybraný rámec měl mít vysoký výkon, rozšiřitelné funkce a umožňovat přizpůsobení. Log4j2 je bezplatná knihovna protokolování Java, která zaškrtává všechna políčka.

Integrace Log4j2 s jakoukoli aplikací odemkne možnosti, jako je pokročilé filtrování, podpora Java 8 lambda, vyhledávání vlastností a vlastní úrovně protokolů. Pojďme se podívat na to, jak můžete přidat Log4j2 do svých projektů a jaké funkce vám mohou pomoci zůstat ve hře.

Co je Log4j2?

Protokolování je metoda zachycování užitečných informací, známých jako protokoly, na které lze později odkazovat a analyzovat je. Protokoly můžete použít k rychlému ladění kódu aplikace. Protokoly aplikací pomáhají porozumět toku kódu a řešit produkční problémy a chyby.

Kromě případů použití diagnostiky se protokoly používají také pro účely auditu – například sledování, zda byla uživateli úspěšně odeslána zpráva s upozorněním.

Log4j2 je jednou z nejpopulárnějších knihoven protokolování Java. Je to nástupce velmi vlivné knihovny Log4j. Log4j2, vyvinutý organizací Apache Software Foundation a součástí Apache Logging Services, je bezplatný a otevřený software (FOSS) distribuovaný pod licencí Apache, verze 2.0.

Log4j2 je postaven na pevných základech původního Log4j. Použití Loggeru má oproti jednoduchým tiskovým příkazům System.out.println() výhody. To zahrnuje kontrolu nad tím, které zprávy se mají zobrazovat, a zároveň se vyhnout dalším zprávám protokolu. Mít správné protokoly je zásadní v produkčním prostředí, kde nejsou k dispozici debuggery.

Jak přidat Log4j2 do vašeho projektu?

Existuje několik způsobů, jak přidat Log4j2 do vašeho projektu Java. Abyste mohli využívat všechny funkce Log4j2, je vhodné používat Java 8 nebo vyšší.

Pojďme diskutovat o různých metodách, kterými můžete přidat Log4j2 v závislosti na požadavcích, které můžete mít.

Přidání Log4j2 do projektů pomocí Apache Maven

Pokud váš projekt používá jako systém sestavení Apache Maven, je třeba do souboru pom.xml přidat závislosti Log4j2.

<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 bylo snazší udržovat stejnou verzi napříč různými artefakty, má Log4j2 soubor pom.xml kusovníku (Bill of Material). Pokud jej přidáte pod správu závislostí, nemusíte přidávat jednotlivé verze.

<!-- 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>

Přidání Log4j2 do projektů pomocí Apache Gradle

V případě, že jako nástroj pro sestavení používáte Apache Gradle, můžete do souboru build.gradle přidat závislosti Log4j2.

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

Pokud používáte Gradle verze 5.0 nebo vyšší, máte možnost importovat Log4j2 Maven Bill Of Materials (BOM), abyste zachovali konzistentní verze závislostí. Toho lze dosáhnout přidáním následujícího do souboru 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'
}

Pro verze Gradle 2.8-4.10 není možnost přímého importu kusovníku Maven. Pro funkci správy závislostí musíte přidat další plugin.

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'
}

Přidání Log4j2 do samostatných aplikací bez nástroje pro sestavení

Pokud váš projekt nemá nástroj pro sestavení, můžete si stáhnout požadovanou verzi artefaktu Log4j2 z oficiální stránky stahování Log4j2.

Jakmile si je stáhnete, musíte zajistit, aby třída vaší aplikace obsahovala následující jary.

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

Jaké jsou součásti v Log4j2?

Aby bylo možné porozumět funkcím Log4j2 a využít jeho schopnosti naplno, je důležité pochopit, jak Log4j2 funguje. Pod povrchem tvoří několik stavebních bloků Log4j2. Pojďme si o nich popovídat jeden po druhém.

#1. LoggerContext

LoggerContext je centrální jednotkou logovacího systému. Obsahuje všechny Loggery požadované v aplikaci. Obsahuje také odkaz na konfiguraci.

#2. Konfigurace

Konfigurace obsahuje všechny informace požadované logovacím systémem. To zahrnuje Loggery, Appendery, Filtry a další. V Log4j2 můžete definovat konfiguraci pomocí různých formátů souborů, jako je XML, JSON a YAML, a také programově prostřednictvím Log4j2 API.

Při každé změně vlastnosti v konfiguraci dojde k automatickému opětovnému načtení. Není tedy vyžadováno restartování aplikace.

#3. Logger

Hlavní součástí systému Log4j2 je Logger. Protokoly se získávají v kódu aplikace pomocí příkazu LogManager.getLogger() a používají se ke generování protokolů. Zprávy protokolu lze generovat na různých úrovních závažnosti, jako je ladění, informace, varování, chyba a fatální.

#4. LoggerConfig

LoggerConfig je zodpovědný za chování konkrétního Loggeru. Definuje chování a nastavení pro protokolování událostí generovaných tímto konkrétním loggerem. Umožňuje konfiguraci různých úrovní protokolování, nastavení appenderů a použití filtrů.

#5. Filtr

Události protokolu můžete selektivně zpracovávat v Log4j2 pomocí filtrů. Filtry se používají na základě specifických kritérií. Tyto filtry můžete použít na loggery nebo appendery. Filtry řídí, které události protokolu mohou projít kanálem protokolování k dalšímu zpracování. Pomocí filtrů lze chování protokolování vyladit a zajistit, aby byly zpracovávány pouze relevantní protokoly.

#6. Appender

Cíl každé zprávy protokolu je určen Appenderem. Jeden Logger může mít více Appenderů. Událost protokolu bude odeslána všem Appenderům pro daný Logger. Log4j2 má mnoho předkonfigurovaných appenderů. Například ConsoleAppender se používá k protokolování zpráv do konzoly a FileAppender se používá k výstupu zpráv do souboru. Každý Appender potřebuje své vlastní rozvržení, které určuje, jak bude vypadat výsledná zpráva protokolu.

#7. Rozložení

V Log4j2 se Layout používá k definování toho, jak bude vypadat výsledná zpráva protokolu. Rozvržení je spojeno s Appenderem. Zatímco Appender určuje výstupní cíl, Layout popisuje, jak bude zpráva vytištěna.

Top 5 funkcí Log4j2

Log4j2 je bohatý na funkce a to ho odlišuje od ostatních dostupných protokolovacích rámců Java. Od asynchronních loggerů až po podporu Java 8 lambdas má Log4j2 náskok před ostatními. Pojďme diskutovat o některých pozoruhodných vlastnostech tohoto rámce.

#1. Rozšíření funkcí pomocí pluginů

V Log4j 1.x bylo pro vytvoření rozšíření vyžadováno mnoho úprav kódu. Log4j2 řeší problém rozšiřitelnosti zavedením systému Plugin.

Nový plugin můžete deklarovat pomocí anotace @Plugin ve vaší třídě. S využitím síly zásuvných modulů můžete vytvářet své vlastní komponenty, jako jsou filtry a doplňky. Do knihovny lze také snadno přidat komponenty třetích stran.

#2. Podpora Java 8 Lambda

S vydáním Log4j2 verze 2.4 byla zavedena podpora pro Java 8 lambda výrazy. Pomocí výrazů lambda můžete inline definovat logiku protokolování. To snižuje potřebu víceřádkových kontrol nebo anonymních vnitřních tříd. To také zajišťuje, že drahé metody nejsou prováděny zbytečně. Kód je tedy nejen čistší a lépe čitelný, ale také se snižuje režie systému.

Uvažujme příklad, kdy zaznamenáte výsledek drahé operace, ale pouze pokud je povolena úroveň ladění. Před podporou pro lambdy by to bylo provedeno pomocí níže uvedeného kódu:

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

Více takových případů použití by zbytečně zavádělo podmíněné kontroly. Nicméně s Log42 lze stejnou akci provést následovně:

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

Metoda exrensiveOperation() je vyhodnocena pouze v případě, že je povolena úroveň ladění. Není potřeba žádných explicitních kontrol.

#3. Asynchronní loggery

Každá událost protokolu je I/O operace, což zvyšuje režii systému. Aby se to zmírnilo, Log4j2 zavádí asynchronní loggery, které běží v odděleném vlákně od aplikačního vlákna. Při použití asynchronních loggerů získá volající vlákno okamžitě zpět kontrolu po vyvolání metody logger.log().

To mu umožňuje pokračovat v aplikační logice namísto čekání na dokončení události protokolování. Využitím tohoto asynchronního chování se dosáhne vyšší propustnosti protokolování. Můžete buď zvolit, aby byly všechny protokolovače ve výchozím nastavení asynchronní, nebo mít kombinaci synchronního i asynchronního chování.

#4. Logování bez odpadků

V Javě je garbage collection proces, při kterém se nepoužívané objekty v aplikaci automaticky vyčistí. I když se o tuto operaci nemusíte starat ručně, úklid má svou vlastní režii.

Pokud vaše aplikace vytvoří příliš mnoho objektů v krátkém časovém období, může proces shromažďování paměti zabrat více systémových prostředků, než je nutné. Několik knihoven protokolování, včetně předchozích verzí Log4j, vytváří během procesu protokolování mnoho dočasných objektů. Následně zvýšený tlak na sběrač odpadu ovlivňuje výkon systému.

Od verze 2.6 běží Log4j2 v režimu „bez odpadu“. Toto je výchozí chování. Objekty jsou tedy znovu používány a vytváření dočasných je značně omezeno.

Následující obrázky ukazují, jak Log4j2 verze 2.6 zmírňuje problém nepotřebných objektů ve srovnání s Log4j2 verze 2.5.

V Log4j2 verze 2.5 se během procesu protokolování vytváří mnoho dočasných objektů; Zdroj: apache.org

V Log4j2.6 nejsou během procesu protokolování vytvořeny žádné dočasné objekty; Zdroj: apache.org

#5. Vyhledávání

V log4j2 můžete do protokolů přidat kontextové informace pomocí vyhledávání. Jejich využitím můžete přidávat data z různých zdrojů, jako jsou vlastnosti systému, proměnné prostředí nebo uživatelsky definované hodnoty. Můžete tedy zahrnout relevantní informace, které jsou dynamicky načítány, díky čemuž jsou protokoly užitečnější.

Uvažujme příklad, kdy chcete zaznamenat ID relace uživatele se všemi řádky protokolu. To vám umožní vyhledat všechny protokoly odpovídající id relace.

Hrubým způsobem, jak toho dosáhnout, by bylo explicitně přidat ID relace jednotlivě, což je obtížné udržovat. Brzy jej můžete zapomenout přidat a ztratit tak cenné informace.

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);

Lepší způsob, jak to udělat, by bylo použít kontextové vyhledávání mapy. ID relace lze přidat do kontextu vlákna v kódu aplikace. Hodnotu lze poté použít v konfiguraci Log4j2. Tím odpadá potřeba výslovného zmiňování ve zprávách protokolu.

ThreadContext.put("sessionId", sessionId);

Jakmile je hodnota přidána, lze totéž použít ve vyhledávání pomocí klíčového slova ctx.

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

Jak vytvořit vlastní úrovně protokolu v Log4j2?

Úrovně protokolu v Log4j2 se používají ke kategorizaci událostí protokolu na základě jejich závažnosti nebo důležitosti. Úroveň protokolu můžete ovládat při protokolování zprávy v kódu aplikace.

Například logger.debug() přidá úroveň DEBUG. Proto logger.error() přidá úroveň ERROR. To určuje, které zprávy se nakonec zobrazí ve výstupu. Úroveň protokolu můžete nakonfigurovat v konfiguračním souboru.

Předkonfigurované úrovně protokolu v Log4j2 a jejich odpovídající hodnoty jsou uvedeny níže.

OFF0FATAL100ERROR200WARN300INFO400DEBUG500TRACE600ALLMAX HODNOTA

Pokud je úroveň protokolu nastavena na určitou úroveň, vydají se všechny řádky protokolu pro příslušnou hodnotu a ty nad ní (s nižší hodnotou). Ostatní jsou ignorováni.

Pokud například nastavíte úroveň protokolu na WARN, zobrazí se zprávy WARN, ERROR a FATAL. Jakýkoli řádek protokolu s jinou úrovní bude ignorován. To je zvláště užitečné, když spouštíte stejný kód v různých prostředích.

Při spouštění kódu ve vývojovém prostředí můžete chtít nastavit Úroveň protokolu na INFO nebo DEBUG. To vám umožní vidět více protokolů a pomoci v procesu vývoje. Pokud však běží v produkčním prostředí, budete jej chtít nastavit na ERROR. Budete se tak moci soustředit na nalezení problému v případě výskytu jakékoli anomálie a nebudete muset procházet zbytečné řádky protokolu.

Může se stát, že budete chtít přidat svou vlastní úroveň protokolu navíc k předem nakonfigurovaným. Log4j2 vám to umožňuje snadno. Podívejme se, jak můžete přidat své vlastní úrovně protokolu a použít je ve své aplikaci.

#1. Přidání vlastní úrovně protokolu pomocí konfiguračního souboru

Vlastní úrovně protokolu můžete přidat tak, že je deklarujete v konfiguračním souboru.

V níže uvedeném příkladu byla definována vlastní úroveň protokolu s názvem NOTICE s hodnotou 450. Tím je umístěna mezi INFO (s hodnotou 400) a DEBUG (s hodnotou 500). To znamená, že pokud je úroveň nastavena na NOTICE, budou protokolovány INFO zprávy, ale zprávy DEBUG budou přeskočeny.

<?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. Přidání vlastní úrovně protokolu do kódu

Kromě jejich deklarování v konfiguračním souboru můžete ve svém kódu definovat své vlastní úrovně protokolu.

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

Tím se vytvoří nová úroveň protokolu s názvem VERBOSE. Tato úroveň protokolu bude ležet mezi DEBUG (s hodnotou 500) a TRACE (s hodnotou 600). Pokud je logger nastaven na úroveň VERBOSE, pak budou protokolovány všechny zprávy protokolu VERBOSE a vyšší, včetně DEBUG. Zprávy TRACE však budou přeskočeny.

#3. Použití Custom Log Level v kódu

Vlastní úrovně protokolu musí být před použitím nejprve deklarovány. Můžete je deklarovat buď v konfiguračním souboru, nebo ve vašem kódu. Jakmile je deklarujete, můžete je volně používat.

Tento příklad kódu ukazuje, jak můžete deklarovat vlastní úroveň nazvanou NOTICE a poté použít stejnou.

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

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

Ačkoli to vygeneruje požadovanou zprávu s nově vytvořenou úrovní, může být obtížné vždy úroveň explicitně projít. Naštěstí můžete generovat zdrojový kód, takže získáte pomocné metody pro protokolování vlastních úrovní. Pomocí téhož budete moci použít vlastní metodu logger.notice() podobnou té, jakou byste použili logger.debug() nebo logger.error().

Log4j2 přichází s utilitou, která vám pomůže vytvořit si vlastní rozšířené loggery. Následující příkaz vytvoří soubor Java s názvem CustomLogger.java. Tento soubor obsahuje existující metody protokolu spolu s nově vygenerovanými metodami pro úroveň NOTICE.

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

Jakmile je soubor vygenerován, můžete třídu ve svém kódu použít k vytváření nových protokolů. Tyto protokoly budou obsahovat další metody pro vaši vlastní úroveň protokolu. Můžete tak rozšířit funkčnost svých loggerů.

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

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

Závěr

Log4j2 je velmi výkonný protokolovací rámec Java, který nabízí širokou škálu funkcí, konfigurací, vylepšení výkonu a další. Protože protokoly jsou velmi důležitou součástí procesu vývoje softwaru, robustní rámec, jako je Log4j2, zvyšuje schopnosti aplikace.

Flexibilita a rozšiřitelnost Log4j2 umožňuje správné zachycení událostí, které se dějí ve vaší aplikaci. Následně vám umožňuje považovat protokoly za výkonný nástroj pro ladění a auditování. Se všemi svými funkcemi a vylepšeními se Log4j2 odlišuje a činí z něj preferovanou volbu v rozmanité řadě softwarových projektů.

Také by vás mohla zajímat tato Java IDE a online kompilátory.