CodeGym /Java-blogg /Tilfeldig /Logging: hva, hvordan, hvor og med hva?
John Squirrels
Nivå
San Francisco

Logging: hva, hvordan, hvor og med hva?

Publisert i gruppen
Hei alle sammen i CodeGym-fellesskapet! Logging: hva, hvordan, hvor og med hva?  - 1 I dag skal vi snakke om logging:
  1. Hva det er, hvorfor det eksisterer, når du bør bruke det, når du bør unngå det.
  2. Hvilke loggimplementeringer er tilgjengelige i Java, og hva du bør gjøre med alle disse loggingsalternativene.
  3. Og loggnivåer. Vi vil diskutere hva appender er og hvordan du konfigurerer den riktig.
  4. Logge noder og hvordan konfigurere dem riktig slik at alt fungerer slik vi ønsker.
Dette materialet er beregnet på et bredt publikum. Det vil være klart for alle som bare blir kjent med Java, så vel som folk som allerede jobber, men som bare har utforsket logger.info("log something"); La oss gå!

Hvorfor trenger du logging?

La oss se på noen reelle tilfeller der logging kan løse et problem. Her er et eksempel fra arbeidet mitt. Det er punkter der en applikasjon integreres med andre tjenester. Jeg bruker logging på disse punktene for å etablere et slags "alibi" : hvis integrasjonen ikke fungerer, blir det lett å finne ut hvilken side som har problemet. Det er også ønskelig å logge viktig informasjon som er lagret i en database. For eksempel opprettelsen av en admin-bruker. Det er nettopp denne typen ting som ville vært bra å logge.

Verktøy for å logge på Java

Blant de velkjente loggingsløsningene i Java kan vi fremheve følgende:
  • Log4j
  • JUL — java.util.logging
  • JCL — Jakarta Commons Logging
  • Logg tilbake
  • SLF4J — Enkel loggingsfasade for Java
Vi vil gi en oversikt over hver av dem. Deretter tar vi en slf4j - log4j binding som grunnlag for en praktisk diskusjon. Dette kan virke rart nå, men ikke bekymre deg: mot slutten av artikkelen vil alt være klart.

System.err.println

I begynnelsen var det System.err.println (som viser loggoppføringer på konsollen). Selv i dag brukes denne teknikken til raskt å logge ved feilsøking. Selvfølgelig er det ingen innstillinger å diskutere her, så bare husk denne metoden, så går vi videre.

Log4j

Dette er en komplett løsning som utviklere har laget av nødvendighet. Resultatet er et veldig interessant verktøy som du kan bruke. På grunn av ulike omstendigheter havnet ikke denne løsningen i JDK, et faktum som opprørte hele samfunnet sterkt. Log4j har konfigurasjonsalternativer som lar deg aktivere innlogging av com.example.typepakken og slå den av i com.example.type.genericunderpakken. Dette gjør det mulig å raskt ekskludere kode som ikke trenger å logges. Det er viktig å merke seg her at det er to versjoner av Log4j: 1.2.x og 2.xx, og de er inkompatible med hverandre . Log4j la til begrepene appender(et verktøy som brukes til å skrive logger) og layout (loggformatering). Dette lar deg logge bare det du trenger og logge det akkurat slik du trenger det. Vi snakker mer om appender litt senere.

JUL — java.util.logging

En av de viktigste fordelene med denne løsningen er at JUL er inkludert i JDK (Java Development Kit). Dessverre, da den ble utviklet, baserte skaperne den ikke på det populære Log4j-verktøyet, men snarere en løsning fra IBM. Den avgjørelsen har fått konsekvenser. Realiteten er at ingen bruker JUL nå. Loggnivåene i JUL skiller seg fra hva Logback, Log4j og Slf4j har. Dette gjør det vanskeligere for dem å forstå hverandre. Å lage en logger er mer eller mindre likt. For å gjøre dette må du importere:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
Klassenavnet er bestått, så vi vet hvor loggingen vår kommer fra. Fra og med Java 8 kan du bestå Supplier<String>. Dette hjelper oss å lese og lage en linje bare når vi virkelig trenger det, i stedet for hver gang, som tidligere var tilfellet. Først med utgivelsen av Java 8 løste utviklere endelig viktige problemer og gjorde JUL virkelig brukbar. Nemlig metoder med en Supplier<String> msgSupplierparameter, som vist nedenfor:

public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL — Jakarta Commons Logging

