En guide för Java-utvecklare

By rik

En kritisk aspekt inom mjukvaruutveckling är korrekt loggning. Med ett flertal Java-loggningsramverk tillgängliga, är det viktigt att välja ett som är enkelt att använda, högpresterande, har utökningsbara funktioner och anpassningsmöjligheter. Log4j2, ett kostnadsfritt Java-loggningsbibliotek, uppfyller alla dessa kriterier.

Genom att integrera Log4j2 i dina applikationer får du tillgång till avancerade filtreringsalternativ, stöd för Java 8 lambda-uttryck, egenskapsuppslagningar och anpassade loggnivåer. Låt oss utforska hur du kan integrera Log4j2 i dina projekt och vilka funktioner som hjälper dig att hantera dina loggar effektivt.

Vad är Log4j2?

Loggning är processen att registrera värdefull information, så kallade loggar, som kan analyseras i efterhand. Loggar kan användas för snabb felsökning av programkod. Applikationsloggar underlättar förståelsen av kodflödet och hanteringen av produktionsproblem och fel.

Utöver diagnostiska ändamål används loggar också för spårbarhet, till exempel för att verifiera om ett varningsmeddelande har skickats till användaren.

Log4j2 är ett av de mest använda Java-loggningsbiblioteken och är efterföljaren till det inflytelserika Log4j-biblioteket. Utvecklat av Apache Software Foundation, som en del av Apache Logging Services, är Log4j2 en kostnadsfri programvara med öppen källkod (FOSS) under Apache-licensen, version 2.0.

Log4j2 bygger på grunderna i det ursprungliga Log4j. Att använda en Logger jämfört med enkla System.out.println()-satser har fördelar, inklusive kontroll över vilka meddelanden som visas och möjligheten att undvika vissa loggmeddelanden. Korrekt loggning är vital i produktionsmiljöer där felsökningsverktyg inte alltid är tillgängliga.

Hur lägger jag till Log4j2 i ditt projekt?

Det finns flera sätt att integrera Log4j2 i ditt Java-projekt. För att fullt utnyttja alla funktioner i Log4j2 rekommenderas Java 8 eller högre.

Låt oss undersöka olika metoder för att lägga till Log4j2, beroende på dina specifika behov.

Lägga till Log4j2 i projekt med Apache Maven

Om ditt projekt använder Apache Maven som byggsystem, måste Log4j2-beroendena läggas till i `pom.xml`-filen.

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

För att underlätta underhållet av samma versioner över olika artefakter, tillhandahåller Log4j2 en BOM (Bill of Material) `pom.xml`-fil. Om du lägger till denna under din beroendehantering, slipper du ange versionsnummer individuellt.

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

Lägga till Log4j2 i projekt med Apache Gradle

Om du använder Apache Gradle som byggverktyg kan du lägga till Log4j2-beroendena i din `build.gradle`-fil.

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

Om du använder Gradle version 5.0 eller senare, kan du importera Log4j2 Maven BOM för att säkerställa konsistenta versionsnummer. Detta uppnås genom att lägga till följande i din `build.gradle`-fil.

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

För Gradle version 2.8-4.10 finns det ingen möjlighet att direktimportera Maven BOM. Du måste lägga till ett extra plugin för beroendehantering.

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

Lägga till Log4j2 i fristående applikationer utan ett byggverktyg

Om ditt projekt saknar ett byggverktyg kan du ladda ner de nödvändiga artefakterna från den officiella Log4j2-nedladdningssidan.

Efter nedladdningen måste du se till att applikationens klassväg innehåller följande JAR-filer:

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

Vilka är komponenterna i Log4j2?

För att förstå Log4j2:s funktionalitet och utnyttja dess fulla potential, är det viktigt att förstå dess struktur. Flera grundläggande byggstenar utgör Log4j2. Låt oss gå igenom dem en efter en.

#1. LoggerContext

LoggerContext är loggningssystemets kärna. Den innehåller alla loggare som används i applikationen, samt en referens till konfigurationen.

#2. Konfiguration

Konfigurationen innehåller all information som loggningssystemet behöver, inklusive Loggers, Appenders, Filter och mer. I Log4j2 kan konfigurationen definieras med olika filformat som XML, JSON och YAML, eller programmatiskt via Log4j2 API.

En automatisk omladdning sker när en egenskap i konfigurationen ändras, vilket eliminerar behovet av att starta om applikationen.

#3. Logger

Loggern är huvudkomponenten i Log4j2-systemet. Loggers hämtas i applikationskoden med hjälp av `LogManager.getLogger()` och används för att generera loggar. Loggmeddelanden kan genereras på olika nivåer av allvarlighet, som debug, info, warn, error och fatal.

#4. LoggerConfig

LoggerConfig styr beteendet hos en specifik Logger. Den definierar beteendet och inställningarna för loggningshändelser som genereras av just den loggern, inklusive loggnivå, bilagor och filter.

#5. Filter

