Het extraheren van bruikbare informatie uit je logbestanden is van cruciaal belang voor het succes van je Java applicatie. Loggegevens geven je een waardevol inzicht in de prestaties, stabiliteit en bruikbaarheid van je applicatie.

Het analyseren van loggegevens lijkt misschien vervelend, maar dat hoeft het niet te zijn. Er zijn diverse hulpmiddelen voor het lezen, ontleden en consolideren van loggegevens. Basishulpmiddelen voor de commandoregel, zoals grep, uniq en sort, kunnen nuttige informatie uit logbestanden combineren en extraheren. Meer geavanceerde log-parsers zoals Logstash of Fluentd kunnen belangrijke gegevens uit uw logs extraheren in gemakkelijk doorzoekbare tokens. Cloud-gebaseerde logboekdiensten zoals SolarWinds® Loggly® slaan uw logboekgegevens voor u op en bieden geavanceerde analysemogelijkheden, zodat u zelf geen logboeken meer hoeft bij te houden.

Dit gedeelte gaat in op enkele methoden en hulpmiddelen voor het analyseren van logboeken met als doel uw toepassingen te verbeteren.

De meest voorkomende uitzonderingen vinden

Het vinden van de meest voorkomende uitzonderingen kan u helpen gebieden met slechte prestaties in uw Java-toepassing aan te wijzen. De meeste logging frameworks registreren het type uitzondering, het bericht van de uitzondering, en de methode waarin de uitzondering zich voordeed. Met Log4j ziet een logboek van uitzonderingen er ongeveer zo uit.

09:54:44.565 ERROR Log4jTest.MathClass - java.lang.ArithmeticException: / by zero10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: myFile (No such file or directory)10:00:10.157 ERROR Log4jTest.Log4jTest - java.io.FileNotFoundException: newFile (No such file or directory)

Als uw uitvoer stack traces bevat, zie dan de Parsing Multiline Stack Traces sectie van deze gids.

Laten we beginnen met een eenvoudig voorbeeld met behulp van het populaire GNU gereedschap grep. Daarna laten we zien hoe een hulpprogramma voor logboekbeheer het nog eenvoudiger kan maken.

Vinden van uitzonderingen op type met behulp van grep

Het volgende Unix-commando vindt uitzonderingen, extraheert het type uitzondering en telt het aantal keren dat het voorkomt. grep is een populair opdrachtregelhulpprogramma dat patroonherkenning uitvoert, terwijl uniq en sort de resultaten groeperen en sorteren, respectievelijk:

$ grep -o "\w*Exception" myLog.log | sort -r | uniq -c

In dit voorbeeld komt de reguliere expressie \w*Exception overeen met elk woord dat eindigt op “Uitzondering.” De vlag -o vertelt grep om alleen de delen van de uitvoer af te drukken die overeenkomen met de zoekreeks. sort -r sorteert het resultaat in omgekeerde volgorde, terwijl uniq -c de resultaten groepeert en het aantal keren dat het voorkomt telt. Als resultaat krijgen we een telling van uitzonderingen per type.

2 FileNotFoundException1 ArithmeticException

We kunnen grep ook gebruiken om het log te doorzoeken op elk specifiek geval van deze uitzonderingen.

$ grep ArithmeticException myLog.log09:54:44.565 ERROR Log4jTest.Log4jTest - java.lang.ArithmeticException: / by zero

Dit retourneert elke regel die een instantie van de zoek string ArithmeticException bevat, met de zoek string zelf gemarkeerd in de uitvoer.

Vinden van uitzonderingen per klasse met behulp van Grep

U kunt ook zoeken naar uitzonderingen door de klasse waarin ze zich voordeden. Voor eenregelige log-events groepeert het volgende commando het aantal uitzonderingen per klasse en per type. grep extraheert de klassenaam door te zoeken naar een woord gevolgd door een bepaald teken (in dit voorbeeld wordt een koppelteken gebruikt, hoewel elk teken kan worden gebruikt zolang het maar uniek is voor de entry). Hoewel dit karakter niet essentieel is, helpt het ons om de klassenaam in de log-event te lokaliseren. Met de OR operator (aangeduid met |), zal grep ook de naam van de uitzondering extraheren door te zoeken naar een woord dat de string “Uitzondering” bevat.

