Flogger Vloeiend loggen

1. Overzicht

In deze tutorial gaan we het hebben over het Flogger-framework, een vloeiende logging-API voor Java ontworpen door Google.

2. Waarom Flogger gebruiken?

Met alle logging-frameworks die momenteel op de markt zijn, zoals Log4j en Logback, waarom hebben we nog een ander logging-framework nodig?

Het blijkt dat Flogger verschillende voordelen heeft ten opzichte van andere frameworks - laten we eens kijken.

2.1. Leesbaarheid

De vloeiende aard van de API van Flogger zorgt voor een lange weg om het leesbaarder te maken.

Laten we eens kijken naar een voorbeeld waarin we elke tien iteraties een bericht willen loggen.

Met een traditioneel logboekkader zouden we zoiets zien als:

int i = 0; // ... if (i% 10 == 0) {logger.info ("Dit logboek toont elke 10 iteraties"); i ++; }

Maar nu, met Flogger, kan het bovenstaande worden vereenvoudigd tot:

logger.atInfo (). every (10) .log ("Dit logboek toont elke 10 iteraties");

Hoewel je zou beweren dat de Flogger-versie van de logger-instructie er iets meer uitgebreid uitziet dan de traditionele versies, het maakt meer functionaliteit mogelijk en leidt uiteindelijk tot beter leesbare en expressieve log-statements.

2.2. Prestatie

Logging-objecten zijn geoptimaliseerd zolang we niet bellen toString op de gelogde objecten:

Gebruiker gebruiker = nieuwe gebruiker (); logger.atInfo (). log ("De gebruiker is:% s", gebruiker);

Als we loggen, zoals hierboven weergegeven, heeft de backend de mogelijkheid om de logging te optimaliseren. Aan de andere kant, als we bellen toString direct, of voeg de strings samen, dan gaat deze kans verloren:

logger.atInfo (). log ("Deze gebruiker is:% s", user.toString ()); logger.atInfo (). log ("Deze gebruiker is:% s" + gebruiker);

2.3. Uitbreidbaarheid

Het Flogger-framework dekt al de meeste basisfunctionaliteit die we van een logging-framework mogen verwachten.

Er zijn echter gevallen waarin we de functionaliteit zouden moeten uitbreiden. In deze gevallen is het mogelijk om de API uit te breiden.

Momenteel vereist dit een aparte ondersteunende klasse. We zouden bijvoorbeeld de Flogger API kunnen uitbreiden door een UserLogger klasse:

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

Dit kan handig zijn in gevallen waarin we het bericht consistent willen opmaken. De UserLogger zou dan de implementatie voor de aangepaste methoden verzorgen forUserId (tekenreeks-id) en withUsername (String gebruikersnaam).

Om dit te doen, de UserLogger klasse zal de AbstractLogger class en zorg voor een implementatie voor de API. Als we kijken FluentLogger, het is gewoon een logger zonder aanvullende methoden, daarom kunnen we begin met het kopiëren van deze klasse zoals ze is, en bouw vervolgens vanaf deze basis op door er methoden aan toe te voegen.

2.4. Efficiëntie

Traditionele frameworks maken op grote schaal gebruik van varargs. Deze methoden vereisen een nieuw Voorwerp[] worden toegewezen en gevuld voordat de methode kan worden aangeroepen. Bovendien moeten alle ingevoerde fundamentele typen automatisch worden ingekaderd.

Dit alles kost extra bytecode en latentie op de oproepsite. Het is bijzonder jammer als de log-instructie niet echt is ingeschakeld. De kosten worden duidelijker in logboeken op foutopsporingsniveau die vaak in lussen verschijnen. Flogger neemt deze kosten weg door varargs volledig te vermijden.

Flogger omzeilt dit probleem door een vloeiende oproepketen te gebruiken waaruit logboekinstructies kunnen worden opgebouwd. Hierdoor kan het framework slechts een klein aantal overrides hebben voor het logboek methode, en dus zaken als varargs en auto-boxing kunnen vermijden. Dit betekent dat de API een verscheidenheid aan nieuwe functies kan accommoderen zonder een combinatorische explosie.

Een typisch raamwerk voor logboekregistratie zou de volgende methoden hebben:

niveau (String, Object) niveau (String, Object ...)

waar niveau kan een van de ongeveer zeven namen op logboekniveau zijn (erge, ernstige bijvoorbeeld), evenals een canonieke logmethode die een extra logboekniveau accepteert:

log (niveau, object ...)

