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