Flogger Flytende logging

1. Oversikt

I denne veiledningen skal vi snakke om Flogger-rammeverket, et flytende logg-API for Java designet av Google.

2. Hvorfor bruke Flogger?

Med alle loggerammene som for tiden er i markedet, som Log4j og Logback, hvorfor trenger vi enda et loggingsrammeverk?

Det viser seg at Flogger har flere fordeler i forhold til andre rammer - la oss ta en titt.

2.1. Lesbarhet

Den flytende naturen til Flogger API går langt for å gjøre den mer lesbar.

La oss se på et eksempel der vi vil logge en melding hver tiende iterasjon.

Med et tradisjonelt loggingsrammeverk, ville vi se noe sånt som:

int i = 0; // ... if (i% 10 == 0) {logger.info ("Denne loggen viser hver 10. iterasjon"); i ++; }

Men nå, med Flogger, kan ovenstående forenkles til:

logger.atInfo (). hver (10) .log ("Denne loggen viser hver 10. iterasjon");

Mens man vil hevde at Flogger-versjonen av loggeruttalelsen ser litt mer utdypende ut enn de tradisjonelle versjonene, det tillater større funksjonalitet og fører til slutt til mer lesbare og uttrykksfulle logguttalelser.

2.2. Opptreden

Loggobjekter er optimalisert så lenge vi unngår å ringe toString på de loggede objektene:

Brukerbruker = ny bruker (); logger.atInfo (). log ("Brukeren er:% s", bruker);

Hvis vi logger, som vist ovenfor, har backend muligheten til å optimalisere loggingen. På den annen side, hvis vi ringer toString direkte, eller sammenkoble strengene, så er denne muligheten tapt:

logger.atInfo (). log ("Denne brukeren er:% s", user.toString ()); logger.atInfo (). log ("Denne brukeren er:% s" + bruker);

2.3. Utvidbarhet

Flogger-rammeverket dekker allerede det meste av den grunnleggende funksjonaliteten som vi forventer fra et loggingsrammeverk.

Imidlertid er det tilfeller der vi trenger å legge til funksjonaliteten. I disse tilfellene er det mulig å utvide API.

Foreløpig krever dette en egen støtteklasse. Vi kan for eksempel utvide Flogger API ved å skrive en UserLogger klasse:

logger.at (INFO) .forUserId (id) .withUsername (username) .log ("Message:% s", param);

Dette kan være nyttig i tilfeller der vi vil formatere meldingen konsekvent. De UserLogger vil da gi implementeringen for de tilpassede metodene forUserId (streng-id) og withUsername (String username).

Å gjøre dette, de UserLogger klassen må utvide AbstractLogger klasse og gi en implementering for API. Hvis vi ser på FluentLogger, det er bare en logger uten tilleggsmetoder, vi kan derfor begynn med å kopiere denne klassen som den er, og bygg deretter opp fra dette fundamentet ved å legge til metoder for det.

2.4. Effektivitet

Tradisjonelle rammer bruker i stor grad varargs. Disse metodene krever en ny Gjenstand[] som skal tildeles og fylles ut før metoden kan påberopes. I tillegg må alle grunnleggende typer som sendes inn, være automatisk innrammet.

Alt dette koster ekstra bytekode og ventetid på anropssiden. Det er spesielt uheldig hvis loggerklæringen ikke er aktivert. Kostnaden blir tydeligere i feilsøkingslogger som ofte vises i løkker. Flogger grøfter disse kostnadene ved å unngå varargs helt.

Flogger omgir dette problemet ved å bruke en flytende samtalekjede som loggsetninger kan bygges fra. Dette gjør at rammeverket bare har et lite antall overstyringer til Logg metode, og dermed være i stand til å unngå ting som varargs og autoboksing. Dette betyr at API-en kan ta imot en rekke nye funksjoner uten en kombinatorisk eksplosjon.

Et typisk loggingsrammeverk vil ha disse metodene:

nivå (streng, objekt) nivå (streng, objekt ...)

hvor nivå kan være ett av omtrent syv loggnivinnavn (alvorlig for eksempel), samt å ha en kanonisk loggmetode som godtar et ekstra loggnivå:

logg (nivå, objekt ...)

I tillegg til dette er det vanligvis varianter av metodene som tar en årsak (a Kastbar forekomst) som er tilknyttet logguttalelsen:

nivå (kastbar, streng, objekt) nivå (kastbar, streng, objekt ...)

Det er klart at API-en kobler tre bekymringer til en metodeanrop:

  1. Det prøver å spesifisere loggnivå (metodevalg)
  2. Prøver å knytte metadata til logguttalelsen (Kastbar årsaken)
  3. Og også angi loggmeldingen og argumentene.

