CodeGym /Java Blog /Willekeurig /Loggen: wat, hoe, waar en waarmee?
John Squirrels
Niveau 41
San Francisco

Loggen: wat, hoe, waar en waarmee?

Gepubliceerd in de groep Willekeurig
Hallo iedereen in de CodeGym-community! Loggen: wat, hoe, waar en waarmee?  - 1 Laten we het vandaag hebben over loggen:
  1. Wat het is, waarom het bestaat, wanneer je het moet gebruiken, wanneer je het moet vermijden.
  2. Welke logging-implementaties zijn beschikbaar in Java en wat u moet doen met al deze logging-opties.
  3. En logniveaus. We zullen bespreken wat apper is en hoe het correct te configureren.
  4. Knooppunten loggen en hoe ze correct te configureren zodat alles werkt zoals we willen.
Dit materiaal is bedoeld voor een breed publiek. Het zal duidelijk zijn voor iedereen die Java net leert kennen, evenals voor mensen die al aan het werk zijn maar alleen logger.info("log something"); Let's go!

Waarom heb je logboekregistratie nodig?

Laten we eens kijken naar enkele echte gevallen waarin logboekregistratie een probleem kan oplossen. Hier is een voorbeeld uit mijn werk. Er zijn punten waarop een applicatie integreert met andere services. Ik gebruik logging op deze punten om een ​​soort "alibi" vast te stellen : als de integratie niet werkt, wordt het gemakkelijk om erachter te komen welke kant het probleem heeft. Het is ook wenselijk om belangrijke informatie die is opgeslagen in een database te loggen. Bijvoorbeeld het aanmaken van een admin-gebruiker. Dit is precies het soort dingen dat goed zou zijn om te loggen.

Tools voor het inloggen op Java

Onder de bekende logging-oplossingen in Java kunnen we het volgende benadrukken:
  • Log4j
  • JUL — java.util.logging
  • JCL - Jakarta Commons-houtkap
  • Inloggen
  • SLF4J — Eenvoudige logboekregistratie voor Java
We zullen een overzicht van elk van hen geven. Dan nemen we een slf4j - log4j binding als basis van een praktische discussie. Dit lijkt nu misschien vreemd, maar maak je geen zorgen: aan het einde van het artikel zal alles duidelijk zijn.

Systeem.fout.println

In het begin was er System.err.println (het weergeven van logboekvermeldingen op de console). Zelfs vandaag de dag wordt deze techniek gebruikt om snel een log te maken bij het debuggen. Er zijn natuurlijk geen instellingen om hier te bespreken, dus onthoud deze methode en we gaan verder.

Log4j

Dit is een complete oplossing die ontwikkelaars uit noodzaak hebben gemaakt. Het resultaat is een heel interessant hulpmiddel dat u kunt gebruiken. Door verschillende omstandigheden is deze oplossing niet in de JDK beland, een feit dat de hele gemeenschap enorm van streek maakte. Log4j heeft configuratie-opties waarmee u loggen in het com.example.typepakket kunt inschakelen en uitschakelen in het com.example.type.genericsubpakket. Dit maakt het mogelijk om snel code uit te sluiten die niet gelogd hoeft te worden. Het is belangrijk op te merken dat er twee versies van Log4j zijn: 1.2.x en 2.xx, en deze zijn niet compatibel met elkaar . Log4j heeft de concepten van appender toegevoegd(een tool die wordt gebruikt om logboeken te schrijven) en lay-out (opmaak van logboeken). Hierdoor kunt u alleen loggen wat u nodig heeft en precies hoe u het nodig hebt. We zullen later meer over appender praten.

JUL — java.util.logging

Een van de belangrijkste voordelen van deze oplossing is dat JUL is opgenomen in de JDK (Java Development Kit). Helaas, toen het werd ontwikkeld, baseerden de makers het niet op het populaire Log4j-hulpprogramma, maar eerder op een oplossing van IBM. Dat besluit heeft gevolgen gehad. De realiteit is dat niemand JUL nu gebruikt. De logniveaus in JUL verschillen van wat Logback, Log4j en Slf4j hebben. Dit maakt het voor hen moeilijker om elkaar te begrijpen. Het maken van een logger is min of meer vergelijkbaar. Om dit te doen, moet u een import doen:

java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
De klassenaam wordt doorgegeven, zodat we weten waar onze logging vandaan komt. Vanaf Java 8 kunt u slagen voor Supplier<String>. Dit helpt ons alleen een regel te lezen en te maken wanneer we deze echt nodig hebben, in plaats van elke keer, zoals voorheen het geval was. Pas met de release van Java 8 hebben ontwikkelaars eindelijk belangrijke problemen opgelost en JUL echt bruikbaar gemaakt. Namelijk methoden met een Supplier<String> msgSupplierparameter, zoals hieronder weergegeven:

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

JCL - Jakarta Commons-houtkap

Omdat er lange tijd geen industriestandaard bestond met betrekking tot logging en veel mensen hun eigen aangepaste loggers maakten, werd besloten om JCL uit te brengen, een algemene verpakking die bovenop andere kon worden gebruikt. Waarom? Soms gebruikten afhankelijkheden die aan het project werden toegevoegd een andere logger dan die in het project. Daarom werden ze aan het project toegevoegd als transitieve afhankelijkheden, en dit veroorzaakte echte problemen bij het proberen alles samen te voegen. Helaas was de verpakking niet erg functioneel en voegde niets toe. Het zou waarschijnlijk handig zijn als iedereen JCL zou gebruiken. Maar dat is niet wat er is gebeurd, dus het gebruik van JCL is op dit moment niet het beste idee.

Inloggen

Het open-sourcepad is netelig... Dezelfde ontwikkelaar die Log4j schreef, schreef ook Logback als opvolger van het logging-framework. Het was gebaseerd op hetzelfde idee als Log4j. De verschillen in Logback zijn:
  • verbeterde prestatie
  • native ondersteuning voor Slf4j toegevoegd
  • uitgebreide filteropties
Logback vereist standaard geen configuratie en registreert alle gebeurtenissen op DEBUG-niveau en hoger. Als u wat aanpassingen nodig heeft, kunt u dit bereiken via een XML-configuratie:

<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 — Eenvoudige logboekregistratie voor Java

Ergens in 2006 verliet een van de grondleggers van Log4j het project en creëerde Slf4j (Simple Logging Facade for Java), een verpakking voor Log4j, JUL, common-logging en Logback. Zoals u kunt zien, zijn we zover gevorderd dat we een wrapper over een wrapper maken... In dit geval is het verdeeld in twee delen: een API die wordt gebruikt in de toepassing en een implementatie die wordt toegevoegd met afzonderlijke afhankelijkheden voor elk type logboekregistratie. Bijvoorbeeld, slf4j-log4j12.jaren slf4j-jdk14.jar. U moet de juiste implementatie aansluiten en dat is alles: uw hele project zal het gebruiken. Slf4j ondersteunt alle nieuwste functies, zoals het formatteren van tekenreeksen voor logboekregistratie. Eerder was er zo'n probleem. Laten we zeggen dat we een logboekitem als volgt maken:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
Door de aaneenschakelingsoperator userwordt het object stilletjes een string dankzij user.toString(). Dit kost tijd en vertraagt ​​het systeem. En dat kan in orde zijn als we de toepassing debuggen. We beginnen problemen te krijgen als het logniveau voor deze klasse INFO of hoger is. Met andere woorden, we zouden deze log-invoer niet moeten schrijven (voor INFO of hoger), en we zouden geen string-aaneenschakeling moeten gebruiken. In theorie zou de logboekbibliotheek dit zelf moeten aanpakken. Dit bleek namelijk het grootste probleem te zijn in de eerste versie van Log4j. Het leverde geen fatsoenlijke oplossing op, maar stelde in plaats daarvan voor om zoiets als dit te doen:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Dat wil zeggen, in plaats van één regel code voor loggen, stelden ze voor om er 3 te schrijven! Logging zou codewijzigingen tot een minimum moeten beperken, en de drie regels schenden duidelijk die algemene benadering. Slf4j had geen compatibiliteitsproblemen met de JDK en API, dus kwam er meteen een mooie oplossing naar voren:

