You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by sd...@apache.org on 2004/02/29 21:23:40 UTC
cvs commit: logging-log4j/src/java/org/apache/log4j/varia LogFilePatternReceiverBeanInfo.java LogFilePatternReceiver.java
sdeboy 2004/02/29 12:23:40
Modified: src/java/org/apache/log4j/chainsaw/receivers known.receivers
Added: src/java/org/apache/log4j/varia
LogFilePatternReceiverBeanInfo.java
LogFilePatternReceiver.java
Log:
Adding a LogFilePatternReceiver, which can parse non-XML-based log files, with some limitations
Revision Changes Path
1.3 +1 -0 logging-log4j/src/java/org/apache/log4j/chainsaw/receivers/known.receivers
Index: known.receivers
===================================================================
RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/chainsaw/receivers/known.receivers,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- known.receivers 23 Dec 2003 01:50:13 -0000 1.2
+++ known.receivers 29 Feb 2004 20:23:40 -0000 1.3
@@ -3,3 +3,4 @@
org.apache.log4j.net.SocketReceiver
org.apache.log4j.net.UDPReceiver
org.apache.log4j.net.XMLSocketReceiver
+org.apache.log4j.varia.LogFilePatternReceiver
1.1 logging-log4j/src/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java
Index: LogFilePatternReceiverBeanInfo.java
===================================================================
/*
* ============================================================================
* The Apache Software License, Version 1.1
* ============================================================================
*
* Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by the Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "log4j" and "Apache Software Foundation" must not be used to
* endorse or promote products derived from this software without prior
* written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache", nor may
* "Apache" appear in their name, without prior written permission of the
* Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Apache Software Foundation. For more information on the
* Apache Software Foundation, please see <http://www.apache.org/>.
*
*/
package org.apache.log4j.varia;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
/**
* BeanInfo class for the meta-data of the LogFilePatternReceiver.
*
*/
public class LogFilePatternReceiverBeanInfo extends SimpleBeanInfo {
/* (non-Javadoc)
* @see java.beans.BeanInfo#getPropertyDescriptors()
*/
public PropertyDescriptor[] getPropertyDescriptors() {
try {
return new PropertyDescriptor[] {
new PropertyDescriptor("fileName", LogFilePatternReceiver.class),
new PropertyDescriptor("timestampFormat", LogFilePatternReceiver.class),
new PropertyDescriptor("logFormat", LogFilePatternReceiver.class),
};
} catch (Exception e) {
}
return null;
}
}
1.1 logging-log4j/src/java/org/apache/log4j/varia/LogFilePatternReceiver.java
Index: LogFilePatternReceiver.java
===================================================================
/*
* ============================================================================
* The Apache Software License, Version 1.1
* ============================================================================
*
* Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
* tion, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by the Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "log4j" and "Apache Software Foundation" must not be used to
* endorse or promote products derived from this software without prior
* written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache", nor may
* "Apache" appear in their name, without prior written permission of the
* Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Apache Software Foundation. For more information on the
* Apache Software Foundation, please see <http://www.apache.org/>.
*
*/
package org.apache.log4j.varia;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.helpers.Constants;
import org.apache.log4j.plugins.Receiver;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* A receiver which supports the definition of the log format using keywords, the
* contained timestamp using SimpleDateFormat's format support, and the file name
*
* FEATURES:
* specify the file to be processed
* specify the timestamp format (if one exists)
* specify the layout used in the log file
* define your file's layout using these keywords along with any text being added as delimiters
* supports the conversion of exceptions found in the file
*
* TIMESTAMP
* LOGGER
* LEVEL
* THREAD
* CLASS
* FILE
* LINE
* METHOD
* RELATIVETIME
* MESSAGE
* *
*
* For example,
*
* If your file's patternlayout is this:
* %d %-5p [%t] %C{2} (%F:%L) - %m%n
*
* specify this as the log format:
* TIMESTAMP LEVEL [THREAD] CLASS (FILE:LINE) - MESSAGE
*
* If your file's patternlayout is this:
* %r [%t] %-5p %c %x - %m%n
*
* specify this as the log format:
* RELATIVETIME [THREAD] LEVEL LOGGER * - MESSAGE
*
* Note the * - it can be used to ignore a single word or sequence of words in the log file
* (in order for the wildcard to ignore a sequence of words, the text being ignored must be
* followed by some delimiter, like '-' or '[')
*
* Note how keywords may be surrounded by delimiters, and in the second example,
* ndc is ignored (even multiple words in the ndc in this case, since the keyword
* is followed by a delimiter (-)
*
* LIMITATIONS:
* - no support for mdc, ndc, properties or the single-line version of throwable supported by patternlayout
* - relativetime is set as a property
* - loggers with spaces in their names are not supported (but may work if followed by a delimiter,
* similar to wildcard example above)
* - messages must appear at the end of the line
* - note the explanation above describing the rules for ignoring text using the wildcard
* keyword
* - exceptions will be converted if the exception stack trace lines (other than the first line)
* are stored in the log file with a tab as the first character
*
* EXAMPLE RECEIVER CONFIGURATION (add these as params, specifying a LogFilePatternReceiver 'plugin'
*
* param: "timestampFormat" value="yyyy-MM-d HH:mm:ss,SSS"
* param: "logFormat" value="RELATIVETIME [THREAD] LEVEL LOGGER * - MESSAGE"
* param: "fileName" value="c:/logs/A4.log"
*
* NOTE: in our example file content below, the timestampFormat entry defined above
* is not required, but included as an example of how to specify the format. See SimpleDateFormat
* for more information.
*
* This configuration will be able to process these sample events:
* 710 [ Thread-0] DEBUG first.logger first - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test>
* 880 [ Thread-2] DEBUG first.logger third - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test>
* 880 [ Thread-0] INFO first.logger first - infomsg-0
* java.lang.Exception: someexception-first
* at Generator2.run(Generator2.java:102)
*
*/
public class LogFilePatternReceiver extends Receiver {
public static final String TIMESTAMP = "TIMESTAMP";
public static final String LOGGER = "LOGGER";
public static final String LEVEL = "LEVEL";
public static final String THREAD = "THREAD";
public static final String CLASS = "CLASS";
public static final String FILE = "FILE";
public static final String LINE = "LINE";
public static final String METHOD = "METHOD";
public static final String RELATIVETIME = "RELATIVETIME";
public static final String MESSAGE = "MESSAGE";
public static final String WILDCARD = "*";
private static final String TAB = "\t";
private final List keywords = new ArrayList();
private final List logFormatFields = new ArrayList();
private final Map defaultMap = new HashMap();
private SimpleDateFormat dateFormat;
private String timestampFormat;
private String logFormat;
private String fileName;
private String shortFileName;
/**
* Creates a new LogFilePatternReceiver object.
*/
public LogFilePatternReceiver() {
//define defaults which may not be provided by log file but are required by log4j
defaultMap.put(LOGGER, "Unknown");
//supported keyword replacements are expected to be single words, except for the MESSAGE keyword,
//which is expected to appear at the end of each entry in the log file
//since throwable, mdc, ndc and properties can all have spaces, they're not yet supported
//while loggers may containspaces, only loggers without spaces are currently supported
//fullinfo pattern is not supported directly - build from individual keywords instead
keywords.add(TIMESTAMP);
keywords.add(LOGGER);
keywords.add(LEVEL);
keywords.add(THREAD);
keywords.add(CLASS);
keywords.add(FILE);
keywords.add(LINE);
keywords.add(METHOD);
//wildcard removes any single word
//will also remove any number of words if the next entry in the log file format
//is not found in the wildcard text itself
//(for example, 'LOGGER * [THREAD]' would successfully resolve keywords for
//'MYLOGGER any text here [Thread-0]'
//but, 'LOGGER * THREAD' would not successfully resolve keywords for
//'MYLOGGER any text here Thread-0'
//but would successfully resolve
//'MYLOGGER singleword Thread-0'
keywords.add(WILDCARD);
//supported as a property on the event
keywords.add(RELATIVETIME);
//keywords.add("NDC");
//keywords.add("MDC");
//keywords.add("THROWABLE");
//keywords.add("PROPERTIES");
keywords.add(MESSAGE);
}
/**
* Test
*
* @param args file name to parse
*/
public static void main(String[] args) {
LogFilePatternReceiver parser = new LogFilePatternReceiver();
parser.setTimestampFormat("yyyy-MM-d HH:mm:ss,SSS");
//parser.parse(args[0]);
//%d %-5p [%t] %C{2} (%F:%L) - %m%n -- a2
//parser.setLogFormat("TIMESTAMP LEVEL * [THREAD] CLASS (FILE:LINE) - MESSAGE");
//parser.initialize();
//System.out.println("Created event: " + parser.convertToEvent("2004-12-13 22:49:22,820 DEBUG SOME VALUE [Thread-0] Generator2 (Generator2.java:100) - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test>"));
//parser.initialize("THREAD LEVEL LOGGER - MESSAGE");
parser.setLogFormat("RELATIVETIME [THREAD] LEVEL LOGGER * - MESSAGE");
parser.setFileName(args[0]);
parser.initialize();
try {
parser.process(new FileReader(new File(parser.getFileName())));
//parser.process(new StringReader("2004-12-13 22:49:22,820 DEBUG SOME VALUE [Thread-0] Generator2 (Generator2.java:100) - <test> <test2>something here</test2> <test3 blah=something/> <test4> <test5>something else</test5> </test4></test>\nException blah\n\tat someplace.java:555\n\tat nextline blah:55"));
} catch (IOException ioe) {
ioe.printStackTrace();
}
//comment out doPost calls to run via main
}
/**
* Accessor
*
* @return file name
*/
public String getFileName() {
return fileName;
}
/**
* Mutator
*
* @param fileName
*/
public void setFileName(String fileName) {
this.fileName = fileName;
shortFileName=new File(fileName).getName();
}
/**
* Accessor
*
* @return log format
*/
public String getLogFormat() {
return logFormat;
}
/**
* Mutator
*
* @param logFormat the format
*/
public void setLogFormat(String logFormat) {
this.logFormat = logFormat;
}
/**
* Mutator
*
* @param timestampFormat
*/
public void setTimestampFormat(String timestampFormat) {
this.timestampFormat = timestampFormat;
}
/**
* Accessor
*
* @return timestamp format
*/
public String getTimestampFormat() {
return timestampFormat;
}
/**
* Convert a reader into a buffered reader, reading lines and converting lines into log events
*
* @param unbufferedReader
*
* @throws IOException
*/
public void process(Reader unbufferedReader) throws IOException {
BufferedReader reader = new BufferedReader(unbufferedReader);
LinkedList list = new LinkedList();
String line = null;
while ((line = reader.readLine()) != null) {
//ignore blank lines in file
if (line.length() == 0) {
continue;
}
//System.out.println("Added: " + line);
list.addLast(line);
if (list.size() > 2) {
//System.out.println("size > 2 - processing");
if (line.startsWith(TAB)) {
String firstLine = (String) list.remove(0);
//System.out.println("exception line1: " + firstLine);
while ((line != null) && line.startsWith(TAB)) {
line = reader.readLine();
if ((line != null) && (line.length() != 0)) {
list.addLast(line);
}
}
//hold a reference to the last entry added - while loop adds one too many
String nextLine = (String) list.getLast();
if ((nextLine != null) && !(nextLine.startsWith(TAB))) {
list.removeLast();
}
String[] exception = new String[list.size()];
for (int i = 0, j = list.size(); i < j; i++) {
exception[i] = (String) list.remove(0);
//System.out.println("exception " + i + ".." + exception[i]);
}
//now that exceptions have been taken from list, re-add last entry if not an
//exception line
if ((nextLine != null) && !(nextLine.startsWith(TAB))) {
list.addLast(nextLine);
}
//GENERATE EXCEPTION EVENT
LoggingEvent event = convertToEvent(firstLine, exception);
//System.out.println(
// "created event with exception " + event.getLoggerName() + ".."
// + event.getMessage());
doPost(event);
} else {
//GENERATE NON-EXCEPTION EVENT
LoggingEvent event = convertToEvent((String) list.remove(0));
//System.out.println(
// "Created event " + event.getLoggerName() + ".."
// + event.getMessage());
doPost(event);
}
}
}
//System.out.println(
// "outside loop - processing remaining entries: " + list.size());
//clean up remaining - should not be an exception
for (int k = 0, l = list.size(); k < l; k++) {
String s = (String) list.remove(0);
if ((s != null) && (s.length() > 0)) {
//GENERATE NON-EXCEPTION EVENT
LoggingEvent event = convertToEvent(s);
//System.out.println(
// "cleanup - Created non-exception event " + event.getLoggerName()
// + ".." + event.getMessage());
doPost(event);
}
}
}
/**
* Walk the entries in the log format fields, adding keyword matches to a
* map
*
* @param logEntry
*
* @return map of keywords to values in the log entry
*/
private Map extractEventFields(String logEntry) {
//System.out.println("extracting from " + logEntry);
Map fieldMap = new HashMap(defaultMap);
for (int i = 0; i < logFormatFields.size(); i++) {
String thisField = (String) logFormatFields.get(i);
//ignore wildcard entries - skip to next field
if (thisField.equals(WILDCARD)) {
String nextField = (String) logFormatFields.get(i + 1);
logEntry = logEntry.substring(logEntry.indexOf(nextField));
} else if (thisField.equals(TIMESTAMP)) {
String nextField = (String) logFormatFields.get(i + 1);
//uses nextfield and length of format to guess at timestamp field
//luckily, SimpleDateFormat is very flexible and forgiving of trailing text
int firstLength =
logEntry.substring(0, timestampFormat.length()).length() - 1;
int nextLength =
(firstLength
+ logEntry.substring(firstLength - 1).indexOf(nextField)) - 1;
int length = Math.max(firstLength, nextLength);
// System.out.println("parsing timestamp: " + logEntry.substring(0, length));
fieldMap.put(thisField, logEntry.substring(0, length));
//System.out.println(
// "added " + thisField + ":" + fieldMap.get(thisField));
logEntry = logEntry.substring(length);
} else if (keywords.contains(thisField)) {
logEntry = logEntry.trim();
if (i < (logFormatFields.size() - 1)) {
//expects no two keywords to be butted up directly against eachother
String nextField = (String) logFormatFields.get(i + 1);
//System.out.println(
// thisField + " = " + logEntry.substring(0, logEntry.indexOf(nextField)).trim());
fieldMap.put(
thisField,
logEntry.substring(0, logEntry.indexOf(nextField)).trim());
//System.out.println(
// "added " + thisField + ":" + fieldMap.get(thisField));
logEntry = logEntry.substring(logEntry.indexOf(nextField));
} else {
fieldMap.put(thisField, logEntry.trim());
//System.out.println(
// "added " + thisField + ":" + fieldMap.get(thisField));
}
} else {
//ignore non-fields
//System.out.println("text: " + thisField);
logEntry = logEntry.substring(thisField.length());
}
}
return fieldMap;
}
/**
* Helper convert method that doesn't support exceptions
*
* @param logEntry
*
* @return logging event
*/
private LoggingEvent convertToEvent(String logEntry) {
return convertToEvent(logEntry, null);
}
/**
* Convert log entry with exceptions
*
* @param logEntry
* @param exception string array of exception lines
*
* @return logging event
*/
private LoggingEvent convertToEvent(String logEntry, String[] exception) {
return convertToEvent(extractEventFields(logEntry), exception);
}
/**
* Convert entries in the map of keywords to values in a log entry into a
* loggingEvent
*
* @param fieldMap
* @param exception
*
* @return logging event
*/
private LoggingEvent convertToEvent(Map fieldMap, String[] exception) {
Logger logger = null;
long timeStamp = 0L;
String level = null;
String threadName = null;
Object message = null;
// String ndc = null;
// Hashtable mdc = null;
String className = null;
String methodName = null;
String eventFileName = null;
String lineNumber = null;
Hashtable properties = new Hashtable();
if (dateFormat != null && fieldMap.containsKey(TIMESTAMP)) {
try {
timeStamp =
dateFormat.parse((String) fieldMap.get(TIMESTAMP)).getTime();
} catch (Exception e) {
e.printStackTrace();
}
}
if (timeStamp == 0L) {
timeStamp = System.currentTimeMillis();
}
logger = Logger.getLogger((String) fieldMap.get(LOGGER));
level = (String) fieldMap.get(LEVEL);
threadName = (String) fieldMap.get(THREAD);
message = (String) fieldMap.get(MESSAGE);
className = (String) fieldMap.get(CLASS);
methodName = (String) fieldMap.get(METHOD);
eventFileName = (String) fieldMap.get(FILE);
lineNumber = (String) fieldMap.get(LINE);
if (fieldMap.get(RELATIVETIME) != null) {
properties.put(RELATIVETIME, fieldMap.get(RELATIVETIME));
}
Level levelImpl = Level.toLevel(level);
LocationInfo info = null;
if (
(eventFileName != null) || (className != null) || (methodName != null)
|| (lineNumber != null)) {
info =
new LocationInfo(eventFileName, className, methodName, lineNumber);
}
properties.put(Constants.HOSTNAME_KEY, "file");
properties.put(Constants.APPLICATION_KEY, shortFileName);
LoggingEvent event =
new LoggingEvent(
logger.getName(), logger, timeStamp, levelImpl, threadName, message,
null, null, exception, info, properties);
return event;
}
/**
* Initialize and post log entries to framework
*/
public void activateOptions() {
new Thread(
new Runnable() {
public void run() {
initialize();
try {
process(new FileReader(new File(getFileName())));
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}).start();
}
/**
* Walk the passed-in log format, building a list of entries which are (from
* left to right), either a keyword, or any string of characters not a
* keyword.
*/
private void initialize() {
if (timestampFormat != null) {
dateFormat = new SimpleDateFormat(timestampFormat);
}
int i = 0;
int j = 0;
while (i < logFormat.length()) {
String keyword = endsInKeyword(logFormat.substring(i, j));
//System.out.println("current arg is " + arg.substring(i,j));
//fragment ends in keyword.
//add everything before the keyword as an entry in the list
//then add the keyword
if (keyword != null) {
String firstPart = logFormat.substring(i, j - keyword.length());
if (firstPart.length() > 0) {
//System.out.println("ADDED FIRSTPART: " + firstPart);
logFormatFields.add(firstPart);
}
logFormatFields.add(keyword);
//System.out.println("ADDED: " + keyword);
i = j;
j = j + 1;
} else {
j++;
}
}
}
/**
* Check to see if the log format fragment passed in ends with a keyword.
*
* @param logFormatFragment
*
* @return keyword or null
*/
private String endsInKeyword(String logFormatFragment) {
Iterator iter = keywords.iterator();
while (iter.hasNext()) {
String keyword = (String) iter.next();
if (logFormatFragment.endsWith(keyword)) {
return keyword;
}
}
return null;
}
/* (non-Javadoc)
* @see org.apache.log4j.plugins.Plugin#shutdown()
*/
public void shutdown() {
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org