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 }