package hudson.plugins.logparser;
import hudson.model.AbstractBuild;
import java.io.*;
import java.util.HashMap;
import java.util.regex.*;
import java.util.ArrayList;
public class LogParserParser {
final private HashMap statusCount = new HashMap();
final private HashMap writers = new HashMap();
final private HashMap linkFiles = new HashMap();
final private String[] parsingRulesArray;
final private Pattern[] compiledPatterns;
// if key is 3-ERROR it shows how many errors are in section 3
final private HashMap statusCountPerSection = new HashMap();
final private ArrayList headerForSection = new ArrayList();
private int sectionCounter = 0;
final private LogParserDisplayConsts displayConstants = new LogParserDisplayConsts();
public LogParserParser(final String parsingRulesPath) throws FileNotFoundException , IOException {
// Count of lines in this status
statusCount.put(LogParserConsts.ERROR, 0);
statusCount.put(LogParserConsts.WARNING, 0);
statusCount.put(LogParserConsts.INFO, 0);
this.parsingRulesArray = LogParserUtils.readParsingRules(parsingRulesPath);
// This causes each regular expression to be compiled once for better performance
this.compiledPatterns = LogParserUtils.compilePatterns(this.parsingRulesArray);
}
/*
* This method creates the parsed log file : log.html
* It also creates the lists of links to these errors/warnings/info messages respectively :
* errorLinks.html, warningLinks.html, infoLinks.html
*/
public LogParserResult parseLog(final AbstractBuild build) throws FileNotFoundException , IOException {
// Get console log file
final File logFile = build.getLogFile();
final String logDirectory = logFile.getParent();
final String filePath = logFile.getAbsolutePath();
// Determine parsed log files
final String parsedFilePath = logDirectory+"/log_content.html";
final String errorLinksFilePath = logDirectory+"/logerrorLinks.html";
final String warningLinksFilePath = logDirectory+"/logwarningLinks.html";
final String infoLinksFilePath = logDirectory+"/loginfoLinks.html";
final String buildRefPath = logDirectory+"/log_ref.html";
final String buildWrapperPath = logDirectory+"/log.html";
// Record file paths in hash
linkFiles.put(LogParserConsts.ERROR, errorLinksFilePath);
linkFiles.put(LogParserConsts.WARNING, warningLinksFilePath);
linkFiles.put(LogParserConsts.INFO, infoLinksFilePath);
// Open console log for reading and all other files for writing
final BufferedReader reader = new BufferedReader(new FileReader(filePath));
final BufferedWriter writer = new BufferedWriter(new FileWriter(parsedFilePath));
// Record writers to links files in hash
writers.put(LogParserConsts.ERROR, new BufferedWriter(new FileWriter(errorLinksFilePath)));
writers.put(LogParserConsts.WARNING, new BufferedWriter(new FileWriter(warningLinksFilePath)));
writers.put(LogParserConsts.INFO, new BufferedWriter(new FileWriter(infoLinksFilePath)));
//Loop on the console log as long as there are input lines and parse line by line
//At the end of this loop, we will have:
// - a parsed log with colored lines
// - 3 links files which will be consolidated into one referencing html file.
// Create dummy header and section for beginning of log
final String shortLink = " <a target=\"content\" href=\"log_content.html\">Beginning of log</a>";
LogParserWriter.writeHeaderTemplateToAllLinkFiles(writers,sectionCounter); // This enters a line which will later be replaced by the actual header and count for this header
headerForSection.add(shortLink);
writer.write(LogParserConsts.htmlOpen);
String line = null;
while ((line=reader.readLine()) != null) {
final String status = getLineStatus(line);
final String parsedLine = parseLine(line,status);
// This is for displaying sections in the links part
writer.write(parsedLine);
writer.newLine(); // Write system dependent end of line.
}
writer.write(LogParserConsts.htmlClose);
//... Close reader and writer.
reader.close(); // Close to unlock.
writer.close(); // Close to unlock and flush to disk.
((BufferedWriter)writers.get(LogParserConsts.ERROR)).close();
((BufferedWriter)writers.get(LogParserConsts.WARNING)).close();
((BufferedWriter)writers.get(LogParserConsts.INFO)).close();
// Build the reference html from the warnings/errors/info html files created in the loop above
LogParserWriter.writeReferenceHtml( buildRefPath,
headerForSection,
statusCountPerSection,
displayConstants.getIconTable(),
displayConstants.getLinkListDisplay(),
displayConstants.getLinkListDisplayPlural(),
statusCount,
linkFiles);
// Write the wrapping html for the reference page and the parsed log page
LogParserWriter.writeWrapperHtml(buildWrapperPath);
//String hudsonRoot = Hudson.getInstance().getRootUrl() ; // hudson link
final String buildUrlPath = build.getUrl() ; //job/cat_log/58
final String buildActionPath = LogParserAction.getUrlNameStat(); //"parsed_console";
final String parsedLogURL = buildUrlPath+buildActionPath+"/log.html";
// Create result class
final LogParserResult result = new LogParserResult();
result.setHtmlLogFile(parsedFilePath);
result.setTotalErrors((Integer)statusCount.get(LogParserConsts.ERROR));
result.setTotalWarnings((Integer)statusCount.get(LogParserConsts.WARNING));
result.setTotalInfos((Integer)statusCount.get(LogParserConsts.INFO));
result.setErrorLinksFile(errorLinksFilePath);
result.setWarningLinksFile(warningLinksFilePath);
result.setInfoLinksFile(infoLinksFilePath);
result.setParsedLogURL(parsedLogURL);
result.setHtmlLogPath(logDirectory);
return result;
}
public String getLineStatus(final String line) {
for (int i=0;i<this.parsingRulesArray.length;i++){
final String parsingRule = this.parsingRulesArray[i];
if (!LogParserUtils.skipParsingRule(parsingRule) &&
this.compiledPatterns[i] != null &&
this.compiledPatterns[i].matcher(line).find()) {
final String status = parsingRule.split("\\s")[0];
return LogParserUtils.standardizeStatus(status);
}
}
return LogParserConsts.NONE;
}
public String parseLine(final String line) throws IOException {
return parseLine(line,null);
}
public String parseLine(final String line, final String status) throws IOException {
String parsedLine = line;
String effectiveStatus = status;
if (status.equals(LogParserConsts.START)) {
effectiveStatus = LogParserConsts.INFO;
}
parsedLine = parsedLine.replaceAll("<", "<"); // Allows < to be seen in log which is html
parsedLine = parsedLine.replaceAll(">", ">"); // Allows > to be seen in log which is html
if (effectiveStatus != null && !effectiveStatus.equals(LogParserConsts.NONE)) {
// Increment count of the status
incrementCounter(effectiveStatus);
incrementCounterPerSection(status,sectionCounter);
// Color line according to the status
final String parsedLineColored = colorLine(parsedLine,effectiveStatus);
// Mark line and add to left side links of highlighted lines
final String parsedLineColoredAndMarked = addMarkerAndLink(parsedLineColored,effectiveStatus,status);
parsedLine = parsedLineColoredAndMarked;
}
final StringBuffer result = new StringBuffer(parsedLine);
result.append("<br/>\n");
return result.toString() ;
}
public void incrementCounter(final String status) {
final int currentVal = (Integer)statusCount.get(status);
statusCount.put(status, currentVal+1);
}
public void incrementCounterPerSection(final String status, final int sectionNumber) {
final String key = LogParserUtils.getSectionCountKey(status,sectionNumber);
Integer currentValInteger = (Integer)statusCountPerSection.get(key);
// No value - entered yet - initialize with 0
if (currentValInteger == null) {
currentValInteger = new Integer(0);
}
final int newVal = currentValInteger+1;
statusCountPerSection.put(key, newVal);
}
private String colorLine(final String line,final String status) {
final String color = (String)displayConstants.getColorTable().get(status);
final StringBuffer result = new StringBuffer("<font color=\"");
result.append(color);
result.append("\">");
result.append(line);
result.append("</font>");
return result.toString();
}
private String addMarkerAndLink(final String line,final String effectiveStatus,final String status) throws IOException {
// Add marker
final String statusCountStr = ((Integer)statusCount.get(effectiveStatus)).toString();
final String marker = effectiveStatus + statusCountStr;
// Add link
final StringBuffer shortLink = new StringBuffer(" <a target=\"content\" href=\"log_content.html#");
shortLink.append(marker);
shortLink.append("\">");
shortLink.append(line);
shortLink.append("</a>");
final StringBuffer link = new StringBuffer("<li>");
link.append(statusCountStr);
link.append(shortLink);
link.append("</li><br/>");
final BufferedWriter linkWriter = (BufferedWriter)writers.get(effectiveStatus);
linkWriter.write(link.toString());
linkWriter.newLine(); // Write system dependent end of line.
// Mark the line
final StringBuffer markedLine = new StringBuffer("<a name=\"");
markedLine.append(marker);
markedLine.append("\"></a>");
markedLine.append(line);
// Handle case where we are entering a new section
if (status.equals(LogParserConsts.START)) {
sectionCounter++;
LogParserWriter.writeHeaderTemplateToAllLinkFiles(writers,sectionCounter); // This enters a line which will later be replaced by the actual header and count for this header
final StringBuffer brShortLink = new StringBuffer("<br/>");
brShortLink.append(shortLink);
headerForSection.add(brShortLink.toString());
}
return markedLine.toString();
}
}