blob: 8f243d01e1603963293832c99e7e4ea87160b57f [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package batterytest.greplogs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LogConsumer {
private final List expectedExceptions = new ArrayList();
private boolean skipLogMsgs = false;
private boolean infoMsgFlag = false;
private int eatLines = 0;
private boolean tmpErrFlag = false;
private int tmpErrLines = 0;
private boolean saveFlag = false;
private int savelinenum = 0;
private final List testExpectStrs;
StringBuilder all = null;
private int lineNumber;
private String fileName;
HashMap individalErrorCount = new HashMap();
private final int repeatLimit;
private static final Pattern ExpectedExceptionPattern = Pattern.compile("<ExpectedException action=(add|remove)>(.*)</ExpectedException>");
private static final Pattern logPattern = Pattern.compile("^\\[(?:fatal|error|warn|info|debug|trace|severe|warning|fine|finer|finest)");
private static final Pattern blankPattern = Pattern.compile("^\\s*$");
private static final Pattern infoOrBelowPattern = Pattern.compile("^\\[(?:info|debug|trace|fine|finer|finest)");
private static final Pattern fatalOrErrorPattern = Pattern.compile("^\\[(?:fatal|error|severe)");
private static final Pattern causedByPattern = Pattern.compile("Caused by");
private static final Pattern shortErrPattern = Pattern.compile("^\\[[^\\]]+\\](.*)$", Pattern.MULTILINE | Pattern.DOTALL);
private static final Pattern wroteExceptionPattern = Pattern.compile("\\[debug.*Wrote exception:");
private static final Pattern rmiWarnPattern = Pattern.compile("^WARNING: Failed to .*java.rmi.ConnectException: Connection refused to host: .*; nested exception is:");
private static final Pattern javaLangErrorPattern = Pattern.compile("^java\\.lang\\.\\S+Error$");
private static final Pattern exceptionPattern = Pattern.compile("Exception:");
private static final Pattern exceptionPattern2 = Pattern.compile("( [\\w\\.]+Exception: (([\\S]+ ){0,6}))");
private static final Pattern exceptionPattern3 = Pattern.compile("( [\\w\\.]+Exception)$");
private static final Pattern exceptionPattern4 = Pattern.compile("^([^:]+: (([\\w\"]+ ){0,6}))");
private static final Pattern misformatedI18nMessagePattern = Pattern.compile("[^\\d]\\{\\d+\\}");
private static final Pattern rvvBitSetMessagePattern = Pattern.compile("RegionVersionVector.+bsv\\d+.+bs=\\{\\d+\\}");
/** Limit long errors to this many lines */
private static int ERROR_BUFFER_LIMIT = 50;
public LogConsumer(boolean skipLogMsgs,
List testExpectStrs, String fileName, int repeatLimit) {
super();
this.skipLogMsgs = skipLogMsgs;
this.testExpectStrs = testExpectStrs;
this.fileName = fileName;
this.repeatLimit = repeatLimit;
}
public StringBuilder consume(CharSequence line) {
{
lineNumber++;
Matcher m = ExpectedExceptionPattern.matcher(line);
if (m.find()) {
if ( m.group(1).equals("add")) {
expectedExceptions.add(Pattern.compile(m.group(2)));
} else {
//assume add and remove are the only choices
expectedExceptions.remove(Pattern.compile(m.group(2)));
}
return null;
}
}
if(skipLogMsgs) {
if(infoMsgFlag) {
if(logPattern.matcher(line).find()) {
infoMsgFlag = false;
} else if (blankPattern.matcher(line).matches()) {
infoMsgFlag = false;
return null;
} else {
return null;
}
}
if (infoOrBelowPattern.matcher(line).find()){
infoMsgFlag = true;
return null;
}
}
if ( eatLines != 0 ) {
eatLines--;
return null;
} else {
if(saveFlag || fatalOrErrorPattern.matcher(line).find()) {
if(! saveFlag) {
saveFlag = true;
tmpErrFlag = true;
if(checkExpectedStrs(line, expectedExceptions)) {
saveFlag = false;
tmpErrFlag = false;
tmpErrLines = 0;
}
if(tmpErrFlag) {
tmpErrLines=1;
all = new StringBuilder(line);
all.append("\n");
savelinenum = lineNumber;
}
} else {
if (causedByPattern.matcher(line).find()) {
tmpErrFlag = false;
tmpErrLines = 0;
saveFlag = false;
StringBuilder buffer = new StringBuilder();
buffer.append("-----------------------------------------------------------------------\n");
buffer.append("Found suspect string in ")
.append(fileName)
.append(" at line ")
.append(savelinenum).append("\n\n")
.append(all.toString());
return buffer;
} else if (checkExpectedStrs(line, expectedExceptions)) {
// reset the counters and throw it all away if it matches
// one of the registered ignorable strings
tmpErrFlag = false;
tmpErrLines = 0;
saveFlag = false;
}
// We save all the lines up to the next blank line so we're
//looking for a blank line here
if (blankPattern.matcher(line).matches()) {
// we found a blank line so print the suspect string
// and reset the savetag flag
saveFlag = false;
Matcher m = shortErrPattern.matcher(all.toString());
if (m.matches()) {
String shortName = m.group(1);
Integer i = (Integer) individalErrorCount.get(shortName);
Integer occurances =
new Integer((i == null) ? 1 : i.intValue() + 1);
individalErrorCount.put(shortName, occurances);
return enforceErrorLimit(occurances.intValue(),
all.toString(),
//reader.getLineNumber(),
savelinenum,
fileName);
} else {
//error in determining shortName, wing it
return enforceErrorLimit(1,
all.toString(),
lineNumber,
fileName);
}
}
// we're still saving lines to append them on to all which contains
// all the lines we're trying to save
if ( tmpErrFlag ) {
if ( tmpErrLines < ERROR_BUFFER_LIMIT ) {
tmpErrLines++;
all.append(line).append("\n");
}
if ( tmpErrLines == ERROR_BUFFER_LIMIT ) {
tmpErrLines++; //increment to prevent this line from repeating
all.append("GrepLogs: ERROR_BUFFER_LIMIT limit reached,")
.append(" the error was too long to display completely.\n");
}
}
}
// unique condition for when bridge server see log exception and
// logging level is set to fine. Message looks like this:
//[fine 2005/10/25 17:53:13.586 PDT gemfire2 Server connection from hobbes.gemstone.com:34466-0xf4 nid=0x23e40f1] Server connection from hobbes.gemstone.com:34466: Wrote exception:
//com.gemstone.gemfire.cache.EntryNotFoundException: remote-destroy-key
// also now handles a JMX WARNING
} else if(wroteExceptionPattern.matcher(line).find()
|| rmiWarnPattern.matcher(line).find()) {
//Eat only the single EntryNotFound Exception
eatLines=1;
// if we are here then the line didn't have severe or error in it and
// didn't meet any special cases that require eating lines
// Check for other kinds of exceptions. This is by no means inclusive
//of all types of exceptions that could occur and some ARE missed.
} else if (exceptionPattern.matcher(line).find()
|| javaLangErrorPattern.matcher(line).find()
|| (misformatedI18nMessagePattern.matcher(line).find()
&& !(infoOrBelowPattern.matcher(line).find()
&& rvvBitSetMessagePattern.matcher(line).find())) ) {
if(! checkExpectedStrs(line, expectedExceptions)) {
// it's the Exception colon that we want to find
// along with the next six words and define to shortline
// shortline is only used for the unique sting to count the
// number of times an exception match occurs. This is so
// we can suppress further printing if we hit the limit
Matcher m2 = exceptionPattern2.matcher(line);
Matcher m3 = exceptionPattern3.matcher(line);
Matcher m4 = exceptionPattern4.matcher(line);
String shortName = "";
if(m2.find()) {
shortName = m2.group(1);
} else if (m3.find()) {
shortName = m3.group(1);
} else if (m4.find()) {
shortName = m4.group(1);
}
Integer i = (Integer) individalErrorCount.get(shortName);
Integer occurances =
new Integer((i == null) ? 1 : i.intValue() + 1);
individalErrorCount.put(shortName, occurances);
return enforceErrorLimit(occurances.intValue(),
line + "\n",
lineNumber,
fileName);
}
}
}
return null;
}
public StringBuilder close() {
if(saveFlag) {
// Bug fix for severe that occurs at the end of a log file. Since we
// collect lines up to a blank line that never happens this prints the
// collection of in process suspect strings if we close the file and
// we're still trying to save lines
saveFlag = false;
StringBuilder buffer = new StringBuilder();
buffer.append("\n-----------------------------------------------------------------------\n")
.append("Found suspect string in ")
.append(fileName)
.append(" at line ")
.append(savelinenum)
.append("\n\n")
.append(all);
return buffer;
}
return null;
}
private boolean checkExpectedStrs(CharSequence line, List expectedExceptions) {
for(int i = 0; i < expectedExceptions.size(); i++) {
Pattern p = (Pattern) expectedExceptions.get(i);
if(p.matcher(line).find()) return true;
}
for(int i = 0; i < testExpectStrs.size(); i++) {
Pattern p = (Pattern) testExpectStrs.get(i);
if(p.matcher(line).find()) return true;
}
return false;
}
private StringBuilder enforceErrorLimit(int hits,
String line,
int linenum,
String filename) {
if ( hits <= repeatLimit ) {
StringBuilder buffer = new StringBuilder();
buffer.append("-----------------------------------------------------------------------\n")
.append("Found suspect string in ")
.append(filename)
.append(" at line ")
.append(linenum)
.append("\n\n")
.append(line)
.append("\n");
return buffer;
}
if ( hits == repeatLimit ) {
StringBuilder buffer = new StringBuilder();
buffer.append("\n\nHit occurrence limit of ")
.append(hits)
.append(" for this string.\n")
.append("Further reporting of this type of error will be suppressed.\n");
return buffer;
}
return null;
}
}