sed is een andere command-line utility die gebruikt kan worden om uitvoer van grep te formatteren. In dit voorbeeld verwijdert sed het speciale karakter uit onze uitvoer, evenals alle nieuwe-regeltekens. De uitvoer wordt dan gepijpt naar de commando’s uniq en sort om respectievelijk de resultaten te groeperen en te sorteren.

$ grep -o "w* -|\w*Exception" myLog.log | sed 'N; s/ -n/ /' | sort -r | uniq -c

Het resultaat brengt ons dichter bij de belangrijkste probleemgebieden:

2 Log4jTest FileNotFoundException1 MathClass ArithmeticException

Met deze resultaten kunnen we grep gebruiken om meer details te vinden over problemen die in specifieke klassen voorkomen. Bijvoorbeeld, het volgende commando haalt uitzonderingen op die zich voordeden in de MathClass klasse.

$ grep -e "MathClass.*Exception" myLog.log09:54:44.565 ERROR Log4jtest.MathClass - java.lang.ArithmeticException: / by zero

Een oplossing voor logboekbeheer gebruiken

De meeste oplossingen voor logboekbeheer bieden manieren om logboekvermeldingen te groeperen en te doorzoeken op basis van logboektype, berichtinhoud, klasse, methode en thread. Als uw logboeken al worden geanalyseerd en opgeslagen, kunnen veel oplossingen uitzonderingen grafisch weergeven en sorteren op het aantal keren dat ze voorkomen. Dit is een point and click operatie in Loggly, zodat u geen ingewikkelde grep commando’s uit het hoofd hoeft te leren. Loggly indexeert ook de logrecords, waardoor zoeken en tellen veel sneller gaat dan met grep. We kunnen bijvoorbeeld de veldverkenner gebruiken om een lijst met uitzonderingen en hun frequentie te bekijken, en vervolgens op een uitzondering klikken om alle relevante logs te bekijken.

De veldverkenner van Loggly gebruiken om snel logs te vinden op uitzonderingssoort.

Loggly biedt ook visualisatiehulpmiddelen die informatiever kunnen zijn dan de op tekst gebaseerde uitvoer van grep. Grafieken zoals deze kunnen u helpen bij het prioriteren van resources voor ontwikkelingssprints of voor patches na een nieuwe release.

Uitzonderingen op frequentie in Loggly in kaart brengen.

Ontdekken van productieproblemen

Wanneer het om productieproblemen gaat, is tijd van essentieel belang. Een kritieke fout in uw toepassing zal niet alleen uw gebruikers ongelukkig maken; het zal ook de verkoop verlagen en het vertrouwen in uw toepassing of service verminderen.

In de meeste gevallen kan het oplossen van een probleem worden onderverdeeld in drie belangrijke stappen:

  1. Verzamel informatie over het probleem
  2. Identificeer de oorzaak van het probleem
  3. Vind een oplossing en voorkom dat het probleem zich herhaalt

Verzamel informatie over het probleem

De eerste stap is het verzamelen van informatie over het probleem. Verzamel zoveel mogelijk informatie – schermafbeeldingen, crashrapporten, logboeken, links (voor webdiensten), enzovoort – om de mogelijke oorzaken te beperken. U wilt dat de gebruiker die het probleem heeft ondervonden gedetailleerde informatie verstrekt over de gebeurtenis, inclusief: wanneer en waar in het programma het probleem zich voordeed, zijn of haar acties in de aanloop naar het probleem, de gebruiksomgeving, en elk vreemd of ongebruikelijk gedrag van het programma voor en na het optreden van het probleem.