I Log4j2 kan du selektivt bearbeta logghändelser med hjälp av filter, som tillämpas baserat på specifika kriterier. Filter kan tillämpas på loggare eller tillägg och styr vilka logghändelser som släpps igenom loggningsprocessen för vidare behandling. Detta möjliggör finjustering av loggningsbeteendet och säkerställer att endast relevanta loggar behandlas.

#6. Appender

Bilagan bestämmer destinationen för alla loggmeddelanden. En enda Logger kan ha flera bilagor, och en logghändelse skickas till alla bilagor som är kopplade till den loggern. Log4j2 har många förkonfigurerade bilagor, som `ConsoleAppender` för att logga till konsolen och `FileAppender` för att skriva till en fil. Varje bilaga kräver en egen layout som definierar formatet på det slutliga loggmeddelandet.

#7. Layout

Layouten definierar hur det slutliga loggmeddelandet kommer att se ut i Log4j2. En layout är knuten till en bilaga. Bilagan bestämmer utdatatdestinationen, medan layouten beskriver hur meddelandet kommer att formateras.

Topp 5 funktioner i Log4j2

Log4j2 är rikt på funktioner, vilket skiljer det från andra tillgängliga Java-loggningsramverk. Från asynkrona loggare till stöd för Java 8 lambdas, har Log4j2 ett försprång. Låt oss undersöka några av de mest framstående funktionerna.

#1. Utöka funktionerna med plugins

I Log4j 1.x krävdes stora kodändringar för att skapa tillägg. Log4j2 löser detta genom att introducera ett plugin-system.

Du kan deklarera ett nytt plugin med `@Plugin`-kommentaren på din klass. Med plugins kan du skapa egna komponenter, som filter och bilagor. Tredjepartskomponenter kan också enkelt läggas till i biblioteket.

#2. Java 8 Lambda-stöd

Med Log4j2 version 2.4 introducerades stöd för Java 8 lambda-uttryck. Med lambda-uttryck kan du definiera loggningslogik direkt i koden, vilket minskar behovet av flerradskontroller eller anonyma inre klasser. Detta säkerställer också att resurskrävande metoder inte exekveras i onödan. Koden blir inte bara renare och mer läsbar, utan systemets omkostnader minskar också.

Låt oss ta ett exempel där du vill logga resultatet av en resurskrävande operation, men endast om felsökningsnivån är aktiverad. Före lambdas skulle detta utföras med följande kod:

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

Flera sådana fall skulle i onödan introducera villkorliga kontroller. Men med Log4j2 kan samma åtgärd utföras så här:

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

Metoden `expensiveOperation()` utvärderas endast om felsökningsnivån är aktiverad. Inga explicita kontroller behövs.

#3. Asynkrona loggare

Varje logghändelse är en I/O-operation som ökar systemets omkostnader. Log4j2 introducerar asynkrona loggare som körs i en separat tråd från applikationens trådar. När asynkrona loggare används, får anropstråden omedelbart tillbaka kontrollen efter att `logger.log()`-metoden anropats.

Detta gör att tråden kan fortsätta med applikationslogiken istället för att vänta på att loggningshändelsen ska slutföras. Genom att utnyttja detta asynkrona beteende uppnås högre loggningskapacitet. Du kan välja att göra alla loggare asynkrona som standard eller använda en kombination av synkront och asynkront beteende.

#4. Skräpfri loggning

I Java är sophämtning processen där oanvända objekt i applikationen automatiskt rensas. Även om du inte behöver hantera detta manuellt, har sophämtning sina egna omkostnader.

Om din applikation skapar för många objekt under en kort tidsperiod, kan sophämtningsprocessen förbruka mer systemresurser än nödvändigt. Flera loggningsbibliotek, inklusive tidigare versioner av Log4j, skapar många temporära objekt under loggningsprocessen. Det ökade trycket på sopsamlaren påverkar därmed systemets prestanda.

Sedan version 2.6 körs Log4j2 i ”skräpfritt” läge. Detta är standardbeteendet. Objekt återanvänds och skapandet av temporära objekt reduceras kraftigt.

Följande bilder visar hur Log4j2 version 2.6 reducerar problemet med onödiga objekt jämfört med Log4j2 version 2.5.

I Log4j2 version 2.5 skapas många tillfälliga objekt under loggningsprocessen; Källa: apache.org

I Log4j2.6 finns inga tillfälliga objekt som skapas under loggningsprocessen; Källa: apache.org

#5. Uppslagningar

I log4j2 kan du lägga till kontextuell information i dina loggar med hjälp av Lookups. Med dessa kan du lägga till data från olika källor, som systemegenskaper, miljövariabler eller specialdefinierade värden. På så sätt kan du inkludera relevant information som hämtas dynamiskt, vilket gör loggarna mer användbara.

Låt oss anta att du vill logga användarens sessions-id med alla loggrader. Detta gör att du kan söka efter alla loggar som är kopplade till ett visst sessions-id.

Ett enkelt sätt skulle vara att explicit lägga till sessions-id:t individuellt, vilket blir svårt att underhålla. Du kan snart glömma att lägga till det och förlora värdefull information.

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