Daarnaast zijn er meestal varianten van de methoden die een oorzaak hebben (a Gooibaar instantie) die is gekoppeld aan de log-instructie:

level (Throwable, String, Object) level (Throwable, String, Object ...)

Het is duidelijk dat de API drie zorgen koppelt aan één methodeaanroep:

  1. Het probeert het logniveau te specificeren (methode keuze)
  2. Probeert metagegevens toe te voegen aan de log-instructie (Gooibaar oorzaak)
  3. En ook, het logboekbericht en de argumenten specificeren.

Deze aanpak vermenigvuldigt snel het aantal verschillende logboekregistratiemethoden dat nodig is om aan deze onafhankelijke zorgen te voldoen.

We kunnen nu zien waarom het belangrijk is om twee methoden in de keten te hebben:

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

Laten we nu eens kijken hoe we het in onze codebase kunnen gebruiken.

3. Afhankelijkheden

Het is vrij eenvoudig om Flogger in te stellen. We hoeven alleen maar toe te voegen flogger en flogger-systeem-backend naar onze pom:

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

Nu deze afhankelijkheden zijn ingesteld, kunnen we nu verder gaan met het verkennen van de API die tot onze beschikking staat.

4. Het verkennen van de Fluent API

Laten we eerst een statisch bijvoorbeeld voor onze logger:

privé statische finale FluentLogger-logger = FluentLogger.forEnclosingClass ();

En nu kunnen we beginnen met loggen. We beginnen met iets eenvoudigs:

int resultaat = 45/3; logger.atInfo (). log ("Het resultaat is% d", resultaat);

De logboekberichten kunnen elk van Java's gebruiken printf formaatspecificaties, zoals % s,% d of % 016x.

4.1. Werk vermijden op logboeklocaties

Flogger-makers raden aan om geen werk te doen op de log-site.

Laten we zeggen dat we de volgende langlopende methode hebben om de huidige status van een component samen te vatten:

openbare statische String collectSummaries () {longRunningProcess (); int items = 110; int s = 30; return String.format ("% d seconden verstreken tot dusver.% d items in afwachting van verwerking", s, items); }

Het is verleidelijk om te bellen collectSummaries direct in onze logverklaring:

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

Ongeacht de geconfigureerde logniveaus of snelheidsbeperking, kan het collectSummaries methode wordt nu elke keer aangeroepen.

De kosten van uitgeschakelde logboekregistratie-instructies vrijwel gratis maken, vormt de kern van het logging-framework. Dit betekent op zijn beurt dat er meer van hen zonder schade in de code kunnen worden gelaten. Door het logboek te schrijven zoals we net deden, wordt dit voordeel weggenomen.

In plaats daarvan zouden we de LazyArgs.lazy methode:

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

Nu wordt er bijna geen werk verricht op de log-site - gewoon het maken van een instantie voor de lambda-uitdrukking. Flogger zal deze lambda alleen evalueren als het van plan is het bericht daadwerkelijk te loggen.

Hoewel het is toegestaan ​​om logboekinstructies te bewaken met is ingeschakeld:

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

Dit is niet nodig en we moeten het vermijden omdat Flogger deze controles voor ons doet. Deze benadering bewaakt ook alleen logboekverklaringen op niveau en helpt niet met tariefbeperkte logboekverklaringen.

4.2. Omgaan met uitzonderingen

Hoe zit het met uitzonderingen, hoe gaan we ermee om?

Nou, Flogger wordt geleverd met een metStackTrace methode die we kunnen gebruiken om een Gooibaar voorbeeld:

probeer {int resultaat = 45/0; } catch (RuntimeException re) {logger.atInfo (). withStackTrace (StackSize.FULL) .withCause (re) .log ("Message"); }

Waar metStackTrace neemt als argument de Grootte van de hoop enum met constante waarden KLEIN, MIDDEL, GROOT of VOLLEDIG. Een stacktracering gegenereerd door metStackTrace () zal verschijnen als een LogSiteStackTrace uitzondering in de standaard java.util.logging backend. Andere backends kunnen ervoor kiezen om dit anders af te handelen.

4.3. Logboekconfiguratie en niveaus

Tot nu toe hebben we gebruikt logger.atInfo in de meeste van onze voorbeelden, maar Flogger ondersteunt ook veel andere niveaus. We zullen deze bekijken, maar laten we eerst introduceren hoe u de logboekregistratie-opties configureert.

Om logboekregistratie te configureren, gebruiken we de LoggerConfig klasse.

Als we bijvoorbeeld het logboekregistratieniveau willen instellen op FIJN:

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

