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 2009/05/13 07:54:49 UTC

svn commit: r774218 - in /logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia: LogFilePatternReceiver.java LogFilePatternReceiverBeanInfo.java

Author: sdeboy
Date: Wed May 13 05:54:49 2009
New Revision: 774218

URL: http://svn.apache.org/viewvc?rev=774218&view=rev
Log:
Added two new parameters to LogFilePatternReceiver:
 - waitmillis (configurable delay between checks for updates to a file - used when tailing=true)
 - appendNonMatches (if true, append non-matching lines to the log, associated with the logger 'Unknown')

Added one new parameter to VFSLogFilePatternReceiver (extends LogFilePatternReceiver, so it 
inherits the waitMillis and appendNonMatches params): 
 - autoReconnect (if true, when a file is truncated, begin processing the file contents from the beginning of the file - supports continuous tailing of a file even when the file is recreated)
 

Modified:
    logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java
    logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java

Modified: logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java
URL: http://svn.apache.org/viewvc/logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java?rev=774218&r1=774217&r2=774218&view=diff
==============================================================================
--- logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java (original)
+++ logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java Wed May 13 05:54:49 2009
@@ -157,7 +157,7 @@
   private static final String DEFAULT_HOST = "file";
   
   //all lines other than first line of exception begin with tab followed by 'at' followed by text
-  private static final String EXCEPTION_PATTERN = "\tat.*";
+  private static final String EXCEPTION_PATTERN = "^\\s+at.*";
   private static final String REGEXP_DEFAULT_WILDCARD = ".*?";
   private static final String REGEXP_GREEDY_WILDCARD = ".*";
   private static final String PATTERN_WILDCARD = "*";
@@ -177,6 +177,7 @@
   private String path;
   private boolean tailing;
   private String filterExpression;
+  private long waitMillis = 2000; //default 2 seconds
 
   private Perl5Util util = null;
   private Perl5Compiler exceptionCompiler = null;
@@ -192,11 +193,14 @@
   private String regexp;
   private Reader reader;
   private Pattern regexpPattern;
+  private Pattern exceptionPattern;
   private String timestampPatternText;
 
-private boolean useCurrentThread;
+  private boolean useCurrentThread;
+  public static final int MISSING_FILE_RETRY_MILLIS = 10000;
+  private boolean appendNonMatches;
 
-  public LogFilePatternReceiver() {
+    public LogFilePatternReceiver() {
     keywords.add(TIMESTAMP);
     keywords.add(LOGGER);
     keywords.add(LEVEL);
@@ -207,6 +211,12 @@
     keywords.add(METHOD);
     keywords.add(MESSAGE);
     keywords.add(NDC);
+    exceptionCompiler = new Perl5Compiler();
+    try {
+        exceptionPattern = exceptionCompiler.compile(EXCEPTION_PATTERN);
+    } catch (MalformedPatternException mpe) {
+        //shouldn't happen
+    }
   }
 
   /**
@@ -229,6 +239,22 @@
 
   /**
    * Accessor
+   * @return append non matches
+   */
+  public boolean isAppendNonMatches() {
+      return appendNonMatches;
+  }
+
+  /**
+   * Mutator
+   * @param appendNonMatches
+   */
+  public void setAppendNonMatches(boolean appendNonMatches) {
+      this.appendNonMatches = appendNonMatches;
+  }
+
+  /**
+   * Accessor
    * 
    * @return filter expression
    */
@@ -292,9 +318,9 @@
     return logFormat;
   }
 
-  /**
+    /**
    * Mutator
-   * 
+   *
    * @param logFormat
    *          the format
    */
@@ -302,18 +328,18 @@
     this.logFormat = logFormat;
   }
 
-  /**
+    /**
    * Mutator.  Specify a pattern from {@link java.text.SimpleDateFormat}
-   * 
+   *
    * @param timestampFormat
    */
   public void setTimestampFormat(String timestampFormat) {
     this.timestampFormat = timestampFormat;
   }
 
