1
2
3
4
5
6
7
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
155
156
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
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
209 e.printStackTrace();
210 } catch (InstantiationException e) {
211
212 e.printStackTrace();
213 } catch (IllegalAccessException e) {
214
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
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 }