Van daaruit kunt u beginnen met het verzamelen van logboekinformatie. Als uw toepassing een gehoste service is, begint u met het ophalen van logboeken van de web- en applicatieservers. Als uw toepassing een gedistribueerd softwarepakket is, vraag de gebruiker dan om loggegevens op te nemen in zijn of haar foutenrapport. Als alternatief, als uw applicatie log events naar een gecentraliseerde log server stuurt, dan zullen uw logs onmiddellijk beschikbaar zijn.

Als u eenmaal een redelijke hoeveelheid gegevens over het probleem heeft, kunt u beginnen met het opsporen ervan in code.

Identificeer de oorzaak van het probleem

Na het verzamelen van informatie over het probleem, is de volgende stap het identificeren van de oorzaak. Het reproduceren van een bug in een ontwikkelomgeving is een van de eenvoudigste manieren om het bestaan ervan te bevestigen, maar het kan tijdrovend zijn en werkt misschien niet in alle gevallen. Het hebben van een grondige set logs brengt u direct naar de bron van het probleem, en bespaart u tijd en frustratie.

Een bug report geeft u een algemeen idee van wat het probleem is en waar het zich voordeed. Met behulp van uw log management tool van keuze, kunt u uw zoekopdracht te beperken tot een kleinere reeks van logboekvermeldingen door te zoeken naar een unieke data token, zoals een gebruikersnaam, sessie-ID, of bericht tekst.

Laten we lopen door een voorbeeldscenario om te laten zien hoe je een systeem te debuggen. Stel dat we een web-based interface hebben om op afstand in te loggen op een website. De webpagina heeft een inlogscherm dat basisauthenticatie uitvoert met een gebruikersnaam en wachtwoord. Gebruikers melden dat ze niet kunnen inloggen op de website. De webpagina accepteert hun invoer, maar geeft vervolgens een algemene foutmelding.

Een voorbeeld van een website die een foutmelding geeft na een inlogpoging.

Deze melding geeft ons niet veel informatie, behalve een algemene indicatie van de ernst van het log. Zoeken in een log manager naar entries met “Severe” in het level of bericht zou kunnen resulteren in honderden resultaten zonder enige garantie dat een van hen gerelateerd is aan het probleem in kwestie. Gelukkig heeft onze Logger ook de gebruikersnaam vastgelegd van de gebruiker die de fout heeft ondervonden, zodat we kunnen filteren op de gebruikersnaam “admin”.

Bekijken van een Java-exception in Loggly.

Als we dieper graven, zien we dat de oorzaak van de fout een ontbrekende of ongeldige tabel is. Dit is een ernstig probleem, omdat het kan duiden op een onbedoelde verwijdering of database corruptie.

ERROR: Exception for user admincom.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test_schema.users' doesn't exist...com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651) at TestApplication.Test.doPost(Test.java:33) at javax.servlet.http.HttpServlet.service(HttpServlet.java:646) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at...

Kijken we door de stack trace, dan zien we dat de service faalde op regel 33 van Test.java. Deze regel bestaat uit een SQL-instructie die gegevens over een gebruiker uit de tabel test_schema.users haalt.

rs = stmt.executeQuery("SELECT * FROM test_schema.users WHERE username = " + username + " AND Password = " + password);

Wanneer we deze query in een SQL front-end proberen uit te voeren, vinden we dat de tabel “test_schema.users” niet bestaat. Echter, de database heeft wel een “test_schema.user” tabel. Op een bepaald punt kan een ontwikkelaar per ongeluk de verkeerde tabelnaam hebben ingevoerd en het herziene formulier naar productie hebben geduwd. Nu weten we wat het probleem is en waar het in onze code voorkomt.

Met SolarWinds Loggly kunt u debuggen en de hoofdoorzaak van problemen vinden door fouten te traceren door uw applicatiestack, over meerdere loggebeurtenissen en zelfs door de relevante regel code in GitHub te lokaliseren.

Het probleem oplossen en voorkomen dat het zich herhaalt