-  /**
+    /**
    * Accessor
-   * 
+   *
    * @return timestamp format
    */
   public String getTimestampFormat() {
@@ -321,54 +347,65 @@
   }
 
   /**
+   * Accessor
+   * @return millis between retrieves of content
+   */
+  public long getWaitMillis() {
+    return waitMillis;
+  }
+
+  /**
+   * Mutator
+   * @param waitMillis
+   */
+  public void setWaitMillis(long waitMillis) {
+    this.waitMillis = waitMillis;
+  }
+
+    /**
    * Walk the additionalLines list, looking for the EXCEPTION_PATTERN.
    * <p>
-   * Return the index of the first matched line minus 1 
-   * (the match is the 2nd line of an exception)
+   * Return the index of the first matched line
+   * (the match may be the 1st line of an exception)
    * <p>
    * Assumptions: <br>
    * - the additionalLines list may contain both message and exception lines<br>
    * - message lines are added to the additionalLines list and then
-   * exception lines (all message lines occur in the list prior to all 
+   * exception lines (all message lines occur in the list prior to all
    * exception lines)
-   * 
+   *
    * @return -1 if no exception line exists, line number otherwise
    */
   private int getExceptionLine() {
-    try {
-      Pattern exceptionPattern = exceptionCompiler.compile(EXCEPTION_PATTERN);
-      for (int i = 0; i < additionalLines.size(); i++) {
-        if (exceptionMatcher.matches((String) additionalLines.get(i), exceptionPattern)) {
-          return i - 1;
-        }
+    for (int i = 0; i < additionalLines.size(); i++) {
+      if (exceptionMatcher.matches((String) additionalLines.get(i), exceptionPattern)) {
+        return i;
       }
-    } catch (MalformedPatternException mpe) {
-      getLogger().warn("Bad pattern: " + EXCEPTION_PATTERN);
     }
     return -1;
   }
 
-  /**
+    /**
    * Combine all message lines occuring in the additionalLines list, adding
    * a newline character between each line
    * <p>
    * the event will already have a message - combine this message
-   * with the message lines in the additionalLines list 
+   * with the message lines in the additionalLines list
    * (all entries prior to the exceptionLine index)
-   * 
+   *
    * @param firstMessageLine primary message line
    * @param exceptionLine index of first exception line
    * @return message
    */
   private String buildMessage(String firstMessageLine, int exceptionLine) {
-    if (additionalLines.size() == 0 || exceptionLine == 0) {
+    if (additionalLines.size() == 0) {
       return firstMessageLine;
     }
     StringBuffer message = new StringBuffer();
     if (firstMessageLine != null) {
       message.append(firstMessageLine);
     }
-      
+
     int linesToProcess = (exceptionLine == -1?additionalLines.size(): exceptionLine);
 
     for (int i = 0; i < linesToProcess; i++) {
@@ -378,12 +415,12 @@
     return message.toString();
   }
 
-  /**
-   * Combine all exception lines occuring in the additionalLines list into a 
+    /**
+   * Combine all exception lines occuring in the additionalLines list into a
    * String array
    * <p>
    * (all entries equal to or greater than the exceptionLine index)
-   * 
+   *
    * @param exceptionLine index of first exception line
    * @return exception
    */
@@ -391,19 +428,19 @@
     if (exceptionLine == -1) {
       return emptyException;
     }
-    String[] exception = new String[additionalLines.size() - exceptionLine];
-    for (int i = 0; i < additionalLines.size() - exceptionLine; i++) {
+    String[] exception = new String[additionalLines.size() - exceptionLine - 1];
+    for (int i = 0; i < exception.length; i++) {
       exception[i] = (String) additionalLines.get(i + exceptionLine);
     }
     return exception;
   }
 
-  /**
-   * Construct a logging event from currentMap and additionalLines 
+    /**
+   * Construct a logging event from currentMap and additionalLines
    * (additionalLines contains multiple message lines and any exception lines)
    * <p>
    * CurrentMap and additionalLines are cleared in the process
-   * 
+   *
    * @return event
    */
   private LoggingEvent buildEvent() {
@@ -421,7 +458,7 @@
     String[] exception = buildException(exceptionLine);
 
     //messages are listed before exceptions in additionallines
-    if (additionalLines.size() > 0 && exceptionLine != 0) {
+    if (additionalLines.size() > 0 && exception.length > 0) {
       currentMap.put(MESSAGE, buildMessage((String) currentMap.get(MESSAGE),
           exceptionLine));
     }
@@ -431,17 +468,16 @@
     return event;
   }
 
-  /**
+    /**
    * Read, parse and optionally tail the log file, converting entries into logging events.
-   * 
-   * A runtimeException is thrown if the logFormat pattern is malformed 
+   *
+   * A runtimeException is thrown if the logFormat pattern is malformed
    * according to ORO's Perl5Compiler.
-   * 
-   * @param unbufferedReader
+   *
+   * @param bufferedReader
    * @throws IOException
    */
   protected void process(BufferedReader bufferedReader) throws IOException {
-
         Perl5Matcher eventMatcher = new Perl5Matcher();
         String line;
         while ((line = bufferedReader.readLine()) != null) {
@@ -454,10 +490,28 @@
                     }
                 }
                 currentMap.putAll(processEvent(eventMatcher.getMatch()));
-            } else {
-                //getLogger().debug("line doesn't match pattern - must be ")
-                //may be an exception or additional message lines
+            } else if (eventMatcher.matches(line, exceptionPattern)) {
+                //an exception line
                 additionalLines.add(line);
+            } else {
+                //neither...either post an event with the line or append as additional lines
+                //if this was a logging event with multiple lines, each line will show up as its own event instead of being
+                //appended as multiple lines on the same event..
+                //choice is to have each non-matching line show up as its own line, or append them all to a previous event
+                if (appendNonMatches) {
+                    //build an event from the previous match (held in current map)
+                    if (currentMap.size() > 0) {
+                        LoggingEvent event = buildEvent();
+                        if (event != null) {
+                            if (passesExpression(event)) {
+                              doPost(event);
+                            }
+                        }
+                    }
+                    currentMap.put(MESSAGE, line);
+                } else {
+                    additionalLines.add(line);
+                }
             }
         }
 
@@ -483,11 +537,11 @@
         }
     }
 
-  /**
+    /**
    * Helper method that supports the evaluation of the expression
-   * 
+   *
    * @param event
-   * @return true if expression isn't set, or the result of the evaluation otherwise 
+   * @return true if expression isn't set, or the result of the evaluation otherwise
    */
   private boolean passesExpression(LoggingEvent event) {
     if (event != null) {
@@ -498,12 +552,12 @@
     return true;
   }
 
-  /**
+    /**
    * Convert the ORO match into a map.
    * <p>
    * Relies on the fact that the matchingKeywords list is in the same
    * order as the groups in the regular expression
-   *  
+   *
    * @param result
    * @return map
    */
@@ -515,28 +569,28 @@
     }
     return map;
   }
