Arbejde med (ukendt kilde) Stack Traces i Java
1. Oversigt
I denne korte artikel vil vi se på, hvorfor vi kan se en ukendt kilde i vores Java-undtagelsesstacksporing, og hvordan kan vi rette det.
2. Klasse Debug Information
En Java-klassefil indeholder valgfri debug-information for at lette debugging. Vi kan i kompileringstiden vælge, om og hvad al fejlretningsinformation skal tilføjes til klassefilerne. Dette vil afgøre, hvilke fejlfindingsoplysninger der er tilgængelige under kørsel.
Lad os undersøge Java-kompilerens hjælpedokumentation for at se de forskellige tilgængelige muligheder:
javac -help
Usage: javac <options> <source files>
where possible options include:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
Standardadfærden for Javas compiler er at tilføje linjerne og kildeoplysningerne til klassefilerne, hvilket svarer til -g:lines,source.
2.1. Kompilere med Debug Option
Lad os se, hvad der sker, når vi kompilerer vores Java-klasser med mulighederne ovenfor. Vi har en Main klasse, der med vilje genererer en StringIndexOutOfBoundsException .
Afhængigt af den anvendte kompileringsmekanisme bliver vi nødt til at specificere kompileringsindstillingen i overensstemmelse hermed. Her vil vi bruge Maven og dets compiler-plugin til at tilpasse compiler-indstillingen:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-g:none</arg>
</compilerArgs>
</configuration>
</plugin>
Vi har indstillet -g til ingen hvilket betyder, at der ikke vil blive genereret fejlfindingsoplysninger for vores kompilerede klasser. Kører vores buggy Main klasse genererer staksporet, hvor vi ser ukendte kilder i stedet for linjenummeret, hvor undtagelsen opstod.
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 0, end 10, length 5
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3751)
at java.base/java.lang.String.substring(String.java:1907)
at com.baeldung.unknownsourcestacktrace.Main.getShortenedName(Unknown Source)
at com.baeldung.unknownsourcestacktrace.Main.getGreetingMessage(Unknown Source)
at com.baeldung.unknownsourcestacktrace.Main.main(Unknown Source)
Lad os se, hvad den genererede klassefil indeholder. Vi bruger javap som er Java-klassens filopdeling for at gøre dette:
javap -l -p Main.class
public class com.baeldung.unknownsourcestacktrace.Main {
private static final org.slf4j.Logger logger;
private static final int SHORT_NAME_LIMIT;
public com.baeldung.unknownsourcestacktrace.Main();
public static void main(java.lang.String[]);
private static java.lang.String getGreetingMessage(java.lang.String);
private static java.lang.String getShortenedName(java.lang.String);
static {};
}
Det kan være svært at vide, hvilken fejlretningsinformation vi skal forvente her, så lad os ændre kompileringsindstillingen og se, hvad der sker.
2.3. Rettelsen
Lad os nu ændre kompileringsindstillingen til -g:lines,vars,source som vil indsætte LineNumberTable, LocalVariableTable og Kilde oplysninger i vores klassefiler. Det svarer også til bare at have -g som sætter alle fejlretningsoplysningerne:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-g</arg>
</compilerArgs>
</configuration>
</plugin>
Kører vores buggy Main klasse igen producerer nu:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 0, end 10, length 5
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3751)
at java.base/java.lang.String.substring(String.java:1907)
at com.baeldung.unknownsourcestacktrace.Main.getShortenedName(Main.java:23)
at com.baeldung.unknownsourcestacktrace.Main.getGreetingMessage(Main.java:19)
at com.baeldung.unknownsourcestacktrace.Main.main(Main.java:15)
Voila, vi ser linjenummeroplysningerne i vores stakspor. Lad os se, hvad der er ændret i vores klassefil:
javap -l -p Main
Compiled from "Main.java"
public class com.baeldung.unknownsourcestacktrace.Main {
private static final org.slf4j.Logger logger;
private static final int SHORT_NAME_LIMIT;
public com.baeldung.unknownsourcestacktrace.Main();
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/baeldung/unknownsourcestacktrace/Main;
public static void main(java.lang.String[]);
LineNumberTable:
line 12: 0
line 13: 8
line 15: 14
line 16: 29
LocalVariableTable:
Start Length Slot Name Signature
0 30 0 args [Ljava/lang/String;
8 22 1 user Lcom/baeldung/unknownsourcestacktrace/dto/User;
private static java.lang.String getGreetingMessage(java.lang.String);
LineNumberTable:
line 19: 0
LocalVariableTable:
Start Length Slot Name Signature
0 28 0 name Ljava/lang/String;
private static java.lang.String getShortenedName(java.lang.String);
LineNumberTable:
line 23: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 name Ljava/lang/String;
static {};
LineNumberTable:
line 8: 0
}
Vores klassefil indeholder nu tre vigtige oplysninger:
- Kilde , den øverste overskrift, der angiver .java fil, hvorfra .class fil er blevet genereret. I sammenhæng med en staksporing giver den klassenavnet, hvor undtagelsen opstod.
- LineNumberTable kortlægger linjenummeret i den kode, som JVM'en rent faktisk kører, til linjenummeret i vores kildekodefil. I sammenhæng med en staksporing giver den linjenummeret, hvor undtagelsen opstod. Vi har også brug for dette for at kunne bruge breakpoints i vores debuggere.
- LocalVariableTable indeholder detaljerne for at få værdien af en lokal variabel. Debuggere kan bruge det til at læse værdien af en lokal variabel. I forbindelse med en staksporing er dette ligegyldigt.
3. Konklusion
Vi er nu bekendt med fejlfindingsoplysningerne, der genereres af Java-kompileren. Måden at manipulere dem på, -g compiler mulighed. Vi så, hvordan vi kan gøre det med Maven compiler plugin.
Så hvis vi finder ukendte kilder i vores stakspor, kan vi undersøge vores klassefiler for at kontrollere, om fejlretningsinformationen er tilgængelig eller ej. Herefter kan vi vælge den rigtige kompileringsmulighed baseret på vores byggeværktøj for at løse dette problem.
Som altid er den komplette kode og Maven-konfigurationer tilgængelige på GitHub.