Nu we het probleem en de oorzaak ervan hebben geïdentificeerd, is de laatste stap om het te repareren. Ons inlogvoorbeeld was een overdreven geval met een relatief eenvoudige oplossing, maar u kunt gecompliceerdere bugs tegenkomen die in verschillende delen van uw toepassing zijn geworteld. Overweeg zorgvuldig hoe uw verandering de applicatie zal beïnvloeden, voordat u naar de snelle en vuile oplossing grijpt. Is dit echt de beste oplossing voor het probleem? Is het mogelijk dat uw wijziging zal interfereren met een ander component? Zal deze fix het moeilijk maken om later nieuwe fixes of features te introduceren? Zal deze oplossing ook voorkomen dat soortgelijke problemen later weer opduiken?

Wat bijvoorbeeld als twee verschillende gebruikers het voor elkaar krijgen om twee verschillende accounts aan te maken met dezelfde gebruikersnaam en hetzelfde wachtwoord? U zou een unieke gebruikersnaam voor alle gebruikers kunnen afdwingen, maar wat voor gevolgen zou dat hebben voor uw databasestructuur, uw verificatiecode en uw bestaande gebruikersbestand? U zou een nieuw uniek veld zoals een e-mailadres kunnen toevoegen en het verplicht stellen, maar wat als sommige van uw huidige gebruikers geen e-mailadressen hebben opgegeven? Wat als sommige e-mailadressen worden gedeeld door meerdere accounts? Wat voor effect hebben deze nieuwe regels op uw registratie-, zoek- en beheerpagina’s?

U zult ook rekening moeten houden met de tijd die verstrijkt tussen het maken van een fix en het implementeren ervan. In een bedrijfsomgeving moet uw code worden beoordeeld door andere ontwikkelaars, worden geïntegreerd in de code base, worden gebouwd, getest door QA, worden geënsceneerd, en misschien nog een aantal stappen doorlopen voordat het in productie wordt genomen. Uw bedrijf kan specifieke protocollen hebben voor kritieke of tijd-gevoelige bugs die u moet volgen. Zodra u een oplossing hebt gevonden, vermeldt u uw oplossing in uw documentatie, evenals alle redelijke workarounds voor het probleem. Op deze wijze, kunnen uw klanten uw product nog gebruiken, en uw ondersteuningsteam kan het aantal dubbele insectenrapporten verminderen terwijl de moeilijke situatie zijn manier aan productie maakt.

Na het uitrollen van de moeilijke situatie, blijf uw productietoepassing controleren om te verifià “ren de kwestie werd opgelost. Bijvoorbeeld, na het implementeren van de oplossing voor het database probleem in het bovenstaande voorbeeld, zouden we geen events meer moeten zien met “Table ’test_schema.users’ doesn’t exist” (Tabel ’test_schema.users’ bestaat niet). Als de fouten blijven bestaan of als we nieuwe fouten beginnen te zien, weten we dat de oplossing niet werkte. In het ideale geval zien we een patroon zoals in de volgende schermafbeelding, waarbij het aantal fouten onmiddellijk na de implementatie van de patch tot nul daalt.

Het aantal logboeken dat een fout bevat in Loggly in kaart brengen.

Meer Production Debugging Tools

Hoewel logging de beproefde methode is voor het oplossen van problemen en het debuggen van toepassingen, kunnen deze tools u helpen meer inzicht te krijgen in hoe uw toepassing werkt.

jdb

jdb, de Java Debugger, is een opdrachtregelhulpprogramma voor het debuggen van Java-klassen. jdb is eenvoudig te gebruiken en wordt geleverd met de Java Development Kit. Met jdb wordt een nieuwe Java Virtual Machine (JVM) gecreëerd, zodat u een klasse kunt debuggen zonder dat dit van invloed is op lopende programma’s. Je kunt jdb ook gebruiken om lopende programma’s te debuggen door de volgende parameters aan je java commando toe te voegen:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n

Wanneer de JVM start, wijst het een poortnummer toe voor inkomende jdb verbindingen. Je kan dan een verbinding maken met de draaiende JVM instantie met jdb -attach:

$ jdb -attach