Fordi det ikke var noen bransjestandard for logging på lenge, og mange mennesker laget sine egne tilpassede loggere, ble beslutningen tatt om å gi ut JCL, en generell innpakning som kunne brukes på toppen av andre. Hvorfor? Noen ganger brukte avhengigheter lagt til prosjektet en annen logger enn den i prosjektet. På grunn av dette ble de lagt til prosjektet som transitive avhengigheter, og dette skapte reelle problemer når man prøvde å sette det hele sammen. Dessverre var innpakningen ikke veldig funksjonell og tilførte ingenting. Det ville nok vært praktisk om alle brukte JCL. Men det var ikke det som skjedde, så å bruke JCL er ikke den beste ideen for øyeblikket.

Logg tilbake

Den åpne kildekodebanen er vanskelig... Den samme utvikleren som skrev Log4j skrev også Logback som et etterfølgende loggingsrammeverk. Den var basert på samme idé som Log4j. Forskjellene i Logback er:
  • forbedret ytelse
  • lagt til innebygd støtte for Slf4j
  • utvidede filtreringsalternativer
Som standard krever ikke Logback noen konfigurasjon, og registrerer alle hendelser på DEBUG-nivå og høyere. Hvis du trenger litt tilpasning, kan du oppnå det gjennom en XML-konfigurasjon:

<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 — Enkel loggingsfasade for Java

En gang i 2006 forlot en av Log4js grunnleggere prosjektet og opprettet Slf4j (Simple Logging Facade for Java), en innpakning for Log4j, JUL, common-logging og Logback. Som du kan se, har vi avansert til poenget med å lage en wrapper over en wrapper... I dette tilfellet er den delt inn i to deler: En API som brukes i applikasjonen, og en implementering som legges til med separate avhengigheter for hver type logging. For eksempel slf4j-log4j12.jarog slf4j-jdk14.jar. Du må koble til den riktige implementeringen, og det er det: hele prosjektet vil bruke den. Slf4j støtter alle de nyeste funksjonene, for eksempel formatering av strenger for logging. Tidligere var det et slikt problem. La oss si at vi lager en loggoppføring som dette:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
På grunn av sammenkoblingsoperatoren userblir objektet stille til en streng takket være user.toString(). Dette tar tid og bremser systemet. Og det kan være greit hvis vi feilsøker programmet. Vi begynner å støte på problemer hvis loggnivået for denne klassen er INFO eller høyere. Med andre ord, vi bør ikke skrive denne loggoppføringen (for INFO eller høyere), og vi bør ikke bruke strengsammenkobling. I teorien burde loggbiblioteket selv ta tak i dette. Som det skjer, viste dette seg å være det største problemet i den første versjonen av Log4j. Det ga ikke en anstendig løsning, men foreslo i stedet å gjøre noe slikt:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Det vil si at i stedet for én linje med kode for logging, foreslo de å skrive 3! Logging skal minimere kodeendringer, og de tre linjene bryter tydelig med den generelle tilnærmingen. Slf4j hadde ingen kompatibilitetsproblemer med JDK og API, så en fin løsning dukket umiddelbart opp:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
hvor {}angir plassholdere for argumentene som sendes til metoden. Det vil si at den første {}tilsvarer user, og den andre {}tilsvarer request.getRemoteAddr(). Ved å gjøre det på denne måten vil vi utføre strengsammenkobling bare hvis loggnivået krever at vi skriver loggoppføringen. Etter det begynte Sjf4j å vokse raskt i popularitet. Foreløpig er det den beste løsningen. La oss følgelig ta en titt på logging ved hjelp av en slf4j-log4j12binding.

Hva må logges

Du skal selvfølgelig ikke logge alt. Dette er ofte ikke nødvendig og noen ganger til og med farlig. For eksempel, hvis du logger noens personlige data og det på en eller annen måte blir lekket, vil det være reelle problemer, spesielt i prosjekter som fokuserer på vestlige markeder. Men det er også ting du definitivt bør logge :
  1. Start/slutt av søknaden. Vi må vite om søknaden virkelig startet og endte som forventet.
  2. Sikkerhetsproblemer. Her vil det være greit å logge forsøk på å gjette noens passord, tilfeller når administratorer logger på osv.
  3. Enkelte søknader sier . For eksempel overgangen fra en tilstand til en annen i en forretningsprosess.
  4. Viss feilsøkingsinformasjon sammen med det tilsvarende loggnivået.
  5. Visse SQL-skript. Det er tilfeller i den virkelige verden når dette er nødvendig. Men igjen, ved å dyktig justere loggnivåene, kan du oppnå utmerkede resultater.
  6. Kjørende tråder kan logges når du bekrefter at ting fungerer som de skal.

Populære feil i logging

Det er mange nyanser her, men vi vil spesielt nevne noen få vanlige feil:
  1. Overdreven logging. Du bør ikke logge hvert trinn som teoretisk sett kan være viktig. Her en god tommelfingerregel: Tømmerstokker bør ikke overstige 10 % av belastningen. Ellers vil det være ytelsesproblemer.
  2. Logger alle data i én fil. På et tidspunkt vil dette gjøre det svært vanskelig å lese/skrive loggen, for ikke å snakke om det faktum at enkelte systemer har begrensninger på filstørrelse.
  3. Bruker feil loggnivåer. Hvert loggnivå har klare grenser, og de bør respekteres. Hvis en grense er uklar, kan man komme til enighet om hvilket nivå som skal brukes.

Loggnivåer

x: Synlig
FATAL FEIL VARSLE INFO FEIL SPOR ALLE
AV
FATAL x
FEIL x x
VARSLE x x x
INFO x x x x
FEIL x x x x x
SPOR x x x x x x
ALLE x x x x x x x
Hva er loggnivåer? For på en eller annen måte å lage et hierarki av loggoppføringer, er det nødvendig med visse konvensjoner og avgrensninger. Dette er grunnen til at loggnivåer ble introdusert. Nivået settes i applikasjonen. Hvis en oppføring er under et spesifisert nivå, blir den ikke logget. For eksempel har vi logger som vi bruker når vi feilsøker applikasjonen. Under normal drift (når applikasjonen brukes til det tiltenkte formålet), er slike logger ikke nødvendig. Derfor er loggnivået høyere enn for feilsøking. La oss se på loggnivåer ved å bruke Log4j. Bortsett fra JUL bruker andre løsninger de samme loggnivåene. Her er de i synkende rekkefølge:
  • AV: Ingen loggoppføringer blir registrert; alt blir ignorert.
  • FATAL: En feil som hindrer programmet i å fortsette å kjøre. For eksempel "JVM tom for minne-feil".
  • FEIL: Feil på dette nivået indikerer problemer som må løses. Feilen stopper ikke applikasjonen som helhet. Andre forespørsler kan fungere riktig.
  • ADVARSEL: Loggoppføringer som representerer en advarsel. Noe uventet skjedde, men systemet klarte å takle det og oppfylte forespørselen
  • INFO: Loggoppføringer som indikerer viktige handlinger i applikasjonen. Dette er ikke feil eller advarsler. De er forventede systemhendelser.
  • DEBUG: Loggoppføringer trenger for å feilsøke applikasjonen. For å sikre at applikasjonen gjør nøyaktig det som forventes, eller for å beskrive handlingene applikasjonen har utført, dvs. "Oppgitt metode1".
  • SPOR: Loggoppføringer med lavere prioritet for feilsøking. Det laveste loggnivået.
  • ALL: Et loggnivå for å skrive alle applikasjonens loggoppføringer.
I INFO-loggnivået er aktivert et sted i applikasjonen, så vil oppføringene for hvert nivå bli logget, fra INFO til FATAL. Hvis FATAL-loggnivået er satt, vil bare loggoppføringer med det nivået bli skrevet.

Logging og sending av logger: Vedlegg

La oss vurdere hvordan alt dette fungerer når vi bruker Log4j, som gir gode muligheter for å skrive/sende logger:
  • å skrive til en fil —DailyRollingFileAppender
  • å skrive informasjon til konsollen —ConsoleAppender
  • å skrive logger til en database —JDBCAppender
  • å administrere sendelogger over TCP/IP —TelnetAppender
  • for å sikre at logging ikke påvirker ytelsen negativt –AsyncAppender
Det er noen flere implementeringer: en fullstendig liste er tilgjengelig her . Forresten, hvis vedlegget du trenger ikke finnes, er det ikke noe problem. Du kan skrive din egen appender ved å implementere Appender- grensesnittet, som Log4j støtter.

Logging noder