log.debug("User {} connected from {}", user, request.getRemoteAddr());
waarbij {}staat voor tijdelijke aanduidingen voor de argumenten die aan de methode zijn doorgegeven. Dat wil zeggen, de eerste {}komt overeen met user, en de tweede {}komt overeen met request.getRemoteAddr(). Door het op deze manier te doen, voeren we alleen stringsamenvoeging uit als het logniveau vereist dat we de loginvoer schrijven. Daarna begon Sjf4j snel in populariteit te groeien. Op dit moment is het de beste oplossing. Laten we daarom eens kijken naar loggen met behulp van een slf4j-log4j12binding.

Wat moet er gelogd worden

Je moet natuurlijk niet alles loggen. Dit is vaak niet nodig en soms zelfs gevaarlijk. Als je bijvoorbeeld iemands persoonlijke gegevens logt en deze op de een of andere manier worden gelekt, zullen er echte problemen ontstaan, vooral bij projecten gericht op westerse markten. Maar er zijn ook dingen die je zeker moet loggen :
  1. Start/einde van de aanvraag. We moeten weten of de applicatie echt is gestart en geëindigd zoals verwacht.
  2. Veiligheidsproblemen. Hier zou het goed zijn om pogingen bij te houden om iemands wachtwoord te raden, gevallen waarin beheerders inloggen, enz.
  3. Bepaalde toepassingsstatussen . Bijvoorbeeld de overgang van de ene toestand naar de andere in een bedrijfsproces.
  4. Bepaalde foutopsporingsinformatie samen met het bijbehorende logniveau.
  5. Bepaalde SQL-scripts. Er zijn praktijkgevallen waarin dit nodig is. Maar nogmaals, door vakkundig de logniveaus aan te passen, kunt u uitstekende resultaten behalen.
  6. Lopende threads kunnen worden gelogd om te controleren of alles correct werkt.

Populaire fouten bij het loggen

Er zijn hier veel nuances, maar we zullen speciale aandacht besteden aan enkele veelvoorkomende fouten:
  1. Overmatig loggen. U moet niet elke stap loggen die theoretisch belangrijk zou kunnen zijn. Hier een goede vuistregel: houtblokken mogen niet meer dan 10% van de belasting uitmaken. Anders zullen er prestatieproblemen zijn.
  2. Loggen van alle gegevens in één bestand. Op een gegeven moment zal dit het erg moeilijk maken om het logboek te lezen/schrijven, om nog maar te zwijgen van het feit dat bepaalde systemen limieten hebben voor de bestandsgrootte.
  3. Onjuiste logboekniveaus gebruiken. Elk logboekniveau heeft duidelijke grenzen en deze moeten worden gerespecteerd. Als een grens onduidelijk is, kunt u met elkaar afspreken welk niveau u gaat gebruiken.

Niveaus loggen