Je kan dit bijvoorbeeld gebruiken om je debugger te verbinden met een draaiende productie instantie. Wees voorzichtig met het gebruik van breakpoints in dit scenario, omdat het een actieve thread kan pauzeren. Dit kan gevolgen hebben als, bijvoorbeeld, een klant de applicatie gebruikt terwijl jij aan het debuggen bent. Voor meer informatie, zie de Java documentatie over jdb.

OverOps

OverOps is een suite van gereedschappen voor het monitoren van applicaties, het analyseren van code, en het opsporen van problemen. In tegenstelling tot logging software, die zich baseert op output van een draaiende applicatie, maakt OverOps direct verbinding met de Java Virtual Machine om de code base van de applicatie in kaart te brengen, variabelen te lezen en fouten te registreren. Hierdoor kan het meer fouten opvangen en meer gegevens vastleggen dan zelfs het logging framework van de applicatie. OverOps gebruikt een software as a service (SaaS) model, waar metrieken worden verzameld en opgeslagen op OverOps’ cloud servers. U kunt het echter ook on-premises implementeren. In beide gevallen kunt u uw gegevens bekijken via een webgebaseerde interface.

BTrace

BTrace is een traceringstool waarmee u alle aspecten van uw applicatie kunt monitoren, van classnamen tot fouten. BTrace gebruikt een aspect-georiënteerde programmeer aanpak met het gebruik van annotaties, die specificeren waar en hoe BTrace uw applicatie controleert. Bijvoorbeeld, het volgende BTrace script controleert en logt elke aanroep naar het javax.swing pakket.

import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*; @BTrace public class AllMethods { @OnMethod( clazz="/javax\.swing\..*/", method="/.*/" ) public static void m(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod) { print(Strings.strcat("entered ", probeClass)); println(Strings.strcat(".", probeMethod)); }}

Voor meer informatie over BTrace, zie de BTrace GitHub repository en Wiki.

Chronon

Chronon stelt u in staat om de gehele executiestroom van een applicatie terug te spoelen en af te spelen. Het registreert elke individuele wijziging die tijdens de levensduur van een toepassing wordt gemaakt, zodat u de toestand van de toepassing op elk gegeven moment kunt reproduceren. Opnames worden opgeslagen in een bestand, waardoor het eenvoudig is om de uitvoeringsgeschiedenis over te brengen van een productiemachine naar een ontwikkelmachine om te testen.

Chronon bestaat uit de Chronon Recording Server, waarmee u op afstand Java-toepassingen kunt opnemen; Embedded Chronon, waarmee de recorder in een toepassing wordt ingebed; en de Time Travelling Debugger, waarmee u opnames kunt afspelen.

jhsdb

jhsdb (Java HotSpot Debugger) is een verzameling gereedschappen voor het debuggen, analyseren en profileren van de standaard JVM die met zowel OpenJDK als de Oracle JDK wordt meegeleverd. jhsdb laat je verbinden met draaiende Java processen, snapshots nemen van stack traces, en zelfs gecrashte JVMs analyseren. Je kunt het gebruiken om toegang te krijgen tot de heap, code cache, garbage collection statistieken, en meer. Zie de jhsdb documentatie pagina voor meer informatie.

Tracing Transactions

Wanneer er een probleem optreedt, is het belangrijk om te weten waar het probleem begon en hoe het de rest van je applicatie beïnvloedde. Dit is al moeilijk genoeg in een monolithische applicatie, maar wordt nog moeilijker in een gedistribueerde service-georiënteerde architectuur, waar een enkel verzoek tientallen services kan raken. Het is niet altijd duidelijk welke service de hoofdoorzaak van de fout bevat, of hoe het andere services heeft beïnvloed. Tracing levert de gegevens die nodig zijn om het executiepad van uw applicatie te volgen en de exacte oorzaak van een probleem te achterhalen.

In de paragraaf Debugging Production Problems van de handleiding, hebben we een voorbeeld doorlopen waarbij een gebruiker problemen had met het inloggen in een webapplicatie vanwege een ongeldige database tabel. In deze sectie laten we zien hoe het traceren van transacties een centrale rol speelde bij het oplossen van het probleem.