Ett bättre sätt är att använda Context Map Lookup. Sessions-id:t kan läggas till i trådkontexten i applikationskoden. Värdet kan sedan användas i Log4j2-konfigurationen, vilket eliminerar behovet av att nämna det explicit i loggmeddelanden.

ThreadContext.put("sessionId", sessionId);

När värdet har lagts till kan det användas i Lookup med nyckelordet `ctx`.

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

Hur gör man anpassade loggnivåer i Log4j2?

Loggnivåer i Log4j2 används för att kategorisera logghändelser baserat på deras svårighetsgrad. Du kan styra loggnivån när du loggar ett meddelande i applikationskoden.

Till exempel lägger `Logger.debug()` till DEBUG-nivån och `logger.error()` lägger till ERROR-nivån. Detta styr vilka meddelanden som slutligen visas i utdatat. Du kan konfigurera loggnivån i konfigurationsfilen.

De förkonfigurerade loggnivåerna i Log4j2 och deras motsvarande värden listas nedan:

OFF 0 FATAL 100 ERROR 200 WARN 300 INFO 400 DEBUG 500 TRACE 600 ALL MAX VÄRDE

Om loggnivån är inställd på en viss nivå, loggas alla loggrader för det motsvarande värdet och de som är högre (med lägre värde). De övriga ignoreras.

Om du till exempel ställer in loggnivån till WARN, kommer WARN-, ERROR- och FATAL-meddelanden att visas. Alla loggrader med en annan nivå ignoreras. Detta är särskilt användbart när du kör samma kod i olika miljöer.

Du kanske vill ställa in loggnivån till INFO eller DEBUG när du kör koden i en utvecklingsmiljö för att se fler loggar och underlätta utvecklingen. I en produktionsmiljö vill du däremot ställa in den på ERROR, för att fokusera på att hitta problem om avvikelser uppstår utan att gå igenom onödiga loggrader.

Du kanske också vill lägga till egna anpassade loggnivåer utöver de förkonfigurerade. Log4j2 gör det enkelt. Låt oss se hur du kan lägga till egna nivåer och använda dem i applikationen.

#1. Lägga till anpassad loggnivå med konfigurationsfilen

Du kan lägga till anpassade loggnivåer genom att deklarera dem i konfigurationsfilen.

I exemplet nedan har en anpassad nivå med namnet `NOTICE` definierats med värdet 450. Detta placerar den mellan INFO (värde 400) och DEBUG (värde 500). Det innebär att om nivån är inställd på `NOTICE` kommer INFO-meddelanden att loggas, medan DEBUG-meddelanden ignoreras.

<?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. Lägga till anpassad loggnivå i kod

Förutom att deklarera dem i konfigurationsfilen, kan du definiera egna loggnivåer i din kod.

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

Detta skapar en ny loggnivå med namnet `VERBOSE`. Denna nivå ligger mellan DEBUG (värde 500) och TRACE (värde 600). Om loggern är inställd på `VERBOSE` kommer alla meddelanden från `VERBOSE` och högre att loggas, inklusive DEBUG. TRACE-meddelanden ignoreras.

#3. Använda den anpassade loggnivån i koden

Anpassade loggnivåer måste först deklareras innan de används. Du kan deklarera dem antingen i konfigurationsfilen eller i koden. När de väl har deklarerats är du fri att använda dem.

Följande kodexempel visar hur du kan deklarera en anpassad nivå med namnet `NOTICE` och använda den.

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

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

Även om detta genererar önskat meddelande med den nyskapade nivån, kan det bli krångligt att alltid ange nivån explicit. Lyckligtvis kan du generera källkod som skapar hjälpmetoder för att logga dina anpassade nivåer. Därmed kan du använda metoden `logger.notice()`, liknande `logger.debug()` eller `logger.error()`.

Log4j2 har ett verktyg för att skapa egna utökade loggare. Följande kommando skapar en Java-fil vid namn `CustomLogger.java`. Denna fil innehåller befintliga loggmetoder tillsammans med nyligen genererade metoder för `NOTICE`-nivån.

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

När filen har genererats kan du använda klassen i din kod för att skapa nya loggare. Dessa loggare kommer att ha extra metoder för din anpassade loggnivå. På så sätt utökar du funktionaliteten hos dina loggare.

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

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

Slutsats

Log4j2 är ett kraftfullt Java-loggningsramverk som erbjuder ett brett utbud av funktioner, konfigurationsmöjligheter, prestandaförbättringar med mera. Med loggar som en vital del av mjukvaruutveckling, förbättrar ett robust ramverk som Log4j2 applikationens möjligheter.

Log4j2:s flexibilitet och utökningsbarhet möjliggör korrekt registrering av händelser som inträffar i din applikation, vilket ger dig ett kraftfullt verktyg för felsökning och granskning. Med alla dess funktioner och förbättringar, sticker Log4j2 ut och är ett utmärkt val för en mängd olika mjukvaruprojekt.

Du kanske också är intresserad av dessa Java IDE och online-kompilatorer.