x: Zichtbaar
FATAAL FOUT WAARSCHUWEN INFO DEBUGGEN SPOOR ALLE
UIT
FATAAL X
FOUT X X
WAARSCHUWEN X X X
INFO X X X X
DEBUGGEN X X X X X
SPOOR X X X X X X
ALLE X X X X X X X
Wat zijn logniveaus? Om op de een of andere manier een hiërarchie van logboekvermeldingen te creëren, zijn bepaalde conventies en afbakeningen nodig. Dit is de reden waarom logniveaus zijn geïntroduceerd. Het niveau wordt ingesteld in de applicatie. Als een item onder een bepaald niveau ligt, wordt het niet gelogd. We hebben bijvoorbeeld logboeken die we gebruiken bij het debuggen van de applicatie. Tijdens normaal gebruik (wanneer de applicatie wordt gebruikt voor het beoogde doel), zijn dergelijke logboeken niet nodig. Daarom is het logboekniveau hoger dan voor foutopsporing. Laten we eens kijken naar logniveaus met behulp van Log4j. Afgezien van JUL gebruiken andere oplossingen dezelfde logniveaus. Hier zijn ze in afnemende volgorde:
  • UIT: er worden geen logboekinvoeren geregistreerd; alles wordt genegeerd.
  • FATAL: een fout waardoor de toepassing niet verder kan worden uitgevoerd. Bijvoorbeeld: 'JVM-geheugenfout'.
  • FOUT: fouten op dit niveau duiden op problemen die moeten worden opgelost. De fout stopt de toepassing als geheel niet. Andere verzoeken kunnen correct werken.
  • WARN: logboekvermeldingen die een waarschuwing vertegenwoordigen. Er gebeurde iets onverwachts, maar het systeem kon het verzoek aan en voldeed
  • INFO: logboekvermeldingen die belangrijke acties in de toepassing aangeven. Dit zijn geen fouten of waarschuwingen. Het zijn verwachte systeemgebeurtenissen.
  • DEBUG: Logboekvermeldingen moeten de toepassing debuggen. Om ervoor te zorgen dat de toepassing precies doet wat wordt verwacht, of om de acties te beschrijven die door de toepassing worden ondernomen, dwz "Ingevoerde methode1".
  • TRACE: logboekvermeldingen met lagere prioriteit voor foutopsporing. Het laagste logboekniveau.
  • ALLES: een logboekniveau voor het schrijven van alle logboekvermeldingen van de toepassing.
Als het INFO-logniveau ergens in de applicatie is ingeschakeld, worden de invoer voor elk niveau gelogd, van INFO tot FATAL. Als het FATAL-logniveau is ingesteld, worden alleen logitems met dat niveau geschreven.

Loggen en verzenden van logs: Appender

Laten we eens kijken hoe dit allemaal werkt wanneer we Log4j gebruiken, wat voldoende mogelijkheden biedt voor het schrijven/verzenden van logs:
  • naar een bestand schrijven —DailyRollingFileAppender
  • om informatie naar de console te schrijven —ConsoleAppender
  • logboeken naar een database schrijven —JDBCAppender
  • het verzenden van logboeken via TCP/IP beheren —TelnetAppender
  • om ervoor te zorgen dat logboekregistratie geen negatieve invloed heeft op de prestaties —AsyncAppender
Er zijn nog een paar implementaties: een volledige lijst is hier beschikbaar . Trouwens, als de apper die je nodig hebt niet bestaat, is dat geen probleem. U kunt uw eigen appender schrijven door de Appender- interface te implementeren, die Log4j ondersteunt.

Knooppunten loggen

Voor demonstratiedoeleinden zullen we een Slf4j-interface gebruiken, met een implementatie van Log4j. Het maken van een logger is heel eenvoudig: in een klasse met de naam MainDemo, die wat logging zal doen, moeten we het volgende toevoegen:

org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
Dit zal een logger voor ons maken. Er zijn verschillende methoden beschikbaar om een ​​logboekinvoer te maken waarvan de namen aangeven welk logboekniveau zal worden gebruikt. Bijvoorbeeld:

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);
Hoewel we de klas passeren, is de definitieve naam de volledige naam van de klas, inclusief pakketten. Dit wordt gedaan zodat u later het loggen in knooppunten kunt verdelen en het logniveau en de appender voor elk knooppunt kunt configureren. De logger is bijvoorbeeld gemaakt in de com.github.romankh3.logginglecture.MainDemoklas. De naam vormt de basis voor het maken van een hiërarchie van logboekknooppunten. Het hoofdknooppunt is RootLogger op het hoogste niveau . Dit is het knooppunt dat alle logboekvermeldingen voor de gehele toepassing ontvangt. De resterende knooppunten kunnen worden weergegeven zoals hieronder weergegeven: Loggen: wat, hoe, waar en waarmee?  - 3Appenders zijn geconfigureerd voor specifieke logboekknooppunten. Nu gaan we naar het bestand log4j.properties kijken om een ​​voorbeeld te zien van hoe ze moeten worden geconfigureerd.