En Flogger ondersteunt verschillende logboekregistratieniveaus:

logger.atInfo (). log ("Info Bericht"); logger.atWarning (). log ("Waarschuwingsbericht"); logger.atSevere (). log ("Ernstig bericht"); logger.atFine (). log ("Fijn bericht"); logger.atFiner (). log ("Fijner bericht"); logger.atFinest (). log ("Beste bericht"); logger.atConfig (). log ("Configuratiebericht");

4.4. Tariefbeperking

Hoe zit het met de kwestie van tariefbeperking? Hoe gaan we om met het geval waarin we niet elke iteratie willen loggen?

Flogger schiet ons te hulp met de elke (int n) methode:

IntStream.range (0, 100) .forEach (waarde -> {logger.atInfo (). Every (40) .log ("Dit logboek toont elke 40 iteraties =>% d", waarde);});

We krijgen de volgende uitvoer wanneer we de bovenstaande code uitvoeren:

18 september 2019 17:04:02 com.baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Dit logboek toont elke 40 herhalingen => 0 [CONTEXT ratelimit_count = 40] 18 september 2019 17:04:02 com. baeldung.flogger.FloggerUnitTest lambda $ givenAnInterval_shouldLogAfterEveryTInterval $ 0 INFO: Dit logboek toont elke 40 iteraties => 40 [CONTEXT ratelimit_count = 40] 18 september 2019 17:04:02 com.baeldung.flogger.FlogdavalgerUnit gegeven $ 0 log toont elke 40 iteraties => 80 [CONTEXT ratelimit_count = 40]

Wat als we elke 10 seconden willen loggen? Dan, we kunnen gebruiken atMostEvery (int n, TimeUnit-eenheid):

IntStream.range (0, 1_000_0000) .forEach (waarde -> {logger.atInfo (). AtMostEvery (10, TimeUnit.SECONDS) .log ("Dit logboek toont [elke 10 seconden] =>% d", waarde); });

Hiermee wordt het resultaat nu:

18 september 2019 17:08:06 com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Dit logboek toont [elke 10 seconden] => 0 [CONTEXT ratelimit_period = "10 SECONDS"] 18 september 2019 5:08 : 16 PM com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Dit logboek toont [elke 10 seconden] => 3545373 [CONTEXT ratelimit_period = "10 SECONDS [overgeslagen: 3545372]" 18 september 2019 5:08:26 PM com.baeldung.flogger.FloggerUnitTest lambda $ givenATimeInterval_shouldLogAfterEveryTimeInterval $ 1 INFO: Dit logboek toont [elke 10 seconden] => 7236301 [CONTEXT ratelimit_period = "10 SECONDS [overgeslagen: 3690927]"]

5. Flogger gebruiken met andere backends

Dus, wat als we zouden willen voeg Flogger toe aan onze bestaande applicatie die al gebruik maakt van bijvoorbeeld Slf4j of Log4j bijvoorbeeld? Dit kan handig zijn in gevallen waarin we zouden willen profiteren van onze bestaande configuraties. Flogger ondersteunt meerdere backends zoals we zullen zien.

5.1 Flogger met Slf4j

Het is eenvoudig om een ​​Slf4j-back-end te configureren. Eerst moeten we de flogger-slf4j-backend afhankelijkheid van onze pom:

 com.google.flogger flogger-slf4j-backend 0.4 

Vervolgens moeten we Flogger vertellen dat we graag een andere back-end willen gebruiken dan de standaard. We doen dit door een Flogger-fabriek te registreren via systeemeigenschappen:

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

En nu zal onze applicatie de bestaande configuratie gebruiken.

5.1 Flogger met Log4j

We volgen vergelijkbare stappen voor het configureren van Log4j-back-end. Laten we de flogger-log4j-backend afhankelijkheid van onze 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-extra's 1.2.17 

We moeten ook een Flogger-back-endfabriek registreren voor Log4j:

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

En dat is alles, onze applicatie is nu ingesteld om bestaande Log4j-configuraties te gebruiken!

6. Conclusie

In deze tutorial hebben we gezien hoe we het Flogger-framework kunnen gebruiken als alternatief voor de traditionele logging-frameworks. We hebben enkele krachtige functies gezien waarvan we kunnen profiteren bij het gebruik van het framework.

We hebben ook gezien hoe we onze bestaande configuraties kunnen gebruiken door verschillende back-ends zoals Slf4j en Log4j te registreren.

Zoals gewoonlijk is de broncode voor deze tutorial beschikbaar op GitHub.


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