/*
* 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.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.rhq.core.domain.event.EventSeverity;
/**
* A {@link org.rhq.core.pluginapi.event.log.LogEntryProcessor} for Log4J log files.
* <pre>
* By default we support log entries of the format: date [delimiter]severity[delimiter] message
* Where:
* - the optional delimiters are either square brackets ort parens, with or without spaces
* - the date format is one of the built-ins below, or a user-specified date format for the logfile source
* - the message portion may contain multiple lines.
* For example:
* 2007-12-21 15:32:49,514 DEBUG [com.example.FooBar] run: IdleRemover notifying pools, interval: 450000
* 2007/12/21 15:32:49,514 (DEBUG) [com.example.FooBar] run: IdleRemover notifying pools, interval: 450000
* 2007-Dec-21 15:32:49 [ DEBUG ] [com.example.FooBar] run: IdleRemover notifying pools, interval: 450000
* </pre>
* @author Ian Springer
*/
public class Log4JLogEntryProcessor extends MultiLineLogEntryProcessor {
private static final String REGEX;
private static final Pattern PATTERN;
//note that the DateFormat instances are INTENTIONALLY instance fields.
//DateFormats are not thread safe and because we can have multiple log processors
//each processing its log file in a separate thread, we'd get formatting errors
//where there weren't any...
private static final String ISO8601_DATE_PATTERN = "yyyy-MM-dd kk:mm:ss,SSS";
private final DateFormat iso8601DateFormat = new SimpleDateFormat(ISO8601_DATE_PATTERN);
private static final String ABSOLUTE_DATE_PATTERN = "kk:mm:ss,SSS";
private final DateFormat absoluteDateFormat = new SimpleDateFormat(ABSOLUTE_DATE_PATTERN);
private static final String DATE_DATE_PATTERN = "dd MMM yyyy kk:mm:ss,SSS";
private final DateFormat dateDateFormat = new SimpleDateFormat(DATE_DATE_PATTERN);
private static final Map<Priority, EventSeverity> PRIORITY_TO_SEVERITY_MAP = new LinkedHashMap<Priority, EventSeverity>();
static {
// just in case there is something unanticipated that our default pattern doesn't like, allow
// a backdoor prop to set the REGEX pattern.
String regex = System.getProperty("rhq.agent.event.log4j.regex");
REGEX = (null != regex) ? regex : "(.*?) [\\[\\(]??\\s*(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\\s*[\\]\\)]?? (.*)";
PATTERN = Pattern.compile(REGEX);
PRIORITY_TO_SEVERITY_MAP.put(Priority.TRACE, EventSeverity.DEBUG);
PRIORITY_TO_SEVERITY_MAP.put(Priority.DEBUG, EventSeverity.DEBUG);
PRIORITY_TO_SEVERITY_MAP.put(Priority.INFO, EventSeverity.INFO);
PRIORITY_TO_SEVERITY_MAP.put(Priority.WARN, EventSeverity.WARN);
PRIORITY_TO_SEVERITY_MAP.put(Priority.ERROR, EventSeverity.ERROR);
PRIORITY_TO_SEVERITY_MAP.put(Priority.FATAL, EventSeverity.FATAL);
}
public Log4JLogEntryProcessor(String eventType, File logFile) {
super(eventType, logFile);
}
protected Pattern getPattern() {
return PATTERN;
}
protected LogEntry processPrimaryLine(Matcher matcher) throws ParseException {
String dateString = matcher.group(1);
Date timestamp = parseDateString(dateString);
String priorityString = matcher.group(2);
Priority priority;
try {
priority = Priority.valueOf(priorityString);
} catch (IllegalArgumentException e) {
throw new ParseException("Unknown priority: " + priorityString);
}
EventSeverity severity = PRIORITY_TO_SEVERITY_MAP.get(priority);
String detail = matcher.group(3);
return new LogEntry(timestamp, severity, detail);
}
protected DateFormat getDefaultDateFormat() {
return iso8601DateFormat;
}
protected Date parseDateString(String dateString) throws ParseException {
Date timestamp;
try {
timestamp = super.parseDateString(dateString);
} catch (ParseException e) {
try {
timestamp = dateDateFormat.parse(dateString);
} catch (java.text.ParseException e1) {
try {
timestamp = absoluteDateFormat.parse(dateString);
} catch (java.text.ParseException e2) {
throw new ParseException("Unable to parse date '" + dateString
+ "' using either ISO8601, DATE, or ABSOLUTE date formats. Please specify a date format.");
}
}
}
setDateIfNotSet(timestamp);
return timestamp;
}
private enum Priority {
TRACE, DEBUG, INFO, WARN, ERROR, FATAL
}
}