En sentral del av programvareutvikling er effektiv loggføring. Med et mangfold av Java-loggingsrammeverk tilgjengelig, er det kritisk å velge et som er lett å bruke. Samtidig bør rammeverket ha høy ytelse, mulighet for utvidelse og tilpasning. Log4j2 er et kostnadsfritt Java-loggingsbibliotek som oppfyller alle disse kravene.
Ved å integrere Log4j2 i applikasjoner får man tilgang til avansert filtrering, Java 8 lambda-støtte, oppslag av egenskaper og egendefinerte loggnivåer. La oss undersøke hvordan du kan implementere Log4j2 i prosjektene dine, og hvilke funksjoner som kan bidra til å forbedre effektiviteten din.
Hva er Log4j2?
Loggføring er prosessen med å registrere nyttig informasjon, kjent som logger, for senere referanse og analyse. Logger kan brukes til rask feilsøking av programkode, gir innsikt i kodeflyt, og hjelper med å håndtere produksjonsproblemer og feil.
Utover diagnostiske formål, brukes logger også til revisjonsformål, for eksempel sporing av om en varselmelding er sendt til en bruker.
Log4j2 er et av de mest populære Java-loggingsbibliotekene og er etterfølgeren til det innflytelsesrike Log4j-biblioteket. Utviklet av Apache Software Foundation, og en del av Apache Logging Services, er Log4j2 gratis og åpen kildekode-programvare (FOSS) lisensiert under Apache License, versjon 2.0.
Log4j2 er bygget på et solid fundament fra den opprinnelige Log4j. Bruk av en logger er fordelaktig fremfor enkle `System.out.println()`-utskrifter. Dette inkluderer kontroll over hvilke meldinger som vises, samtidig som man unngår andre loggmeldinger. Riktig loggføring er avgjørende i produksjonsmiljøer der feilsøkingsprogrammer ikke er tilgjengelige.
Hvordan legge til Log4j2 i prosjektet ditt?
Det er flere måter å implementere Log4j2 i Java-prosjektet ditt. Det anbefales å bruke Java 8 eller nyere for å kunne benytte alle funksjonene til Log4j2.
La oss se på de ulike metodene for å legge til Log4j2, avhengig av dine behov.
Legge til Log4j2 i prosjekter med Apache Maven
Dersom prosjektet ditt bruker Apache Maven som byggesystem, må Log4j2-avhengighetene legges til 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>
For å forenkle vedlikeholdet av samme versjon på tvers av artefakter, har Log4j2 en BOM-fil (Bill of Material) i `pom.xml`. Ved å legge denne til under avhengighetsadministrasjonen din, trenger du ikke å spesifisere versjoner individuelt.
<!-- Legg til BOM i 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> <!-- Når BOM er lagt til, er ikke versjoner nødvendig --> <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>
Legge til Log4j2 i prosjekter med Apache Gradle
Dersom du bruker Apache Gradle som byggverktøy, kan du legge til Log4j2-avhengighetene i `build.gradle`-filen.
dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.20.0' implementation 'org.apache.logging.log4j:log4j-core:2.20.0' }
For Gradle versjon 5.0 eller nyere kan du importere Log4j2 Maven Bill Of Materials (BOM) for å opprettholde konsistente avhengighetsversjoner. Dette oppnås ved å legge til følgende i `build.gradle`-filen.
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' }
For Gradle versjoner 2.8-4.10 er det ikke mulig å importere Maven BOM direkte. Du må legge til en ekstra plugin for å få denne funksjonaliteten.
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' }
Legge til Log4j2 i frittstående applikasjoner uten byggverktøy
Hvis prosjektet ditt ikke bruker et byggverktøy, kan du laste ned de nødvendige Log4j2-artefaktene fra den offisielle nedlastingssiden.
Etter nedlasting må du sørge for at applikasjonens klassebane inkluderer følgende JAR-filer:
- `log4j-api-2.20.0.jar`
- `log4j-core-2.20.0.jar`
Hvilke komponenter består Log4j2 av?
For å fullt ut forstå og benytte funksjonene i Log4j2, er det viktig å kjenne til hvordan det fungerer. Flere byggesteiner utgjør Log4j2. La oss se på dem en etter en.
#1. LoggerContext
`LoggerContext` er sentral i loggingssystemet. Den inneholder alle loggerne som er forespurt i applikasjonen og en referanse til konfigurasjonen.
#2. Konfigurasjon
Konfigurasjonen inneholder all informasjon som trengs av loggingssystemet, inkludert loggere, vedlegg, filtre og mer. I Log4j2 kan du definere konfigurasjonen ved å bruke forskjellige filformater som XML, JSON og YAML, eller programmatisk via Log4j2 API.
Endringer i konfigurasjonen fører til automatisk oppdatering, uten behov for omstart av applikasjonen.
#3. Logger
Hovedkomponenten i Log4j2 er loggeren. Loggere hentes i applikasjonskoden via `LogManager.getLogger()` og brukes til å generere logger. Loggmeldinger kan genereres med ulike alvorlighetsnivåer som debug, info, warn, error og fatal.
#4. LoggerConfig
`LoggerConfig` er ansvarlig for oppførselen til en spesifikk logger. Den definerer innstillingene og hvordan loggingshendelser generert av den aktuelle loggeren skal håndteres, inkludert loggnivåer, vedlegg og filtre.
#5. Filter
Filtre i Log4j2 brukes til selektiv behandling av logghendelser basert på spesifikke kriterier. Filtre kan brukes på loggere eller vedlegg, og kontrollerer hvilke logghendelser som slipper gjennom loggingssystemet for videre behandling. Dette gir mulighet for finjustering av loggingsatferd, slik at kun relevante logger behandles.
#6. Vedlegg
Vedlegget bestemmer destinasjonen for loggmeldinger. En enkelt logger kan ha flere vedlegg, og logghendelsen blir sendt til alle vedleggene for den loggeren. Log4j2 har mange forhåndskonfigurerte vedlegg, som `ConsoleAppender` for loggføring til konsollen, og `FileAppender` for utskrift til en fil. Hvert vedlegg trenger sin egen layout som bestemmer hvordan den endelige loggmeldingen vil se ut.
#7. Layout
Layout i Log4j2 definerer hvordan den endelige loggmeldingen skal formateres. En layout er knyttet til et vedlegg. Mens vedlegget angir utdatamålet, beskriver layouten hvordan meldingen skal leveres.
Topp 5 funksjoner i Log4j2
Log4j2 er svært funksjonsrikt og skiller seg ut fra andre Java-loggingsrammeverk. Med funksjoner som asynkrone loggere og støtte for Java 8 lambdaer, har Log4j2 et forsprang. La oss se på noen av de viktigste egenskapene.
#1. Utvide funksjonaliteten med plugins
I Log4j 1.x krevde utvidelser omfattende kodeendringer. Log4j2 løser dette med plugin-systemet.
Du kan deklarere et nytt plugin ved å bruke `@Plugin`-annotasjonen på klassen din. Plugins gjør det mulig å lage egne komponenter som filtre og vedlegg, og tredjepartskomponenter kan enkelt legges til biblioteket.
#2. Java 8 Lambda-støtte
Log4j2 versjon 2.4 introduserte støtte for Java 8 lambda-uttrykk. Med lambda-uttrykk kan du definere loggingslogikken inline, og dermed redusere behovet for flerlinjekontroller eller anonyme indre klasser. Dette sikrer også at ressurskrevende metoder ikke utføres unødvendig. Resultatet er renere og mer lesbar kode, samtidig som systemkostnadene reduseres.
La oss se på et eksempel der du logger resultatet av en ressurskrevende operasjon, men bare hvis feilsøkingsnivået er aktivert. Før lambda-støtte måtte koden se slik ut:
if (logger.isDebugEnabled()) { logger.debug("Resultatet av operasjonen er: {}", expensiveOperation()); }
Mange slike tilfeller vil føre til unødvendige betingede kontroller. Med Log4j2 kan den samme handlingen utføres slik:
logger.debug("Resultatet av operasjonen er: {}", () -> expensiveOperation()
Metoden `expensiveOperation()` evalueres kun hvis feilsøkingsnivået er aktivert, uten behov for eksplisitte kontroller.
#3. Asynkrone loggere
Hver logghendelse er en I/O-operasjon, noe som øker systembelastningen. For å redusere dette, introduserer Log4j2 Asynkrone Loggere som kjører i en separat tråd fra applikasjonstråden. Når du bruker asynkrone loggere, får kalltråden kontrollen tilbake umiddelbart etter å ha påkalt `logger.log()`-metoden.
Dette lar applikasjonslogikken fortsette uten å måtte vente på at loggingshendelsen er fullført. Med denne asynkrone oppførselen oppnås en høyere logggjennomstrømning. Du kan velge å gjøre alle loggere asynkrone som standard eller ha en blanding av synkron og asynkron oppførsel.
#4. Søppelfri logging
I Java er søppelhåndtering prosessen der ubrukte objekter i applikasjonen automatisk fjernes. Selv om du ikke trenger å håndtere dette manuelt, har søppelhåndtering sin egen overhead.
Hvis applikasjonen din lager for mange objekter på kort tid, kan søppelhåndteringsprosessen bruke mer systemressurser enn nødvendig. Flere loggbiblioteker, inkludert tidligere versjoner av Log4j, oppretter mange midlertidige objekter under loggingsprosessen. Dette øker belastningen på søppelhåndteringen og påvirker systemets ytelse.
Siden versjon 2.6 opererer Log4j2 i «søppelfri» modus. Dette er standardoppførselen. Objekter gjenbrukes, og opprettelsen av midlertidige objekter reduseres betraktelig.
Følgende bilder viser hvordan Log4j2 versjon 2.6 reduserer problemet med unødvendige objekter, sammenlignet med Log4j2 versjon 2.5.
I Log4j2 versjon 2.5 opprettes mange midlertidige objekter under loggingsprosessen; Kilde: apache.org
I Log4j2.6 opprettes ingen midlertidige objekter under loggingsprosessen; Kilde: apache.org
#5. Oppslag
I Log4j2 kan du legge til kontekstuell informasjon til loggene dine ved å bruke oppslag. Du kan hente data fra ulike kilder, som systemegenskaper, miljøvariabler eller egendefinerte verdier. Dette gjør loggene mer nyttige ved å inkludere dynamisk hentet, relevant informasjon.
La oss se på et eksempel der du ønsker å logge brukerens sesjons-ID med alle logglinjer, noe som gjør det enkelt å søke etter alle logger som tilhører en bestemt sesjons-ID.
En «brute force»-tilnærming vil være å eksplisitt legge til sesjons-IDen individuelt, noe som er vanskelig å vedlikeholde. Man kan lett glemme å legge den til, og dermed miste verdifull informasjon.
logger.info("Brukerdata er hentet for sesjons-ID {}", sessionId); ... logger.info("Transaksjonen er behandlet for sesjons-ID {}", sessionId); ... logger.info("Forespørsel er behandlet for sesjons-ID {}", sessionId);
En bedre løsning er å bruke Context Map Lookup. Sesjons-IDen kan legges til trådkonteksten i applikasjonskoden, og verdien kan deretter brukes i Log4j2-konfigurasjonen. Dette eliminerer behovet for eksplisitt å nevne den i loggmeldingene.
ThreadContext.put("sessionId", sessionId);
Når verdien er lagt til, kan den brukes i Lookup med nøkkelordet `ctx`.
<File name="Application" fileName="application.log"> <PatternLayout> <pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern> </PatternLayout> </File>
Hvordan lage tilpassede loggnivåer i Log4j2?
Loggnivåer i Log4j2 brukes til å kategorisere logghendelser basert på alvorlighetsgrad. Du kan kontrollere loggnivået når du logger en melding i applikasjonskoden.
For eksempel legger `logger.debug()` til DEBUG-nivået, og `logger.error()` legger til ERROR-nivået. Dette avgjør hvilke meldinger som vises i utdataene. Loggnivået kan konfigureres i konfigurasjonsfilen.
De forhåndskonfigurerte loggnivåene i Log4j2 og deres tilsvarende verdier er som følger:
OFF (0), FATAL (100), ERROR (200), WARN (300), INFO (400), DEBUG (500), TRACE (600), ALL (MAX VERDI)
Hvis loggnivået er satt til et bestemt nivå, vil alle logglinjer med den tilsvarende verdien og de over den (lavere verdi) sendes ut. De andre ignoreres.
For eksempel vil et loggnivå satt til WARN kun vise WARN-, ERROR- og FATAL-meldinger. Alle andre logglinjer vil bli ignorert. Dette er spesielt nyttig når samme kode kjøres i forskjellige miljøer.
Du vil kanskje sette loggnivået til INFO eller DEBUG under utvikling for å se flere logger, men sette det til ERROR i produksjon. Dette gir deg mulighet til å fokusere på å finne problemer ved avvik, uten å måtte gjennomgå unødvendige logglinjer.
Det er mulig å legge til egne loggnivåer i tillegg til de forhåndskonfigurerte. Log4j2 gjør dette enkelt. La oss se hvordan du kan legge til egne loggnivåer og bruke dem i applikasjonen din.
#1. Legge til tilpasset loggnivå i konfigurasjonsfilen
Du kan legge til egendefinerte loggnivåer ved å deklarere dem i konfigurasjonsfilen.
I eksemplet under er et tilpasset loggnivå kalt NOTICE definert med en verdi på 450, som plasserer det mellom INFO (400) og DEBUG (500). Hvis nivået er satt til NOTICE, vil INFO-meldinger logges, mens DEBUG-meldinger hoppes over.
<?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. Legge til tilpasset loggnivå i koden
I tillegg til å deklarere dem i konfigurasjonsfilen, kan du definere egne loggnivåer i koden.
final Level VERBOSE = Level.forName("VERBOSE", 550);
Dette vil opprette et nytt loggnivå kalt VERBOSE. Dette loggnivået vil ligge mellom DEBUG (500) og TRACE (600). Hvis loggeren er satt til nivået VERBOSE, vil alle loggmeldinger fra VERBOSE og oppover (inkludert DEBUG) bli logget, mens TRACE-meldinger vil hoppes over.
#3. Bruke tilpasset loggnivå i koden
Egendefinerte loggnivåer må deklareres før de kan brukes, enten i konfigurasjonsfilen eller i koden. Etter deklarasjonen kan du bruke dem fritt.
Dette kodeeksemplet viser hvordan du kan deklarere et tilpasset nivå kalt NOTICE, og deretter bruke det samme.
final Level NOTICE = Level.forName("NOTICE", 550); final Logger logger = LogManager.getLogger(); logger.log(NOTICE, "en melding på notice-nivå");
Selv om dette genererer den nødvendige meldingen med det nyopprettede nivået, kan det være tungvint å alltid eksplisitt angi nivået. Heldigvis kan du generere kildekode som gir hjelpemetoder for å logge dine egendefinerte nivåer, som `logger.notice()`, på samme måte som `logger.debug()` eller `logger.error()`.
Log4j2 har et verktøy for å generere dine egne utvidede loggere. Følgende kommando oppretter en Java-fil ved navn `CustomLogger.java`, som inneholder de eksisterende loggmetodene, i tillegg til de nylig genererte metodene for NOTICE-nivået.
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 er generert, kan klassen brukes til å lage nye loggere. Disse loggerne vil inneholde flere metoder for ditt tilpassede loggnivå, og dermed utvide funksjonaliteten til loggerne dine.
final Logger logger = CustomLogger.create(ValueFirstSmsSender.class); //denne nye metoden tilsvarer å bruke logger.debug() logger.notice("en melding på notice-nivå");
Konklusjon
Log4j2 er et kraftig Java-loggingsrammeverk med et bredt spekter av funksjoner, konfigurasjonsmuligheter, ytelsesforbedringer og mer. Logger er en viktig del av programvareutvikling, og et robust rammeverk som Log4j2 forbedrer applikasjonens muligheter.
Log4j2 sin fleksibilitet og utvidbarhet gir korrekt registrering av hendelser i applikasjonen. Du kan se på logger som et kraftig verktøy for feilsøking og revisjon. Med sine funksjoner og forbedringer, skiller Log4j2 seg ut og er et foretrukket valg i mange ulike programvareprosjekter.
Du er kanskje også interessert i disse Java IDEene og online-kompilatorer.