Denne tilnærmingen multipliserer raskt antall forskjellige loggningsmetoder som trengs for å tilfredsstille disse uavhengige bekymringene.

Vi kan nå se hvorfor det er viktig å ha to metoder i kjeden:

logger.atInfo (). withCause (e) .log ("Melding:% s", arg);

La oss nå se på hvordan vi kan bruke den i kodebasen vår.

3. Avhengigheter

Det er ganske enkelt å sette opp Flogger. Vi trenger bare å legge til flogger og flogger-system-backend til vår pom:

  com.google.flogger flogger 0.4 com.google.flogger flogger-system-backend 0.4 runtime 

Når disse avhengighetene er satt opp, kan vi nå gå videre og utforske API-en som vi har til rådighet.

4. Utforske Fluent API

Først av, la oss erklære en statisk eksempel for loggeren vår:

private static final FluentLogger logger = FluentLogger.forEnclosingClass ();

Og nå kan vi begynne å logge. Vi begynner med noe enkelt:

int resultat = 45/3; logger.atInfo (). log ("Resultatet er% d", resultat);

Loggmeldingene kan bruke hvilken som helst Java printf formatspesifikatorer, for eksempel % s,% d eller % 016x.

4.1. Unngå arbeid på loggsteder

Flogger-skapere anbefaler at vi unngår å utføre arbeid på loggstedet.

La oss si at vi har følgende langvarige metode for å oppsummere den nåværende tilstanden til en komponent:

offentlig statisk String collectSummaries () {longRunningProcess (); int-elementer = 110; int s = 30; return String.format ("% d sekunder gått så langt.% d varer i påvente av behandling", s, elementer); }

Det er fristende å ringe samle sammendrag direkte i loggoppgaven vår:

logger.atFine (). log ("stats =% s", collectSummaries ());

Uavhengig av de konfigurerte loggnivåene eller hastighetsbegrensende, er imidlertid samle sammendrag metoden vil nå bli kalt hver gang.

Å gjøre kostnadene for deaktiverte logguttalelser praktisk talt gratis er kjernen i loggingsrammeverket. Dette betyr igjen at flere av dem kan være intakte i koden uten skade. Å skrive loggoppgaven som vi nettopp gjorde, fjerner denne fordelen.

I stedet bør vi bruke LazyArgs.lazy metode:

logger.atFine (). log ("stats =% s", LazyArgs.lazy (() -> collectSummaries ()));

Nå er det nesten ikke gjort noe arbeid på loggstedet - bare forekomst for lambda-uttrykket. Flogger vil bare evaluere denne lambdaen hvis den faktisk har til hensikt å logge meldingen.

Selv om det er lov å beskytte logguttalelser ved hjelp av Er på:

if (logger.atFine (). isEnabled ()) {logger.atFine (). log ("summaries =% s", collectSummaries ()); }

Dette er ikke nødvendig, og vi bør unngå det fordi Flogger gjør disse kontrollene for oss. Denne tilnærmingen beskytter også bare logguttalelser etter nivå og hjelper ikke med satsbegrensede loggutsagn.

4.2. Å håndtere unntak

Hva med unntak, hvordan håndterer vi dem?

Flogger kommer med en medStackTrace metode som vi kan bruke til å logge en Kastbar forekomst:

prøv {int resultat = 45/0; } fange (RuntimeException re) {logger.atInfo (). withStackTrace (StackSize.FULL) .withCause (re) .log ("Message"); }

Hvor medStackTrace tar som argument StackSize enum med konstante verdier LITEN MEDIUM STOR eller FULL. Et stakkspor generert av withStackTrace () vil dukke opp som en LogSiteStackTrace unntak i standard java.util.logging baksiden. Andre backends kan velge å håndtere dette annerledes skjønt.

4.3. Loggkonfigurasjon og nivåer

Så langt har vi brukt logger.atInfo i de fleste eksemplene våre, men Flogger støtter mange andre nivåer. Vi ser på disse, men la oss først introdusere hvordan du konfigurerer loggingsalternativene.

For å konfigurere logging bruker vi LoggerConfig klasse.

For eksempel når vi vil sette loggningsnivået til FINT:

LoggerConfig.of (logger) .setLevel (Level.FINE);

Og Flogger støtter forskjellige loggningsnivåer:

logger.atInfo (). log ("Info Message"); logger.atWarning (). log ("Advarsel"); logger.atSevere (). log ("Alvorlig melding"); logger.atFine (). log ("Fin melding"); logger.atFiner (). log ("Finere melding"); logger.atFinest (). log ("Fineste Message"); logger.atConfig (). log ("Config Message");

4.4. Prisbegrensning

Hva med spørsmålet om rentebegrensning? Hvordan håndterer vi saken der vi ikke vil logge hver iterasjon?

Flogger kommer oss til unnsetning med hver (int n) metode:

IntStream.range (0, 100) .forEach (verdi -> {logger.atInfo (). Hver (40) .log ("Denne loggen viser hver 40 iterasjoner =>% d", verdi);});

Vi får følgende utdata når vi kjører koden ovenfor:

18. september 2019 17:04:02 com.baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Denne loggen viser hver 40 iterasjoner => 0 [CONTEXT ratelimit_count = 40] 18. september 2019 17:04:02 com. baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Denne loggen viser hver 40 iterasjoner => 40 [CONTEXT ratelimit_count = 40] 18. september, 2019 17:04:02 com.baeldung.flogger.FloggerUnitTestInvala_Angel_InvalTalInvalTalInvalTalInvalTalInvaluTalInvaluTalInvaluTalInvaluTalInTalTalAtAlInTalTalInAlTalInAlTaTLaValTaLTaLaValTaTaTaLaValTaTaLaValTaTaTaLaWaTaLaTaAAA! logg viser hver 40 iterasjon => 80 [CONTEXT ratelimit_count = 40]

Hva om vi vil logge si hvert 10. sekund? Deretter, Vi kan bruke atMostEvery (int n, TimeUnit-enhet):

IntStream.range (0, 1_000_0000) .forEach (verdi -> {logger.atInfo (). AtMostEvery (10, TimeUnit.SECONDS) .log ("Denne loggen viser [hvert 10. sekund] =>% d", verdi); });

Med dette blir utfallet nå:

18. september 2019 17:08:06 com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Denne loggen viser [hvert 10. sekund] => 0 [CONTEXT ratelimit_period = "10 SECONDS"] 18. september 2019 5:08 : 16 PM com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Denne loggen viser [hvert 10. sekund] => 3545373 [CONTEXT ratelimit_period = "10 SECONDS [hoppet over: 3545372]"] 18. september 2019 5:08 PM com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Denne loggen viser [hvert 10. sekund] => 7236301 [CONTEXT ratelimit_period = "10 SECONDS [hoppet over: 3690927]"]

5. Bruke Flogger With Other Backends

Så hva om vi vil legg til Flogger i vår eksisterende applikasjon som allerede bruker si Slf4j eller Log4j for eksempel? Dette kan være nyttig i tilfeller der vi ønsker å dra nytte av våre eksisterende konfigurasjoner. Flogger støtter flere backends som vi får se.

5.1 Flogger med Slf4j

Det er enkelt å konfigurere en Slf4j-bakside. Først må vi legge til flogger-slf4j-backend avhengighet til vår pom:

 com.google.flogger flogger-slf4j-backend 0.4 

Deretter må vi fortelle Flogger at vi ønsker å bruke en annen back-end fra standard. Vi gjør dette ved å registrere en Flogger-fabrikk gjennom systemegenskaper:

System.setProperty ("flogger.backend_factory", "com.google.common.flogger.backend.slf4j.Slf4jBackendFactory # getInstance");

Og nå vil applikasjonen vår bruke den eksisterende konfigurasjonen.

5.1 Flogger med Log4j

Vi følger lignende trinn for å konfigurere Log4j back-end. La oss legge til flogger-log4j-backend avhengighet til vår pom:

 com.google.flogger flogger-log4j-backend 0.4 com.sun.jmx jmxri com.sun.jdmk jmxtools javax.jms jms log4j log4j 1.2.17 log4j apache-log4j-ekstras 1.2.17 

Vi må også registrere en Flogger back-end-fabrikk for Log4j:

System.setProperty ("flogger.backend_factory", "com.google.common.flogger.backend.log4j.Log4jBackendFactory # getInstance");

Og det er det, applikasjonen vår er nå konfigurert til å bruke eksisterende Log4j-konfigurasjoner!

6. Konklusjon

I denne veiledningen har vi sett hvordan vi kan bruke Flogger-rammeverket som et alternativ for de tradisjonelle loggerammene. Vi har sett noen kraftige funksjoner som vi kan dra nytte av når vi bruker rammeverket.

Vi har også sett hvordan vi kan utnytte våre eksisterende konfigurasjoner ved å registrere forskjellige bakenden som Slf4j og Log4j.

Som vanlig er kildekoden for denne opplæringen tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found