/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.pluginapi.event.log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.Nullable; import org.rhq.core.domain.event.Event; import org.rhq.core.domain.event.EventSeverity; /** * A {@link LogEntryProcessor} for multi-line log files - provides several abstract methods that subclasses must * implement. * * @author Ian Springer */ public abstract class MultiLineLogEntryProcessor implements LogEntryProcessor { protected final Log log = LogFactory.getLog(this.getClass()); protected String eventType; protected File logFile; protected EventSeverity minimumSeverity; protected Pattern includesPattern; protected DateFormat dateFormat; public MultiLineLogEntryProcessor(String eventType, File logFile) { this.eventType = eventType; this.logFile = logFile; } @Nullable public Set<Event> processLines(BufferedReader bufferedReader) throws IOException { // Use a LinkedHashSet so the Events are in the same order as the log entries they correspond to. Set<Event> events = new LinkedHashSet<Event>(); LogEntry currentEntry = null; String line; while ((line = bufferedReader.readLine()) != null) { currentEntry = processLine(line, events, currentEntry); } // We've reached the end of the passed-in buffer, so assume the current entry is complete, and add an Event for // it. addEventForCurrentEntry(events, currentEntry); return events; } public void setMinimumSeverity(EventSeverity minimumSeverity) { this.minimumSeverity = minimumSeverity; } public void setIncludesPattern(Pattern includesPattern) { this.includesPattern = includesPattern; } public void setDateFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; } protected LogEntry processLine(String line, Set<Event> events, LogEntry currentEntry) { Matcher matcher = getPattern().matcher(line); if (matcher.matches()) { // A matching line means this is the beginning of a new entry, which tells us the current entry // (if there is one) has no more additional lines; we can therefore add an Event for that entry. addEventForCurrentEntry(events, currentEntry); // And then start building up a new entry... try { currentEntry = processPrimaryLine(matcher); } catch (ParseException e) { // NOTE: Do not throw an exception here, otherwise none of the remaining lines that were passed in to // processLines() will get processed. log.warn("Failed to parse line [" + line + "]."); currentEntry = null; } } else { // If the line didn't match, assume it's an additional line (e.g. part of a stack trace). if (currentEntry != null) { currentEntry.appendLineToDetail(line); } } return currentEntry; } protected abstract Pattern getPattern(); private void addEventForCurrentEntry(Set<Event> events, LogEntry currentEntry) { if (currentEntry != null) { if (currentEntry.getSeverity().isAtLeastAsSevereAs(this.minimumSeverity) && (this.includesPattern == null || this.includesPattern.matcher(currentEntry.getDetail()).find())) { Event event = new Event(this.eventType, this.logFile.getPath(), currentEntry.getDate().getTime(), currentEntry.getSeverity(), currentEntry.getDetail()); events.add(event); } } } protected abstract LogEntry processPrimaryLine(Matcher matcher) throws ParseException; protected abstract DateFormat getDefaultDateFormat(); protected Date parseDateString(String dateString) throws ParseException { Date timestamp = null; DateFormat dateFormat = (this.dateFormat != null) ? this.dateFormat : getDefaultDateFormat(); if (dateFormat != null) { try { timestamp = dateFormat.parse(dateString); } catch (java.text.ParseException e) { throw new ParseException("Unable to parse date [" + dateString + "] using date format [" + dateFormat + "].", e); } catch (RuntimeException e) { throw new ParseException("Unable to parse date [" + dateString + "] using date format [" + dateFormat + "].", e); } setDateIfNotSet(timestamp); } return timestamp; } protected static void setDateIfNotSet(Date timestamp) { Calendar date = convertToCalendar(timestamp); // If the format specified a time, but no date, the date will be Jan 1, 1970. In this case, set the date to // today's date. if (date.get(Calendar.YEAR) == 1970) { Calendar currentDate = Calendar.getInstance(); date.set(currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)); timestamp.setTime(date.getTimeInMillis()); } } private static Calendar convertToCalendar(Date timestamp) { Calendar date = Calendar.getInstance(); date.setTime(timestamp); return date; } protected static class LogEntry { private Date date; private EventSeverity severity; private StringBuilder detail; public LogEntry(Date date, EventSeverity severity, String detail) { this.date = date; this.severity = severity; this.detail = new StringBuilder(detail); } Date getDate() { return date; } EventSeverity getSeverity() { return severity; } String getDetail() { return detail.toString(); } void appendLineToDetail(String string) { this.detail.append("\n"); this.detail.append(string); } } protected static class ParseException extends Exception { public ParseException(String message) { super(message); } public ParseException(String message, Throwable cause) { super(message, cause); } } }