Tracing Unique IDs

Om een reeks log events te kunnen traceren, is een manier nodig om gerelateerde logs op een unieke manier te identificeren. Een multi-user omgeving kan honderden identieke logs genereren, waardoor het moeilijk wordt om te zoeken op basis van tijdstempel of Logger. Een eenvoudigere oplossing is om een unieke identifier aan gerelateerde logboekitems toe te voegen. Deze identifier zou een gebruikersnaam, een sessie ID, een API sleutel, of een Universally Unique Identifier (UUID) kunnen zijn. Gelukkig is ThreadContext perfect geschikt voor deze taak.

In het voorbeeld Debugging Production Problems hadden we een web-gebaseerde gebruikersinterface die werd gehost in een Tomcat servlet, die verbinding maakte met een MySQL database. Gebruikers voerden hun gegevens in op de webpagina, en nadat ze op submit hadden gedrukt, voerde de servlet een query uit om hun gegevens te vergelijken met de gegevens die in de database waren opgeslagen. Als de gebruiker met succes werd geauthenticeerd, werd hij doorgestuurd naar de hoofdpagina. Als er een fout optrad, werden details over de fout gelogd en kregen de gebruikers een algemene boodschap te zien.

We waren in staat om dit probleem te debuggen door de gebruikersnamen van de gebruikers in het logbericht op te nemen. Dit stelde ons in staat om snel te zoeken naar log events gerelateerd aan de admin gebruiker. Voortbouwend op ditzelfde voorbeeld, kunnen we ThreadContext.put() gebruiken om een gebruikersnaam aan een Logger te koppelen. Wanneer de gebruikers hun credentials indienen, gaat de servlet de doPost() methode binnen, die de gebruikersnamen toevoegt aan de ThreadContext:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {ThreadContext.put("username", request.getParameter("username"));logger.info("Entering doPost().");...}

Het gebruik van Log4j met het patroon %p : %m%n resulteert in de volgende entry:

INFO : Entering doPost().

We kunnen soortgelijke events toevoegen voor het benaderen van de MySQL database, het verlaten van de doPost methode, en het uitvoeren van andere acties. Op deze manier, als de gebruiker een uitzondering veroorzaakt, weten we precies wat de gebruiker aan het doen was toen de uitzondering optrad.

Tracing Method Calls

Veel loggingframeworks bieden native methoden voor het traceren van het uitvoeringstraject van een toepassing. Deze methoden verschillen enigszins tussen raamwerken, maar volgen hetzelfde algemene formaat.

  • traceEntry() markeert het begin van een methode.
  • traceExit() markeert het einde van een methode. Voor methoden die een object teruggeven, kunt u tegelijkertijd het object teruggeven en de gebeurtenis loggen met return logger.exit(object).
  • throwing() markeert een uitzondering die waarschijnlijk niet zal worden afgehandeld, zoals een RuntimeException.
  • catching() markeert een uitzondering die niet opnieuw zal worden gegooid.

U kunt framework-specifieke informatie over deze methoden vinden in de Logger-documentatie voor Log4j, Logback, en java.util.logging.

Tracing Methods in Log4j

Als voorbeeld voor het gebruik van Log4j, zullen we de Logger.info() methods in onze servlet vervangen door tracing methods.

public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { logger.entry(); ... logger.exit();}

We zullen ook de Appender's PatternLayout veranderen om de naam van de klasse, de methode, en het regelnummer te tonen (de %class, %M, en %line conversie patronen).

<PatternLayout pattern="%p %class %M %line: %m%n" />

Tracing methoden loggen gebeurtenissen op het TRACE niveau, wat betekent dat we het Logger's niveau moeten veranderen van DEBUG in TRACE. Anders zullen de logberichten worden onderdrukt.

<Loggers> <Root level="trace"> <AppenderRef ref="Console"/> </Root></Loggers> TRACE DatabaseApplication.Login doPost 26: entry...TRACE DatabaseApplication.Login doPost 59: exit

