Skip to content

Commit 433d977

Browse files
committed
#155 support unified jvm logging format with tag "gc" (level = info) and decorations tags,level,uptime (-Xlog:gc:file=<filename>)
1 parent 16a1a73 commit 433d977

31 files changed

+1301
-180
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ When logfile rotation (-XX:+UseGCLogFileRotation) is enabled, the logfiles can b
1919

2020
Supported verbose:gc formats are:
2121

22-
- preliminary support for OpenJDK 9 Shenandoah algorithm in unified logging format -Xlog:gc:<file> -XX:+UseShenandoahGC
22+
- partial support for OpenJDK 9 unified logging format -Xlog:gc:<file>
23+
- no heap information is evaluated, but all events with tag "gc" only should be recognised
24+
- required decorations: tags,uptime (others will be ignored or parser might fail)
2325
- Oracle JDK 1.8 -Xloggc:<file> [-XX:+PrintGCDetails] [-XX:+PrintGCDateStamps]
2426
- Sun / Oracle JDK 1.7 with option -Xloggc:<file> [-XX:+PrintGCDetails] [-XX:+PrintGCDateStamps]
2527
- Sun / Oracle JDK 1.6 with option -Xloggc:<file> [-XX:+PrintGCDetails] [-XX:+PrintGCDateStamps]

src/main/java/com/tagtraum/perf/gcviewer/imp/AbstractDataReaderSun.java

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import java.util.LinkedList;
1212
import java.util.List;
1313
import java.util.logging.Level;
14-
import java.util.regex.Matcher;
15-
import java.util.regex.Pattern;
1614