For demonstrasjonsformål vil vi bruke et Slf4j-grensesnitt, med en implementering fra Log4j. Å lage en logger er veldig enkelt: i en klasse som heter MainDemo, som vil gjøre noe logging, må vi legge til følgende:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Dette vil lage en logger for oss. For å lage en loggoppføring er det flere tilgjengelige metoder hvis navn gjenspeiler hvilket loggnivå som skal brukes. For eksempel:

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 the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
Selv om vi består klassen, er det endelige navnet klassens fulle navn, inkludert pakker. Dette gjøres slik at du senere kan dele inn loggingen i noder og konfigurere loggingsnivå og vedlegg for hver node. Loggeren ble for eksempel opprettet i com.github.romankh3.logginglecture.MainDemoklassen. Navnet gir grunnlaget for å lage et hierarki av loggingsnoder. Hovednoden er RootLogger på toppnivå . Dette er noden som mottar alle loggoppføringer for hele applikasjonen. De gjenværende nodene kan avbildes som vist nedenfor: Logging: hva, hvordan, hvor og med hva?  - 3Vedlegg er konfigurert for spesifikke loggingsnoder. Nå skal vi se på log4j.properties- filen for å se et eksempel på hvordan du konfigurerer dem.

En trinn-for-trinn-guide til log4j.properties-filen

Vi setter opp alt ett trinn om gangen og ser hva som er mulig:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Denne linjen sier at vi registrerer CONSOLE-appenderen, som bruker org.apache.log4j.ConsoleAppender-implementeringen. Denne vedlegget skriver informasjon til konsollen. Deretter registrerer vi en annen vedlegg. Denne vil skrive til en fil:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Det er viktig å merke seg at selve vedleggene fortsatt må konfigureres. Når vi har registrert våre vedlegg, kan vi bestemme hvilke loggnivåer og hvilke vedlegg som skal brukes ved nodene.

log4j.rootLogger=DEBUG, KONSOL, FIL

  • log4j.rootLogger betyr at vi konfigurerer rotnoden, som inneholder alle loggoppføringer
  • Det første ordet etter likhetstegnet indikerer minimumsloggnivået som skal skrives (i vårt tilfelle er det DEBUG)
  • Etter kommaet angir vi alle vedleggene som skal brukes.
For å konfigurere en mer spesifikk loggingsnode, bruker du en oppføring som denne:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
hvor log4j.logger.brukes til å referere til en spesifikk node. I vårt tilfelle, com.github.romankh3.logginglecture. la oss nå snakke om å konfigurere CONSOLE-appenderen:

# CONSOLE appender customization
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
Her ser vi at det er mulig å sette det spesifikke nivået som vedlegget skal begynne å fungere på. Her er et eksempel på hva som faktisk skjer: anta at en melding med INFO-nivået mottas av loggingsnoden og sendes til vedlegget som er tildelt den. Hvis appenderens terskel er satt til WARN, mottar den loggoppføringen, men gjør ingenting med den. Deretter må vi bestemme hvilken layout meldingen skal bruke. Jeg bruker PatternLayout i eksemplet, men det er mange andre alternativer. Vi vil ikke dekke dem i denne artikkelen. Eksempel på konfigurering av FILE-tillegget:

# File appender customization
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
Du kan konfigurere den spesifikke filen som loggoppføringer skal skrives til, som du kan se fra denne linjen:

log4j.appender.FILE.File=./target/logging/logging.log
Oppføringen skrives til logging.logfilen. For å unngå problemer med filstørrelsen, kan du konfigurere maksimum, som i dette tilfellet er 1 MB. MaxBackupIndexangir hvor mange slike loggfiler det vil være. Hvis vi trenger å lage flere filer enn dette, vil den første filen bli slettet. For å se på et ekte eksempel hvor logging er konfigurert, kan du gå til det offentlige depotet på GitHub.

Forsterk det vi har diskutert

Prøv på egenhånd å gjøre alt vi har beskrevet:
  • Lag ditt eget prosjekt som ligner på eksemplet ovenfor.
  • Hvis du vet hvordan du bruker Maven, bruk den. Hvis ikke, les denne veiledningen, som beskriver hvordan du kobler til biblioteket.

oppsummert

  1. Vi snakket om loggløsningene som finnes i Java.
  2. Nesten alle de kjente loggbibliotekene ble skrevet av én person :D
  3. Vi lærte hva som bør og ikke bør logges.
  4. Vi fant ut loggnivåer.
  5. Vi ble introdusert for logging noder.
  6. Vi så på hva en appender er og hva den er til for.
  7. Vi opprettet en log4j.proterties-fil trinn for trinn.

Ytterligere materialer

  1. CodeGym: Loggertime
  2. Weekly Geekly: Java-logging. Hei Verden
  3. Kodingskrekk: problemet med logging
  4. YouTube: Forstå Java Logging Hell - Grunnleggende. Java Logging Hell & Hvordan holde seg unna det
  5. Log4j: Vedlegg
  6. Log4j: Layout
Se også min andre artikkel:
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION