Tekuća prijava Floggera

1. Pregled

U ovom uputstvu govorit ćemo o okviru Flogger, tečnom API-ju za bilježenje za Javu koji je dizajnirao Google.

2. Zašto koristiti Flogger?

Uz sve okvire evidentiranja koji su trenutno na tržištu, poput Log4j i Logback, zašto nam treba još jedan okvir za prijavu?

Ispada da Flogger ima nekoliko prednosti u odnosu na druge okvire - pogledajmo.

2.1. Čitljivost

Tečna priroda Floggerovog API-a uvelike ga čini čitljivim.

Pogledajmo primjer gdje želimo zabilježiti poruku svakih deset ponavljanja.

Uz tradicionalni okvir zapisivanja, vidjeli bismo nešto poput:

int i = 0; // ... if (i% 10 == 0) {logger.info ("Ovaj zapisnik prikazuje svakih 10 ponavljanja"); i ++; }

Ali sada, s Floggerom, gore navedeno može se pojednostaviti na:

logger.atInfo (). svaki (10) .log ("Ovaj zapisnik prikazuje svakih 10 iteracija");

Iako bi netko tvrdio da Floggerova verzija izjave zapisnika izgleda malo opširnije od tradicionalnih verzija, dopušta veću funkcionalnost i u konačnici dovodi do čitljivijih i izražajnijih dnevničkih izjava.

2.2. Izvođenje

Objekti za bilježenje optimizirani su sve dok izbjegavamo pozivanje toString na prijavljenim objektima:

Korisnik korisnik = novi korisnik (); logger.atInfo (). log ("Korisnik je:% s", korisnik);

Ako se prijavimo, kao što je gore prikazano, pozadina ima priliku optimizirati bilježenje. S druge strane, ako nazovemo toString izravno, ili spojite žice, tada se ova prilika gubi:

logger.atInfo (). log ("Ovaj korisnik je:% s", user.toString ()); logger.atInfo (). log ("Ovaj korisnik je:% s" + korisnik);

2.3. Proširivost

Okvir Flogger već pokriva većinu osnovnih funkcionalnosti koje bismo očekivali od okvira zapisivanja.

Međutim, postoje slučajevi kada bismo trebali dodati funkcionalnost. U tim je slučajevima moguće proširiti API.

Trenutno je za to potrebna posebna klasa potpore. Na primjer, mogli bismo proširiti Flogger API tako da napišemo UserLogger razred:

logger.at (INFO) .forUserId (id) .withUsername (korisničko ime) .log ("Poruka:% s", param);

To bi moglo biti korisno u slučajevima kada želimo dosljedno formatirati poruku. The UserLogger tada bi osigurao implementaciju prilagođenih metoda forUserId (ID niza) i withUsername (korisničko ime niza).

Uraditi ovo, the UserLogger klasa morat će produžiti AbstractLogger klase i osigurati implementaciju za API. Ako pogledamo FluentLogger, to je samo loger bez dodatnih metoda, možemo, dakle, započnite s kopiranjem ove klase kakva jest, a zatim nadogradite iz ove temelje dodajući joj metode.

2.4. Učinkovitost

Tradicionalni okviri opsežno koriste vararge. Ove metode zahtijevaju novi Objekt[] koji se dodjeljuju i popunjavaju prije nego što se metoda može pozvati. Uz to, svi predani osnovni tipovi moraju biti automatski označeni.

Sve to košta dodatni bajtkod i kašnjenje na mjestu poziva. To je posebno nesretno ako izraz zapisnika zapravo nije omogućen. Cijena postaje očiglednija u zapisnicima na razini otklanjanja pogrešaka koji se često pojavljuju u petljama. Flogger se odriče tih troškova potpuno izbjegavajući vararge.

Flogger zaobilazi ovaj problem koristeći tečni lanac poziva iz kojeg se mogu graditi izvještaji o bilježenju. To omogućuje da okvir ima samo mali broj poništavanja zapisnik metodom, i na taj način moći izbjeći stvari poput vararga i auto-boksa. To znači da API može primiti razne nove značajke bez kombinatorne eksplozije.

Tipični okvir za evidentiranje imao bi ove metode:

razina (niz, objekt) razina (niz, objekt ...)

gdje nivo može biti jedno od oko sedam imena razine dnevnika (ozbiljna na primjer), kao i posjedovanje kanonske metode dnevnika koja prihvaća dodatnu razinu dnevnika:

zapisnik (nivo, objekt ...)

Uz to, obično postoje varijante metoda koje imaju uzrok (a Bacljivo instanca) koja je povezana s izrazom dnevnika:

razina (bacajući, niz, objekt) razina (bacajući, niz, objekt ...)

Jasno je da API spaja tri problema u jedan poziv metode:

  1. Pokušava odrediti razinu dnevnika (izbor metode)
  2. Pokušaj pričvršćivanja metapodataka u izraz dnevnika (Bacljivo uzrok)
  3. I također, specificiranje poruke dnevnika i argumenata.

Ovaj pristup brzo umnožava broj različitih metoda bilježenja potrebnih za zadovoljenje ovih neovisnih problema.

Sada možemo vidjeti zašto je važno imati dvije metode u lancu:

logger.atInfo (). withCause (e) .log ("Poruka:% s", argument);

Pogledajmo sada kako ga možemo koristiti u našoj bazi kodova.

3. Ovisnosti

Postavljanje Floggera prilično je jednostavno. Samo trebamo dodati bičevac i flogger-system-backend našem pom:

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

Nakon postavljanja ovih ovisnosti, sada možemo nastaviti istraživati ​​API koji nam stoji na raspolaganju.

4. Istraživanje API-ja Fluent

Prvo, proglasimo a statički primjer za naš zapisnik:

privatni statički konačni zapisnik FluentLogger = FluentLogger.forEnclosingClass ();

I sada možemo početi s prijavom. Počet ćemo s nečim jednostavnim:

rezultat int = 45/3; logger.atInfo (). log ("Rezultat je% d", rezultat);

Poruke zapisnika mogu koristiti bilo koju od Java printf specifikatori formata, kao što su % s,% d ili % 016x.

4.1. Izbjegavanje rada na web lokacijama

Kreatori floggera preporučuju da izbjegavamo raditi na mjestu dnevnika.

Recimo da imamo sljedeću dugotrajnu metodu sažimanja trenutnog stanja komponente:

javni statički niz collectSummaries () {longRunningProcess (); int predmeta = 110; int s = 30; return String.format ("Do sada je prošlo% d sekundi.% d stavki na čekanju", s, stavki); }

Primamljivo je nazvati collectSažeci izravno u našoj izjavi dnevnika:

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

Bez obzira na konfigurirane razine dnevnika ili ograničenje brzine, collectSažeci metoda će se sada pozivati ​​svaki put.

Ostvarivanje troškova onemogućenih izjava za evidentiranje gotovo besplatnim je srž okvira za bilježenje. To pak znači da više njih može ostati netaknuto u kodu bez štete. Pisanje izvoda iz dnevnika kao što smo to upravo učinili oduzima ovu prednost.

Umjesto toga, trebali bismo koristiti LazyArgs.lazno metoda:

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

Sada se na mjestu dnevnika gotovo ne radi - samo izrada instance za lambda izraz. Flogger će ovu lambdu procijeniti samo ako namjerava poruku stvarno zabilježiti.

Iako je dopušteno čuvati izjave dnevnika pomoću jeOmogućeno:

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

To nije potrebno i trebali bismo to izbjegavati jer Flogger vrši ove provjere umjesto nas. Ovaj pristup također štiti izjave dnevnika po razinama i ne pomaže kod izjava dnevnika s ograničenom brzinom.

4.2. Suočavanje s iznimkama

Što kažete na iznimke, kako se s njima nositi?

Pa, Flogger dolazi s withStackTrace metoda koju možemo koristiti za bilježenje a Bacljivo primjer:

pokušajte {int rezultat = 45/0; } catch (RuntimeException re) {logger.atInfo (). withStackTrace (StackSize.FULL) .withCause (re) .log ("Message"); }

Gdje withStackTrace uzima kao argument StackSize enum s konstantnim vrijednostima MALO SREDNJE VELIKO ili PUNO. Trag stoga koji generira withStackTrace () prikazat će se kao LogSiteStackTrace iznimka u zadanom java.util.logging pozadina. Drugi se backendovi mogu odlučiti nositi se s tim drugačije.

4.3. Konfiguracija i razine evidentiranja

Do sada smo koristili drvosječa.atInfo u većini naših primjera, ali Flogger podržava mnoge druge razine. Pogledat ćemo ih, ali prvo, predstavimo kako konfigurirati opcije bilježenja.

Za konfiguriranje zapisivanja koristimo LoggerConfig razred.

Na primjer, kada želimo postaviti razinu zapisivanja na FINO:

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

A Flogger podržava razne razine bilježenja:

logger.atInfo (). log ("Info poruka"); logger.atWarning (). log ("Poruka upozorenja"); logger.atSevere (). log ("Stroga poruka"); logger.atFine (). log ("Fine Message"); logger.atFiner (). log ("Finija poruka"); logger.atFinest (). log ("Najfinija poruka"); logger.atConfig (). log ("Poruka o konfiguriranju");

4.4. Ograničenje stope

Što kažete na pitanje ograničenja stope? Kako postupamo u slučaju da ne želimo bilježiti svaku iteraciju?

Flogger nas spašava s svaki (int n) metoda:

IntStream.range (0, 100) .forEach (value -> {logger.atInfo (). Every (40) .log ("Ovaj zapisnik prikazuje svakih 40 iteracija =>% d", vrijednost);});

Sljedeći izlaz dobivamo kada pokrenemo gornji kod:

18. rujna 2019. 17:04:02 com.baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Ovaj zapisnik prikazuje svakih 40 ponavljanja => 0 [CONTEXT ratelimit_count = 40] 18. rujna 2019. 17:04:02 com. baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Ovaj dnevnik prikazuje svakih 40 iteracija => 40 [CONTEXT ratelimit_count = 40] 18. septembar 2019 17:04:02 com.baeldung.flogger.FloggerUnitTvestLastLogAnterVaNTAnLVgAntEnVinTvalLogAnterVinTvalLogAnterVinTvalLogAnterVinTvalLogAnterVinTvalLogAnterVinTvalLogAnterVinTvalLogAnterVanTvLagAnterVainTVLVNTVLVNTVLV zapis prikazuje svakih 40 iteracija => 80 [CONTEXT ratelimit_count = 40]

Što ako se želimo prijaviti recimo svakih 10 sekundi? Zatim, možemo koristiti atMostEvery (int n, jedinica vremenske jedinice):

IntStream.range (0, 1_000_0000) .forEach (vrijednost -> {logger.atInfo (). AtMostEvery (10, TimeUnit.SECONDS) .log ("Ovaj zapisnik prikazuje [svakih 10 sekundi] =>% d", vrijednost); });

Uz ovo, ishod sada postaje:

18. rujna 2019. 17:08:06 com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Ovaj zapisnik prikazuje [svakih 10 sekundi] => 0 [CONTEXT ratelimit_period = "10 SEKUNDI"] 18. rujna 2019. 5:08 : 16:00 com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Ovaj zapisnik prikazuje [svakih 10 sekundi] => 3545373 [CONTEXT ratelimit_period = "10 SEKUNDI [preskočeno: 3545372] 5:08, 26. septembar 2019., 5:08, 26. septembar 2019. PM com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Ovaj zapisnik prikazuje [svakih 10 sekundi] => 7236301 [CONTEXT ratelimit_period = "10 SEKUNDI [preskočeno: 3690927]"]

5. Korištenje Floggera s drugim paketima

Pa, što ako bismo htjeli dodajte Flogger u našu postojeću aplikaciju koja već koristi recimo Slf4j ili Log4j na primjer? To bi moglo biti korisno u slučajevima kada bismo željeli iskoristiti naše postojeće konfiguracije. Flogger podržava višestruke pozadinske datoteke, kao što ćemo vidjeti.

5.1 Bič sa Slf4j

Jednostavno je konfigurirati pozadinu Slf4j. Prvo, moramo dodati flogger-slf4j-backend ovisnost o našoj pom:

 com.google.flogger flogger-slf4j-backend 0.4 

Dalje, moramo reći Floggeru da bismo željeli koristiti drugačiji back-end od zadanog. To radimo registracijom tvornice Flogger putem svojstava sustava:

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

A sada će naša aplikacija koristiti postojeću konfiguraciju.

5.1 Aparat za logiranje s log4j

Slijedimo slične korake za konfiguriranje pozadine Log4j. Dodajmo i flogger-log4j-backend ovisnost o našoj 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-extras 1.2.17 

Također trebamo registrirati tvorničku tvornicu Flogger za Log4j:

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

I to je to, naša je aplikacija sada postavljena za upotrebu postojećih Log4j konfiguracija!

6. Zaključak

U ovom uputstvu vidjeli smo kako koristiti okvir Flogger kao alternativu za tradicionalne okvire zapisivanja. Vidjeli smo neke moćne značajke od kojih možemo imati koristi prilikom upotrebe okvira.

Također smo vidjeli kako možemo iskoristiti naše postojeće konfiguracije registrirajući različite pozadine poput Slf4j i Log4j.

Kao i obično, izvorni kod za ovu lekciju dostupan je na GitHubu.