View Javadoc

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