View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.log4j.xml;
    19
    20  import java.io.BufferedReader;
    21  import java.io.FileNotFoundException;
    22  import java.io.IOException;
    23  import java.io.InputStreamReader;
    24  import java.io.Reader;
    25  import java.net.MalformedURLException;
    26  import java.net.URL;
    27  import java.util.Collection;
    28  import java.util.Iterator;
    29
    30  import org.apache.log4j.helpers.Constants;
    31  import org.apache.log4j.plugins.Receiver;
    32  import org.apache.log4j.rule.ExpressionRule;
    33  import org.apache.log4j.rule.Rule;
    34  import org.apache.log4j.spi.Decoder;
    35  import org.apache.log4j.spi.LoggingEvent;
    36
    37  /**
    38   * LogFileXMLReceiver will read an xml-formated log file and make the events in the log file
    39   * available to the log4j framework.
    40   * <p>
    41   * This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging
    42   * XMLFormatter (via the org.apache.log4j.spi.Decoder interface).
    43   * <p>
    44   * By default, log4j's XMLLayout is supported (no need to specify a decoder in that case).
    45   * <p>
    46   * To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param
    47   * of org.apache.log4j.xml.UtilLoggingXMLDecoder.
    48   * <p>
    49   * Tailing -may- work, but not in all cases (try using a file:// URL). If a process has a log file
    50   * open, the receiver may be able to read and tail the file. If the process closes the file and
    51   * reopens the file, the receiver may not be able to continue tailing the file.
    52   * <p>
    53   * An expressionFilter may be specified. Only events passing the expression will be forwarded to the
    54   * log4j framework.
    55   * <p>
    56   * Once the event has been "posted", it will be handled by the appenders currently configured in the
    57   * LoggerRespository.
    58   *
    59   * @author Scott Deboy &lt;sdeboy@apache.org&gt;
    60   * @since 1.3
    61   */
    62
    63  public class LogFileXMLReceiver extends Receiver {
    64      private String fileURL;
    65      private Rule expressionRule;
    66      private String filterExpression;
    67      private String decoder = "org.apache.log4j.xml.XMLDecoder";
    68      private boolean tailing = false;
    69
    70      private Decoder decoderInstance;
    71      private Reader reader;
    72      private static final String FILE_KEY = "file";
    73      private String host;
    74      private String path;
    75      private boolean useCurrentThread;
    76
    77      /**
    78       * Accessor
    79       *
    80       * @return file URL
    81       */
    82      public String getFileURL() {
    83          return fileURL;
    84      }
    85
    86      /**
    87       * Specify the URL of the XML-formatted file to process.
    88       *
    89       * @param fileURL
    90       */
    91      public void setFileURL(String fileURL) {
    92          this.fileURL = fileURL;
    93      }
    94
    95      /**
    96       * Accessor
    97       *
    98       * @return
    99       */
    100     public String getDecoder() {
    101         return decoder;
    102     }
    103
    104     /**
    105      * Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file.
    106      *
    107      * @param _decoder
    108      */
    109     public void setDecoder(String _decoder) {
    110         decoder = _decoder;
    111     }
    112
    113     /**
    114      * Accessor
    115      *
    116      * @return filter expression
    117      */
    118     public String getFilterExpression() {
    119         return filterExpression;
    120     }
    121
    122     /**
    123      * Accessor
    124      *
    125      * @return tailing flag
    126      */
    127     public boolean isTailing() {
    128         return tailing;
    129     }
    130
    131     /**
    132      * Set the 'tailing' flag - may only work on file:// URLs and may stop tailing if the writing
    133      * process closes the file and reopens.
    134      *
    135      * @param tailing
    136      */
    137     public void setTailing(boolean tailing) {
    138         this.tailing = tailing;
    139     }
    140
    141     /**
    142      * Set the filter expression that will cause only events which pass the filter to be forwarded
    143      * to the log4j framework.
    144      *
    145      * @param filterExpression
    146      */
    147     public void setFilterExpression(String filterExpression) {
    148         this.filterExpression = filterExpression;
    149     }
    150
    151     private boolean passesExpression(LoggingEvent event) {
    152         if (event != null) {
    153             if (expressionRule != null) {
    154                 return (expressionRule.evaluate(event, null));
    155             }
    156         }
    157         return true;
    158     }
    159
    160     public static void main(String[] args) {
    161         /*
    162          * LogFileXMLReceiver test = new LogFileXMLReceiver();
    163          * test.setFileURL("file:///c:/samplelog.xml"); test.setFilterExpression("level >= TRACE");
    164          * test.activateOptions();
    165          */
    166     }
    167
    168     /**
    169      * Close the receiver, release any resources that are accessing the file.
    170      */
    171     public void shutdown() {
    172         try {
    173             if (reader != null) {
    174                 reader.close();
    175                 reader = null;
    176             }
    177         } catch (IOException ioe) {
    178             ioe.printStackTrace();
    179         }
    180     }
    181
    182     /**
    183      * Process the file
    184      */
    185     public void activateOptions() {
    186         Runnable runnable = new Runnable() {
    187             public void run() {
    188                 try {
    189                     URL url = new URL(fileURL);
    190                     host = url.getHost();
    191                     if (host != null && host.equals("")) {
    192                         host = FILE_KEY;
    193                     }
    194                     path = url.getPath();
    195                 } catch (MalformedURLException e1) {
    196                     // TODO Auto-generated catch block
    197                     e1.printStackTrace();
    198                 }
    199
    200                 try {
    201                     if (filterExpression != null) {
    202                         expressionRule = ExpressionRule.getRule(filterExpression);
    203                     }
    204                 } catch (Exception e) {
    205                     getLogger().warn("Invalid filter expression: " + filterExpression, e);
    206                 }
    207
    208                 Class c;
    209                 try {
    210                     c = Class.forName(decoder);
    211                     Object o = c.newInstance();
    212                     if (o instanceof Decoder) {
    213                         decoderInstance = (Decoder) o;
    214                     }
    215                 } catch (ClassNotFoundException e) {
    216                     // TODO Auto-generated catch block
    217                     e.printStackTrace();
    218                 } catch (InstantiationException e) {
    219                     // TODO Auto-generated catch block
    220                     e.printStackTrace();
    221                 } catch (IllegalAccessException e) {
    222                     // TODO Auto-generated catch block
    223                     e.printStackTrace();
    224                 }
    225
    226                 try {
    227                     reader = new InputStreamReader(new URL(getFileURL()).openStream());
    228                     process(reader);
    229                 } catch (FileNotFoundException fnfe) {
    230                     getLogger().info("file not available");
    231                 } catch (IOException ioe) {
    232                     getLogger().warn("unable to load file", ioe);
    233                     return;
    234                 }
    235             }
    236         };
    237         if (useCurrentThread) {
    238             runnable.run();
    239         } else {
    240             Thread thread = new Thread(runnable, "LogFileXMLReceiver-" + getName());
    241
    242             thread.start();
    243
    244         }
    245     }
    246
    247     private void process(Reader unbufferedReader) throws IOException {
    248         BufferedReader bufferedReader = new BufferedReader(unbufferedReader);
    249         char[] content = new char[10000];
    250         getLogger().debug("processing starting: " + fileURL);
    251         int length = 0;
    252         do {
    253             System.out.println("in do loop-about to process");
    254             while ((length = bufferedReader.read(content)) > -1) {
    255                 processEvents(decoderInstance.decodeEvents(String.valueOf(content, 0, length)));
    256             }
    257             if (tailing) {
    258                 try {
    259                     Thread.sleep(5000);
    260                 } catch (InterruptedException e) {
    261                     // TODO Auto-generated catch block
    262                     e.printStackTrace();
    263                 }
    264             }
    265         } while (tailing);
    266         getLogger().debug("processing complete: " + fileURL);
    267
    268         shutdown();
    269     }
    270
    271     private void processEvents(Collection c) {
    272         if (c == null) {
    273             return;
    274         }
    275
    276         for (Iterator iter = c.iterator(); iter.hasNext();) {
    277             LoggingEvent evt = (LoggingEvent) iter.next();
    278             if (passesExpression(evt)) {
    279                 if (evt.getProperty(Constants.HOSTNAME_KEY) != null) {
    280                     evt.setProperty(Constants.HOSTNAME_KEY, host);
    281                 }
    282                 if (evt.getProperty(Constants.APPLICATION_KEY) != null) {
    283                     evt.setProperty(Constants.APPLICATION_KEY, path);
    284                 }
    285                 doPost(evt);
    286             }
    287         }
    288     }
    289
    290     /**
    291      * When true, this property uses the current Thread to perform the import, otherwise when false
    292      * (the default), a new Thread is created and started to manage the import.
    293      *
    294      * @return
    295      */
    296     public final boolean isUseCurrentThread() {
    297         return useCurrentThread;
    298     }
    299
    300     /**
    301      * Sets whether the current Thread or a new Thread is created to perform the import, the default
    302      * being false (new Thread created).
    303      *
    304      * @param useCurrentThread
    305      */
    306     public final void setUseCurrentThread(boolean useCurrentThread) {
    307         this.useCurrentThread = useCurrentThread;
    308     }
    309
    310 }