Tracing methods bieden ook hun eigen Markers. Bijvoorbeeld, de Logger.entry() en Logger.exit() methodes geven respectievelijk ENTER en EXIT weer.

ENTER 14:47:41.074 TRACE DatabaseApplication.Login - EnterEXIT 14:47:41.251 TRACE DatabaseApplication.Login - Exit

Tracing Methods in SLF4J

SLF4J gebruikers kunnen hun voordeel doen met MDC om log gebeurtenissen te traceren. Net als bij Log4j, kunnen MDC waarden worden gebruikt met een Appender door gebruik te maken van het %X conversie patroon.

SLF4J biedt ook entry(), exit(), throwing(), en catching() methoden via de XLogger (Extended Logger) klasse. U kunt een XLogger instantie maken met XLoggerFactory.getXLogger().

package DatabaseApplication;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import org.slf4j.ext.XLogger;import org.slf4j.ext.XLoggerFactory; public class Login extends HTTPServlet { final static XLogger xlogger = XLoggerFactory.getXLogger(Login.class.getName()); final static Logger logger = LoggerFactory.getLogger(Login.class.getName()); @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { xlogger.entry(); .. xlogger.exit(); }}

Voeg de %class en %line conversie patronen toe aan logback.xml.

<configuration> <appender name="Console" class="ch.qos.Logback.core.ConsoleAppender"> <encoder> <pattern>%-5level %class{36} %M %L: %msg%xEx%n</pattern> </encoder> </appender> <root level="trace"> ... </root></configuration>

De resulterende log entries zijn identiek aan die gemaakt door Log4j.

Beheer Geheugengebruik

Geheugenbeheer wordt vaak over het hoofd gezien in talen op hoger niveau, zoals Java. Hoewel de gemiddelde hoeveelheid geheugen in moderne apparaten toeneemt, kan een hoog geheugengebruik grote gevolgen hebben voor de stabiliteit en prestaties van uw toepassing. Zodra de Java Virtuele Machine niet langer geheugen kan toewijzen van het besturingssysteem, kan uw programma aflopen en crashen. Als u weet hoe u het geheugen moet beheren, voorkomt u problemen als uw toepassing groter wordt.

Stelt u zich bijvoorbeeld voor dat we een reeks getallen vanaf één willen opslaan. Een gebruiker geeft een bovengrens op en het programma slaat elk geheel getal van “1” tot die grens op. We gebruiken een while-lus met een teller om elk getal aan een array toe te voegen.

import java.util.Scanner;...System.out.print("Please enter the maximum size of the array: ");Scanner scanner = new Scanner(System.in);int limit = scanner.nextInt(); ArrayList intArray = new ArrayList(); int count = 1;while (count <= limit) { intArray.add(count);}

Het is u misschien opgevallen dat count niet toeneemt in de lus. Dit is een groot probleem; als de gebruiker een getal groter dan nul invoert, zal de array blijven groeien totdat de JVM al het beschikbare geheugen gebruikt en het programma crasht.

Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:2245)at java.util.Arrays.copyOf(Arrays.java:2219)at java.util.ArrayList.grow(ArrayList.java:242)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)at java.util.ArrayList.add(ArrayList.java:440)at MemoryTest.main(MemoryTest.java:10)

Garbage Collection

Om het geheugengebruik te verminderen, voert de Java Virtual Machine een periodiek opruimproces uit dat bekend staat als garbage collection. Garbage collection zoekt naar blokken geheugen die niet langer in gebruik zijn en maakt ze beschikbaar voor hergebruik. In de volgende bronnen wordt het garbage collection-proces van Java nader toegelicht.

  • Java Garbage Collection Basics (Oracle)
  • HotSpot Virtual Machine Garbage Collection Tuning Guide (Oracle)
  • Understanding G1 GC Logs (Poonam Bajaj)
  • Garbage Collection Tuning Guide (Atlassian)

De garbage collector genereert diagnostische informatie die nuttig kan zijn voor prestatieprofilering van toepassingen. U kunt deze informatie loggen door -Xlog:gc door te geven aan de JVM bij het starten van uw toepassing. Na elke run drukt de garbage collector statistieken af over het garbage collectie proces in het Unified JVM Logging formaat. Hier volgt een voorbeeld.

 Using G1 Periodic GC disabled GC(0) Pause Young (Normal) (G1 Evacuation Pause) 7M->1M(64M) 8.450ms

Using G1 zegt welke vuilnisophaalmethode wordt gebruikt. De Garbage-First (G1)-collector is vaak standaard ingeschakeld op multiprocessorcomputers met veel RAM. Periodic GC disabled geeft aan dat het vuilnisophaalproces niet wordt herhaald. De derde regel vertelt ons dat dit een Evacuation Pause is, waarbij objecten worden gekopieerd tussen geheugengebieden op basis van of ze nog in gebruik zijn. Pause Young vertelt ons dat het proces de jonge generatie heeft schoongemaakt, dat is waar nieuwe objecten worden toegewezen. Als gevolg daarvan daalde het totale geheugengebruik door objecten in de jonge generatie van 7M naar 1M van de 64M gealloceerd, en het proces nam 8.450ms.

Met de garbage collector die diagnostische informatie naar de console afdrukt, kunnen we zien hoe de hoeveelheid gealloceerd geheugen (de heap grootte) blijft groeien in de tijd. We verwijderen de CPU-tijden voor de eenvoud.

$ java -Xlog:gc MemoryLeakDemo Using G1 Periodic GC disabled GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation) 25M->23M(64M) 4.372ms GC(1) Concurrent Cycle GC(1) Pause Remark 50M->34M(64M) 0.250ms GC(1) Pause Cleanup 94M->94M(124M) 0.057ms GC(1) Concurrent Cycle 169.695ms GC(2) Pause Young (Concurrent Start) (G1 Humongous Allocation) 94M->94M(124M) 3.738ms GC(3) Concurrent Cycle...

Grafiek met een geleidelijke toename van het JVM-heap-gebruik in de loop van de tijd.

U kunt tijdstempels toevoegen aan elk item met -XX:+PrintGCDateStamps en -XX:+PrintGCTimeStamps. U kunt ook de uitvoer van de vuilnisman naar een bestand loggen door Xlog:gc:file: aan de JVM door te geven bij het starten van uw applicatie.

Als u uw logbestanden bewaakt met rsyslog, dan kunt u de logs doorsturen naar een gecentraliseerd logsysteem waar ze worden verwerkt en klaar zijn voor bijna-real-time analyse. Hier is hoe een tijdlijngrafiek er in Loggly uitziet. Het toont de totale heap grootte, evenals de heap grootte voor en na het uitvoeren van de garbage collector.

Geheugenlekken

Een geheugenlek is een situatie waarin een programma geheugen sneller toewijst dan het kan vrijgeven. De eenvoudigste manier om een geheugenlek op te sporen is wanneer uw programma niet meer reageert, instabiel wordt, of OutOfMemoryErrors veroorzaakt. In Java vindt u meer gevallen van Full GC-verzamelingen naarmate het geheugengebruik toeneemt.

U kunt meer te weten komen over de uitvoer van de vuilnisman en het lokaliseren van geheugenlekken in het hoofdstuk Geheugenlekken opsporen in de Java-documentatie.

Aanvullende bronnen

Beperk de debuggingstijd zonder ook maar één regel code te schrijven (Loggly)-gids voor het gebruik van Takipi

De ultieme gids: 5 methoden voor het debuggen van productieservers op schaal (High Scalability)-Tools en technieken voor het debuggen van productieproblemen

Memory Management

Garbage Collection Tuning Guide (Oracle)-Gids voor het begrijpen en afstellen van Java’s garbage collection

Understanding Java Garbage Collection (CUBRID Blog)-Gids voor garb

See it. Analyseer het. Inspecteer het. Los het op

Zie wat belangrijk is.

START GRATIS PROEF

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.