-  
-  /**
+
+    /**
    * Helper method that will convert timestamp format to a pattern
-   * 
-   * 
+   *
+   *
    * @return string
    */
   private String convertTimestamp() {
     return util.substitute("s/("+VALID_DATEFORMAT_CHAR_PATTERN+")+/\\\\w+/g", timestampFormat);
   }
-  
-  protected void setHost(String host) {
+
+    protected void setHost(String host) {
 	  this.host = host;
   }
-  
-  protected void setPath(String path) {
+
+    protected void setPath(String path) {
 	  this.path = path;
   }
 
-  /**
+    /**
    * Build the regular expression needed to parse log entries
-   *  
+   *
    */
   protected void initialize() {
 	if (host == null && path == null) {
@@ -550,12 +604,12 @@
 		}
 	}
 	if (host == null || host.trim().equals("")) {
-		host = DEFAULT_HOST; 
+		host = DEFAULT_HOST;
 	}
 	if (path == null || path.trim().equals("")) {
 		path = fileURL;
 	}
-	
+
     util = new Perl5Util();
     exceptionCompiler = new Perl5Compiler();
     exceptionMatcher = new Perl5Matcher();
@@ -563,7 +617,7 @@
     currentMap = new HashMap();
     additionalLines = new ArrayList();
     matchingKeywords = new ArrayList();
-    
+
     if (timestampFormat != null) {
       dateFormat = new SimpleDateFormat(timestampFormat);
       timestampPatternText = convertTimestamp();
@@ -582,11 +636,11 @@
     String newPattern = logFormat;
 
     /*
-     * examine pattern, adding properties to an index-based map where the key is the 
+     * examine pattern, adding properties to an index-based map where the key is the
      * numeric offset from the start of the pattern so that order can be preserved
-     * 
-     * Replaces PROP(X) definitions in the pattern with the short version X, so 
-     * that the name can be used as the event property later 
+     *
+     * Replaces PROP(X) definitions in the pattern with the short version X, so
+     * that the name can be used as the event property later
      */
     int index = 0;
     int currentPosition = 0;
@@ -616,7 +670,7 @@
     /*
      * we're using a treemap, so the index will be used as the key to ensure
      * keywords are ordered correctly
-     * 
+     *
      * examine pattern, adding keywords to an index-based map patterns can
      * contain only one of these per entry...properties are the only 'keyword'
      * that can occur multiple times in an entry
@@ -654,13 +708,13 @@
     getLogger().debug("regexp is " + regexp);
   }
 
-  /**
+    /**
    * Helper method that will globally replace a section of text
-   * 
+   *
    * @param pattern
    * @param replacement
-   * @param input 
-   * 
+   * @param input
+   *
    * @return string
    */
   private String replace(String pattern, String replacement, String input) {
@@ -668,10 +722,10 @@
         + Perl5Compiler.quotemeta(replacement) + "/g", input);
   }
 
-  /**
-   * Some perl5 characters may occur in the log file format.  
+    /**
+   * Some perl5 characters may occur in the log file format.
    * Escape these characters to prevent parsing errors.
-   * 
+   *
    * @param input
    * @return string
    */
@@ -687,12 +741,12 @@
     return input;
   }
 
-  /**
+    /**
    * Convert a keyword-to-values map to a LoggingEvent
-   * 
+   *
    * @param fieldMap
    * @param exception
-   * 
+   *
    * @return logging event
    */
   private LoggingEvent convertToEvent(Map fieldMap, String[] exception) {
@@ -782,26 +836,25 @@
     return event;
   }
 
-  /*
-  public static void main(String[] args) {
-    org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
-    org.apache.log4j.ConsoleAppender appender = new org.apache.log4j.ConsoleAppender(new org.apache.log4j.SimpleLayout());
-    appender.setName("console");
-    rootLogger.addAppender(appender);
-    LogFilePatternReceiver test = new LogFilePatternReceiver();
-    org.apache.log4j.spi.LoggerRepository repo = new org.apache.log4j.LoggerRepositoryExImpl(org.apache.log4j.LogManager.getLoggerRepository());
-    test.setLoggerRepository(repo);
-    test.setLogFormat("PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE");
-    test.setTailing(false);
-    test.setTimestampFormat("yyyy-MM-d HH:mm:ss,SSS");
-    test.setFileURL("file:///C:/log/test.log");
-    test.initialize();
-    test.activateOptions();
-  }
-  */
+//  public static void main(String[] args) {
+//    org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
+//    org.apache.log4j.ConsoleAppender appender = new org.apache.log4j.ConsoleAppender(new org.apache.log4j.SimpleLayout());
+//    appender.setName("console");
+//    rootLogger.addAppender(appender);
+//    LogFilePatternReceiver test = new LogFilePatternReceiver();
+//    org.apache.log4j.spi.LoggerRepository repo = new org.apache.log4j.LoggerRepositoryExImpl(org.apache.log4j.LogManager.getLoggerRepository());
+//    test.setLoggerRepository(repo);
+//    test.setLogFormat("PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE");
+//    test.setTailing(false);
+//    test.setAppendNonMatches(true);
+//    test.setTimestampFormat("yyyy-MM-d HH:mm:ss,SSS");
+//    test.setFileURL("file:///C:/log/test.log");
+//    test.initialize();
+//    test.activateOptions();
+//  }
 
-  /**
-   * Close the reader. 
+    /**
+   * Close the reader.
    */
   public void shutdown() {
     try {
@@ -814,50 +867,50 @@
     }
   }
 
-  /**
+    /**
    * Read and process the log file.
    */
   public void activateOptions() {
 	Runnable runnable = new Runnable() {
-	             public void run() {
-                initialize();
-                while (reader == null) {
-                    getLogger().info("attempting to load file: " + getFileURL());
+	  public void run() {
+        initialize();
+            while (reader == null) {
+                getLogger().info("attempting to load file: " + getFileURL());
+                try {
+                    reader = new InputStreamReader(new URL(getFileURL()).openStream());
+                } catch (FileNotFoundException fnfe) {
+                    getLogger().info("file not available - will try again");
+                    synchronized (this) {
+                        try {
+                            wait(MISSING_FILE_RETRY_MILLIS);
+                        } catch (InterruptedException ie) {}
+                    }
+                } catch (IOException ioe) {
+                    getLogger().warn("unable to load file", ioe);
+                    return;
+                }
+            }
+            try {
+                BufferedReader bufferedReader = new BufferedReader(reader);
+                createPattern();
+                do {
+                    getLogger().debug("tailing file: " + tailing);
+                    process(bufferedReader);
                     try {
-                        reader = new InputStreamReader(new URL(getFileURL()).openStream());
-                    } catch (FileNotFoundException fnfe) {
-                        getLogger().info("file not available - will try again in 10 seconds");
                         synchronized (this) {
-                            try {
-                                wait(10000);
-                            } catch (InterruptedException ie) {}
+                            wait(waitMillis);
                         }
-                    } catch (IOException ioe) {
-                        getLogger().warn("unable to load file", ioe);
-                        return;
+                    } catch (InterruptedException ie) {
                     }
-                }
-                try {
-                    BufferedReader bufferedReader = new BufferedReader(reader);
-                    createPattern();
-                    do {
-                        getLogger().debug("tailing file: " + tailing);
-                        process(bufferedReader);
-                        try {
-                            synchronized (this) {
-                                wait(2000);
-                            }
-                        } catch (InterruptedException ie) {
-                        }
-                    } while (tailing);
+                } while (tailing);
 
-                } catch (IOException ioe) {
-                    //io exception - probably shut down
-                    getLogger().info("stream closed");
-                }
-                getLogger().debug("processing " + fileURL + " complete");
-                shutdown();
+            } catch (IOException ioe) {
+                //io exception - probably shut down
+                getLogger().info("stream closed");
             }
+            getLogger().debug("processing " + fileURL + " complete");
+            shutdown();
+          }
         };
         if(useCurrentThread) {
             runnable.run();

Modified: logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java
URL: http://svn.apache.org/viewvc/logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java?rev=774218&r1=774217&r2=774218&view=diff
==============================================================================
--- logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java (original)
+++ logging/log4j/companions/receivers/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java Wed May 13 05:54:49 2009
@@ -40,6 +40,8 @@
         new PropertyDescriptor("tailing", LogFilePatternReceiver.class),
         new PropertyDescriptor(
           "filterExpression", LogFilePatternReceiver.class),
+        new PropertyDescriptor("waitMillis", LogFilePatternReceiver.class),
+        new PropertyDescriptor("appendNonMatches", LogFilePatternReceiver.class),
         new PropertyDescriptor("useCurrentThread", LogFilePatternReceiver.class),
       };
     } catch (Exception e) {



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org