1715
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent;
1816
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent.ExtendedType;
@@ -38,8 +36,6 @@ public abstract class AbstractDataReaderSun extends AbstractDataReader {
3836

3937
private static final String CMS_PRINT_PROMOTION_FAILURE = "promotion failure size";
4038

41-
private static Pattern parenthesesPattern = Pattern.compile("\\([^()]*\\) ?");
42-
4339
// java 8 log output
4440
protected static final String LOG_INFORMATION_OPENJDK = "OpenJDK";
4541
protected static final String LOG_INFORMATION_HOTSPOT = "Java HotSpot";
@@ -301,35 +297,12 @@ protected String parseTypeString(String line, ParseInformation pos) throws Parse
301297

302298
protected ExtendedType parseType(String line, ParseInformation pos) throws ParseException {
303299
String typeString = parseTypeString(line, pos);
304-
ExtendedType gcType = extractTypeFromParsedString(typeString);
305-
if (gcType == null) {
306-
throw new UnknownGcTypeException(typeString, line, pos);
307-
}
308-
309-
return gcType;
300+
return getDataReaderTools().parseType(typeString);
310301
}
311302

312303

313304
protected ExtendedType extractTypeFromParsedString(String typeName) throws UnknownGcTypeException {
314-
ExtendedType extendedType = null;
315-
String lookupTypeName = typeName.endsWith("--")
316-
? typeName.substring(0, typeName.length()-2)
317-
: typeName;
318-
AbstractGCEvent.Type gcType = AbstractGCEvent.Type.lookup(lookupTypeName);
319-
// the gcType may be null because there was a PrintGCCause flag enabled - if so, reparse it with the first paren set stripped
320-
if (gcType == null) {
321-
// try to parse it again with the parens removed
322-
Matcher parenMatcher = parenthesesPattern.matcher(lookupTypeName);
323-
if (parenMatcher.find()) {
324-
gcType = AbstractGCEvent.Type.lookup(parenMatcher.replaceFirst(""));
325-
}
326-
}
327-
328-
if (gcType != null) {
329-
extendedType = ExtendedType.lookup(gcType, typeName);
330-
}
331-
332-
return extendedType;
305+
return getDataReaderTools().extractTypeFromParsedString(typeName);
333306
}
334307

335308
/**

src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderFactory.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.tagtraum.perf.gcviewer.imp;
22

3-
import com.tagtraum.perf.gcviewer.model.GCResource;
4-
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;
5-
63
import java.io.BufferedInputStream;
74
import java.io.EOFException;
85
import java.io.IOException;
@@ -12,6 +9,9 @@
129
import java.util.logging.Logger;
1310
import java.util.zip.GZIPInputStream;
1411

12+
import com.tagtraum.perf.gcviewer.model.GCResource;
13+
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;
14+
1515
/**
1616
*
1717
* Date: Jan 30, 2002
@@ -152,6 +152,11 @@ else if (s.indexOf("<AF") != -1) {
152152
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: IBM <1.3.0");
153153
return new DataReaderIBM1_3_0(gcResource, in);
154154
}
155+
// ...][info][gc ] Using Shenandoah <or any other gc algorithm in unified jvm logging format>
156+
else if (s.contains("][gc")) {
157+
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: Oracle / OpenJDK unified jvm logging");
158+
return new DataReaderUnifiedJvmLogging(gcResource, in);
159+
}
155160
else if (s.indexOf(" (young)") > 0 || s.indexOf("G1Ergonomics") > 0) {
156161
// G1 logger usually starts with "<timestamp>: [GC pause (young)...]"
157162
// but can start with <timestamp>: [G1Ergonomics (Heap Sizing) expand the heap...
@@ -202,11 +207,6 @@ else if (s.indexOf("starting collection, threshold allocation reached.") != -1)
202207
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: IBM i5/OS 1.4.2");
203208
return new DataReaderIBMi5OS1_4_2(gcResource, in);
204209
}
205-
// Shenandoah
206-
else if (s.contains("Using Shenandoah")) {
207-
if (getLogger().isLoggable(Level.INFO)) getLogger().info("File format: Red Hat Shenandoah");
208-
return new DataReaderShenandoah(gcResource, in);
209-
}
210210
return null;
211211
}
212212

src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderTools.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
import java.util.logging.Level;
44
import java.util.logging.Logger;
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent;
9+
import com.tagtraum.perf.gcviewer.model.AbstractGCEvent.ExtendedType;
510

611
/**
712
* Tools useful for (most) DataReader implementations.
813
*/
914
public class DataReaderTools {
1015

16+
private static Pattern parenthesesPattern = Pattern.compile("\\([^()]*\\) ?");
17+
1118
private Logger logger;
1219

1320
public DataReaderTools(Logger logger) {
@@ -43,4 +50,46 @@ else if ('G' == memUnit) {
4350
}
4451
}
4552

53+
/**
54+
* Returns the <code>ExtendedType</code> for <code>typeString</code>, if it can find one. If there is a type name
55+
* including gc cause ("ParNew (promotion failed)", where (promotion failed) is the gc cause), the cause is removed
56+
* while trying to find the type.
57+
*
58+
* @param typeString string representation of the gc event
59+
* @return <code>ExtendedType</code> representing <code>typeString</code>
60+
* @throws UnknownGcTypeException If <code>typeString</code> can't be converted to an <code>ExtendedType</code>
61+
*/
62+
public ExtendedType parseType(String typeString) throws UnknownGcTypeException {
63+
ExtendedType gcType = extractTypeFromParsedString(typeString.trim());
64+
if (gcType == null) {
65+
throw new UnknownGcTypeException(typeString);
66+
}
67+
68+
return gcType;
69+
}
70+
71+
public ExtendedType extractTypeFromParsedString(String typeName) {
72+
typeName = typeName.trim();
73+
ExtendedType extendedType = null;
74+
String lookupTypeName = typeName.endsWith("--")
75+
? typeName.substring(0, typeName.length()-2)
76+
: typeName;
77+
AbstractGCEvent.Type gcType = AbstractGCEvent.Type.lookup(lookupTypeName);
78+
// the gcType may be null because there was a PrintGCCause flag enabled - if so, reparse it with the first paren set stripped
79+
if (gcType == null) {
80+
// try to parse it again with the parens removed
81+
Matcher parenMatcher = parenthesesPattern.matcher(lookupTypeName);
82+
if (parenMatcher.find()) {
83+
gcType = AbstractGCEvent.Type.lookup(parenMatcher.replaceFirst(""));
84+
}
85+
}
86+
87+
if (gcType != null) {
88+
extendedType = ExtendedType.lookup(gcType, typeName);
89+
}
90+
91+
return extendedType;
92+
}
93+
94+
4695
}

src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderShenandoah.java renamed to src/main/java/com/tagtraum/perf/gcviewer/imp/DataReaderUnifiedJvmLogging.java

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,32 @@
1919
import com.tagtraum.perf.gcviewer.model.GCResource;
2020

2121
/**
22-
* DataReaderShenandoah can parse all the main messages for GCViewer with default decorations. <p>
23-
* Initial mark, Final mark, Init Update Refs, Final Update Refs, Pause Full Allocation Failure, Pause Full (System.gc()),
24-
* Concurrent mark, Concurrent evacuation, Concurrent reset bitmaps, Concurrent update references, Concurrent precleaning
22+
* DataReaderUnifiedJvmLogging can parse all gc events of unified jvm logs with default decorations.
2523
* <p>
26-
* For more information: <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah Wiki at OpenJDK</a>
24+
* Currently needs the "gc" selector with "info" level and "uptime,level,tags" decorators (Java 9.0.1).
25+
* <ul>
26+
* <li>minimum configuration with defaults: <code>-Xlog:gc:file="path-to-file"</code></li>
27+
* <li>configuration with tags + decorations: <code>-Xlog:gc=info:file="path-to-file":tags,uptime,level</code></li>
28+
* </ul>
29+
* Only processes the following information format for Serial, Parallel, CMS, G1 and Shenandoah algorithms, everything else is ignored:
30+
* <pre>
31+
* [0.731s][info][gc ] GC(0) Pause Init Mark 1.021ms
32+
* [0.735s][info][gc ] GC(0) Concurrent marking 74M-&gt;74M(128M) 3.688ms
33+
* [43.948s][info][gc ] GC(831) Pause Full (Allocation Failure) 7943M-&gt;6013M(8192M) 14289.335ms
34+
* </pre>
2735
*
2836
* <p>
29-
* Example Format
30-
* <p>[0.730s][info][gc,start ] GC(0) Pause Init Mark
31-
* <p>[0.731s][info][gc ] GC(0) Pause Init Mark 1.021ms
32-
* <p>[0.731s][info][gc,start ] GC(0) Concurrent marking
33-
* <p>[0.735s][info][gc ] GC(0) Concurrent marking 74M-&gt;74M(128M) 3.688ms
34-
* <p>[0.735s][info][gc,start ] GC(0) Pause Final Mark
35-
* <p>[0.736s][info][gc ] GC(0) Pause Final Mark 74M-&gt;76M(128M) 0.811ms
36-
* <p>[0.736s][info][gc,start ] GC(0) Concurrent evacuation
37-
* <p>...
38-
* <p>[43.948s][info][gc ] GC(831) Pause Full (Allocation Failure) 7943M-&gt;6013M(8192M) 14289.335ms
37+
* For more information about Shenandoah see: <a href="https://wiki.openjdk.java.net/display/shenandoah/Main">Shenandoah Wiki at OpenJDK</a>
3938
*/
40-
public class DataReaderShenandoah extends AbstractDataReader {
41-
39+
public class DataReaderUnifiedJvmLogging extends AbstractDataReader {
4240

4341
// Input: [0.693s][info][gc ] GC(0) Pause Init Mark 1.070ms
4442
// Group 1: 0.693
4543
// Group 2: Pause Init Mark
46-
// Group 3: 1.070
44+
// Group 3: 1.070 -> optional
4745
// Regex: ^\[([^s\]]*)[^\-]*\)[ ]([^\-]*)[ ]([0-9]+[.,][0-9]+)
4846
private static final Pattern PATTERN_WITHOUT_HEAP = Pattern.compile(
49-
"^\\[([^s\\]]*)[^\\-]*\\)[ ]([^\\-]*)[ ]([0-9]+[.,][0-9]+)");
47+
"^\\[([^s\\]]*)[^\\-]*\\)[ ]([^\\d]*)(([0-9]+[.,][0-9]+)|$)");
5048

5149
// Input: [13.522s][info][gc ] GC(708) Concurrent evacuation 4848M->4855M(4998M) 2.872ms
5250
// Group 1: 13.522
@@ -84,27 +82,29 @@ public class DataReaderShenandoah extends AbstractDataReader {
8482
private static final int HEAP_CURRENT_TOTAL = 5;
8583
private static final int HEAP_CURRENT_TOTAL_UNIT = 6;
8684

87-
private static final List<String> EXCLUDE_STRINGS = Arrays.asList("Using Shenandoah", "Cancelling concurrent GC",
88-
"[gc,start", "[gc,ergo", "[gc,stringtable", "[gc,init", "[gc,heap", "[pagesize", "[class", "[os", "[startuptime",
89-
"[os,thread", "[gc,heap,exit", "Cancelling concurrent GC: Allocation Failure", "Phase ",
90-
"[gc,stats", "[biasedlocking", "[logging", "[verification", "[modules,startuptime", "[safepoint", "[stacktrace",
91-
"[exceptions", "thrown", "at bci", "for thread", "[module,load", "[module,startuptime");
85+
/** list of strings, that must be part of the gc log line to be considered for parsing */
86+
private static final List<String> INCLUDE_STRINGS = Arrays.asList("[gc ", "[gc]");
87+
/** list of strings, that target gc log lines, that - although part of INCLUDE_STRINGS - are not considered a gc event */
88+
private static final List<String> EXCLUDE_STRINGS = Arrays.asList("Cancelling concurrent GC", "[debug", "[trace");
89+
/** list of strings, that are gc log lines, but not a gc event -&gt; should be logged only */
90+
private static final List<String> LOG_ONLY_STRINGS = Arrays.asList("Using");
91+
9292

93-
protected DataReaderShenandoah(GCResource gcResource, InputStream in) throws UnsupportedEncodingException {
93+
protected DataReaderUnifiedJvmLogging(GCResource gcResource, InputStream in) throws UnsupportedEncodingException {
9494
super(gcResource, in);
9595
}
9696

9797
@Override
9898
public GCModel read() throws IOException {
99-
getLogger().info("Reading Shenandoah format...");
99+
getLogger().info("Reading Oracle / OpenJDK unified jvm logging format...");
100100

101101
try {
102102
GCModel model = new GCModel();
103-
model.setFormat(GCModel.Format.RED_HAT_SHENANDOAH_GC);
103+
model.setFormat(GCModel.Format.UNIFIED_JVM_LOGGING);
104104

105105
Stream<String> lines = in.lines();
106-
lines.filter(this::lineNotInExcludedStrings)
107-
.map(this::parseShenandoahEvent)
106+
lines.filter(this::lineContainsParseableEvent)
107+
.map(this::parseEvent)
108108
.filter(Objects::nonNull)
109109
.forEach(model::add);
110110

@@ -114,24 +114,31 @@ public GCModel read() throws IOException {
114114
}
115115
}
116116

117-
private AbstractGCEvent<?> parseShenandoahEvent(String line) {
117+
private AbstractGCEvent<?> parseEvent(String line) {
118118
AbstractGCEvent<?> event = null;
119119

120120
Matcher noHeapMatcher = PATTERN_WITHOUT_HEAP.matcher(line);
121121
Matcher withHeapMatcher = PATTERN_WITH_HEAP.matcher(line);
122-
if (noHeapMatcher.find()) {
123-
event = new GCEvent();
124-
AbstractGCEvent.Type type = AbstractGCEvent.Type.lookup(noHeapMatcher.group(NO_HEAP_EVENT_NAME));
125-
event.setType(type);
126-
setPauseAndDateOrTimestamp(event, noHeapMatcher.group(NO_HEAP_TIMESTAMP), noHeapMatcher.group(NO_HEAP_DURATION));
127-
} else if (withHeapMatcher.find()) {
122+
try {
128123
event = line.contains("Concurrent") ? new ConcurrentGCEvent() : new GCEvent();
129-
AbstractGCEvent.Type type = AbstractGCEvent.Type.lookup(withHeapMatcher.group(WITH_HEAP_EVENT_NAME));
130-
event.setType(type);
131-
setPauseAndDateOrTimestamp(event, withHeapMatcher.group(WITH_HEAP_TIMESTAMP), withHeapMatcher.group(WITH_HEAP_DURATION));
132-
addHeapDetailsToEvent(event, withHeapMatcher.group(WITH_HEAP_MEMORY));
133-
} else {
134-
getLogger().warning(String.format("Failed to parse line number %d in the log file: %s", in.getLineNumber(), line));
124+
if (noHeapMatcher.find()) {
125+
AbstractGCEvent.ExtendedType type = getDataReaderTools().parseType(noHeapMatcher.group(NO_HEAP_EVENT_NAME));
126+
event.setExtendedType(type);
127+
setPauseAndDateOrTimestamp(event, noHeapMatcher.group(NO_HEAP_TIMESTAMP), noHeapMatcher.groupCount() > 2 ? noHeapMatcher.group(NO_HEAP_DURATION) : null);
128+
} else if (withHeapMatcher.find()) {
129+
AbstractGCEvent.ExtendedType type = getDataReaderTools().parseType(withHeapMatcher.group(WITH_HEAP_EVENT_NAME));
130+
event.setExtendedType(type);
131+
setPauseAndDateOrTimestamp(event, withHeapMatcher.group(WITH_HEAP_TIMESTAMP), withHeapMatcher.group(WITH_HEAP_DURATION));
132+
addHeapDetailsToEvent(event, withHeapMatcher.group(WITH_HEAP_MEMORY));
133+
} else {
134+
// prevent incomplete event from being added to the GCModel
135+
event = null;
136+
getLogger().warning(String.format("Failed to parse line number %d (line=\"%s\")", in.getLineNumber(), line));
137+
}
138+
} catch (UnknownGcTypeException | NumberFormatException e) {
139+
// prevent incomplete event from being added to the GCModel
140+
event = null;
141+
getLogger().warning(String.format("Failed to parse gc event (%s) on line number %d (line=\"%s\")", e.toString(), in.getLineNumber(), line));
135142
}
136143

137144
return event;
@@ -143,8 +150,10 @@ private AbstractGCEvent<?> parseShenandoahEvent(String line) {
143150
* @param dateOrTimeStampAsString Date- or timestamp information from regex group as string
144151
*/
145152
private void setPauseAndDateOrTimestamp(AbstractGCEvent<?> event, String dateOrTimeStampAsString, String pauseAsString) {
146-
double pause = Double.parseDouble(pauseAsString.replace(",", "."));
147-
event.setPause(pause / 1000);
153+
if (pauseAsString != null && pauseAsString.length() > 0) {
154+
double pause = Double.parseDouble(pauseAsString.replace(",", "."));
155+
event.setPause(pause / 1000);
156+
}
148157
if (PATTERN_ISO8601_DATE.matcher(dateOrTimeStampAsString).find()) {
149158
ZonedDateTime dateTime = ZonedDateTime.parse(dateOrTimeStampAsString, AbstractDataReaderSun.DATE_TIME_FORMATTER);
150159
event.setDateStamp(dateTime);
@@ -169,12 +178,32 @@ private void addHeapDetailsToEvent(AbstractGCEvent<?> event, String memoryString
169178
Integer.parseInt(matcher.group(HEAP_CURRENT_TOTAL)), matcher.group(HEAP_CURRENT_TOTAL_UNIT).charAt(0), memoryString));
170179
event.setExtendedType(event.getExtendedType());
171180
} else {
172-
getLogger().warning("Failed to find heap details from line: " + memoryString);
181+
getLogger().warning("Failed to find heap details from string: \"" + memoryString + "\"");
173182
}
174183
}
175184

176-
private boolean lineNotInExcludedStrings(String line) {
177-
return EXCLUDE_STRINGS.stream().noneMatch(line::contains);
185+
private boolean isExcludedLine(String line) {
186+
return EXCLUDE_STRINGS.stream().anyMatch(line::contains);
187+
}
188+
189+
private boolean isCandidateForParseableEvent(String line) {
190+
return INCLUDE_STRINGS.stream().anyMatch(line::contains);
191+
}
192+
193+
private boolean isLogOnlyLine(String line) {
194+
return LOG_ONLY_STRINGS.stream().anyMatch(line::contains);
195+
}
196+
197+
private boolean lineContainsParseableEvent(String line) {
198+
if (isCandidateForParseableEvent(line) && !isExcludedLine(line)) {
199+
if (isLogOnlyLine(line)) {
200+
getLogger().info(line.substring(line.lastIndexOf("]")+1));
201+
return false;
202+
} else {
203+
return true;
204+
}
205+
}
206+
return false;
178207
}
179208

180209
}

0 commit comments

Comments
 (0)