Een stapsgewijze handleiding voor het bestand log4j.properties

We zullen alles stap voor stap instellen en kijken wat er mogelijk is:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
Deze regel zegt dat we de CONSOLE-appender registreren, die de org.apache.log4j.ConsoleAppender-implementatie gebruikt. Deze appender schrijft informatie naar de console. Vervolgens registreren we nog een appender. Deze zal naar een bestand schrijven:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
Het is belangrijk op te merken dat de appenders zelf nog moeten worden geconfigureerd. Nadat we onze appenders hebben geregistreerd, kunnen we bepalen welke logniveaus en welke appenders op de knooppunten zullen worden gebruikt.

log4j.rootLogger=DEBUG, CONSOLE, BESTAND

  • log4j.rootLogger betekent dat we het hoofdknooppunt configureren, dat alle logboekvermeldingen bevat
  • Het eerste woord na het gelijkteken geeft het minimale logniveau aan om te schrijven (in ons geval is dit DEBUG)
  • Na de komma geven we alle te gebruiken appenders aan.
Om een ​​specifieker logboekknooppunt te configureren, gebruikt u een vermelding als deze:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
waar log4j.logger.wordt gebruikt om naar een specifiek knooppunt te verwijzen. com.github.romankh3.logginglecture. Laten we het in ons geval hebben over het configureren van de CONSOLE-appender:

# 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
Hier zien we dat het mogelijk is om het specifieke niveau in te stellen waarop de appender zal gaan werken. Hier is een voorbeeld van wat er feitelijk gebeurt: stel dat een bericht met het INFO-niveau wordt ontvangen door het logboekknooppunt en wordt doorgegeven aan de appender die eraan is toegewezen. Als de drempel van de appender is ingesteld op WARN, ontvangt deze de logboekvermelding maar doet er niets mee. Vervolgens moeten we beslissen welke lay-out het bericht zal gebruiken. Ik gebruik PatternLayout in het voorbeeld, maar er zijn nog veel meer opties. We behandelen ze niet in dit artikel. Voorbeeld van het configureren van de FILE-appender:

# 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
U kunt het specifieke bestand configureren waarnaar logboekvermeldingen worden weggeschreven, zoals te zien is op deze regel:

log4j.appender.FILE.File=./target/logging/logging.log
De invoer wordt naar het bestand geschreven logging.log. Om problemen met de bestandsgrootte te voorkomen, kunt u het maximum instellen, in dit geval 1 MB. MaxBackupIndexgeeft aan hoeveel van dergelijke logbestanden er zullen zijn. Als we meer bestanden moeten maken, wordt het eerste bestand verwijderd. Om naar een echt voorbeeld te kijken waar logboekregistratie is geconfigureerd, kun je naar de openbare repository op GitHub gaan.

Versterk wat we hebben besproken

Probeer zelf alles te doen wat we hebben beschreven:
  • Maak uw eigen project vergelijkbaar met ons voorbeeld hierboven.
  • Als je weet hoe je Maven moet gebruiken, gebruik het dan. Zo niet, lees dan deze tutorial, waarin wordt beschreven hoe je de bibliotheek aansluit.

samengevat

  1. We hebben het gehad over de logboekoplossingen die in Java bestaan.
  2. Bijna alle bekende logboekbibliotheken zijn door één persoon geschreven :D
  3. We hebben geleerd wat er wel en niet gelogd moet worden.
  4. We hebben logniveaus berekend.
  5. We maakten kennis met logboekknooppunten.
  6. We hebben gekeken naar wat een appender is en waar het voor dient.
  7. We hebben stap voor stap een bestand log4j.proterties gemaakt.

Aanvullende materialen

  1. CodeGym: Loggerles
  2. Wekelijkse Geekly: Java-logboekregistratie. Hallo Wereld
  3. Horror coderen: het probleem met loggen
  4. YouTube: Java Logging Hell begrijpen - de basis. Java Logging Hell en hoe je er buiten blijft
  5. Log4j: bijlage
  6. Log4j: Lay-out
Zie ook mijn andere artikel:
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION