1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.xml;
19
20 import java.awt.Component;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.LineNumberReader;
24 import java.io.StringReader;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Hashtable;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.Vector;
32
33 import javax.swing.ProgressMonitorInputStream;
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
37
38 import org.apache.log4j.Level;
39 import org.apache.log4j.Logger;
40 import org.apache.log4j.helpers.UtilLoggingLevel;
41 import org.apache.log4j.spi.Decoder;
42 import org.apache.log4j.spi.LoggingEvent;
43 import org.apache.log4j.spi.ThrowableInformation;
44 import org.apache.log4j.spi.LocationInfo;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Node;
47 import org.w3c.dom.NodeList;
48 import org.xml.sax.InputSource;
49
50
51 /***
52 * Decodes JDK 1.4's java.util.logging package events
53 * delivered via XML (using the logger.dtd).
54 *
55 * @author Scott Deboy (sdeboy@apache.org)
56 * @author Paul Smith (psmith@apache.org)
57 *
58 */
59 public class UtilLoggingXMLDecoder implements Decoder {
60
61
62 /***
63 * Document prolog.
64 */
65 private static final String BEGIN_PART =
66 "<log>";
67 /***
68 * Document close.
69 */
70 private static final String END_PART = "</log>";
71 /***
72 * Document builder.
73 */
74 private DocumentBuilder docBuilder;
75 /***
76 * Additional properties.
77 */
78 private Map additionalProperties = new HashMap();
79 /***
80 * Partial event.
81 */
82 private String partialEvent;
83 /***
84 * Record end.
85 */
86 private static final String RECORD_END = "</record>";
87 /***
88 * Owner.
89 */
90 private Component owner = null;
91
92 /***
93 * Create new instance.
94 * @param o owner
95 */
96 public UtilLoggingXMLDecoder(final Component o) {
97 this();
98 this.owner = o;
99 }
100
101 /***
102 * Create new instance.
103 */
104 public UtilLoggingXMLDecoder() {
105 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
106 dbf.setValidating(false);
107
108 try {
109 docBuilder = dbf.newDocumentBuilder();
110 docBuilder.setErrorHandler(new SAXErrorHandler());
111 docBuilder.setEntityResolver(new UtilLoggingEntityResolver());
112 } catch (ParserConfigurationException pce) {
113 System.err.println("Unable to get document builder");
114 }
115 }
116
117 /***
118 * Sets an additionalProperty map, where each Key/Value pair is
119 * automatically added to each LoggingEvent as it is decoded.
120 *
121 * This is useful, say, to include the source file name of the Logging events
122 * @param properties additional properties
123 */
124 public void setAdditionalProperties(final Map properties) {
125 this.additionalProperties.putAll(properties);
126 }
127
128 /***
129 * Converts the LoggingEvent data in XML string format into an actual
130 * XML Document class instance.
131 * @param data XML fragment
132 * @return XML document
133 */
134 private Document parse(final String data) {
135 if (docBuilder == null || data == null) {
136 return null;
137 }
138
139 Document document = null;
140
141 try {
142
143
144
145
146
147 /***
148 * resetting the length of the StringBuffer is dangerous, particularly
149 * on some JDK 1.4 impls, there's a known Bug that causes a memory leak
150 */
151 StringBuffer buf = new StringBuffer(1024);
152
153 if (!data.startsWith("<?xml")) {
154 buf.append(BEGIN_PART);
155 }
156
157 buf.append(data);
158
159 if (!data.endsWith(END_PART)) {
160 buf.append(END_PART);
161 }
162
163 InputSource inputSource =
164 new InputSource(new StringReader(buf.toString()));
165 document = docBuilder.parse(inputSource);
166 } catch (Exception e) {
167 e.printStackTrace();
168 }
169
170 return document;
171 }
172
173 /***
174 * Decodes a File into a Vector of LoggingEvents.
175 * @param url the url of a file containing events to decode
176 * @return Vector of LoggingEvents
177 * @throws IOException if IO error during processing.
178 */
179 public Vector decode(final URL url) throws IOException {
180 LineNumberReader reader = null;
181 if (owner != null) {
182 reader = new LineNumberReader(
183 new InputStreamReader(
184 new ProgressMonitorInputStream(owner,
185 "Loading " + url , url.openStream())));
186 } else {
187 reader = new LineNumberReader(new InputStreamReader(url.openStream()));
188 }
189 Vector v = new Vector();
190
191 String line = null;
192 try {
193 while ((line = reader.readLine()) != null) {
194 StringBuffer buffer = new StringBuffer(line);
195 for (int i = 0; i < 100; i++) {
196 buffer.append(reader.readLine());
197 }
198 v.addAll(decodeEvents(buffer.toString()));
199 }
200 } finally {
201 partialEvent = null;
202 try {
203 if (reader != null) {
204 reader.close();
205 }
206 } catch (Exception e) {
207 e.printStackTrace();
208 }
209 }
210 return v;
211 }
212
213 /***
214 * Decodes a String representing a number of events into a
215 * Vector of LoggingEvents.
216 * @param document to decode events from
217 * @return Vector of LoggingEvents
218 */
219 public Vector decodeEvents(final String document) {
220
221 if (document != null) {
222
223 if (document.equals("")) {
224 return null;
225 }
226
227 String newDoc;
228 String newPartialEvent = null;
229
230
231
232
233
234
235 if (document.lastIndexOf(RECORD_END) == -1) {
236 partialEvent = partialEvent + document;
237 return null;
238 }
239
240 if (document.lastIndexOf(RECORD_END) + RECORD_END.length()
241 < document.length()) {
242 newDoc = document.substring(0,
243 document.lastIndexOf(RECORD_END) + RECORD_END.length());
244 newPartialEvent = document.substring(
245 document.lastIndexOf(RECORD_END) + RECORD_END.length());
246 } else {
247 newDoc = document;
248 }
249 if (partialEvent != null) {
250 newDoc = partialEvent + newDoc;
251 }
252 partialEvent = newPartialEvent;
253
254 Document doc = parse(newDoc);
255 if (doc == null) {
256 return null;
257 }
258 return decodeEvents(doc);
259 }
260 return null;
261 }
262
263 /***
264 * Converts the string data into an XML Document, and then soaks out the
265 * relevant bits to form a new LoggingEvent instance which can be used
266 * by any Log4j element locally.
267 * @param data XML fragment
268 * @return a single LoggingEvent or null
269 */
270 public LoggingEvent decode(final String data) {
271 Document document = parse(data);
272
273 if (document == null) {
274 return null;
275 }
276
277 Vector events = decodeEvents(document);
278
279 if (events.size() > 0) {
280 return (LoggingEvent) events.firstElement();
281 }
282
283 return null;
284 }
285
286 /***
287 * Given a Document, converts the XML into a Vector of LoggingEvents.
288 * @param document XML document
289 * @return vector of logging events
290 */
291 private Vector decodeEvents(final Document document) {
292 Vector events = new Vector();
293
294 NodeList eventList = document.getElementsByTagName("record");
295
296 for (int eventIndex = 0; eventIndex < eventList.getLength();
297 eventIndex++) {
298 Node eventNode = eventList.item(eventIndex);
299
300 Logger logger = null;
301 long timeStamp = 0L;
302 Level level = null;
303 String threadName = null;
304 Object message = null;
305 String ndc = null;
306 String[] exception = null;
307 String className = null;
308 String methodName = null;
309 String fileName = null;
310 String lineNumber = null;
311 Hashtable properties = new Hashtable();
312
313
314
315 NodeList list = eventNode.getChildNodes();
316 int listLength = list.getLength();
317
318 if (listLength == 0) {
319 continue;
320 }
321
322 for (int y = 0; y < listLength; y++) {
323 String tagName = list.item(y).getNodeName();
324
325 if (tagName.equalsIgnoreCase("logger")) {
326 logger = Logger.getLogger(getCData(list.item(y)));
327 }
328
329 if (tagName.equalsIgnoreCase("millis")) {
330 timeStamp = Long.parseLong(getCData(list.item(y)));
331 }
332
333 if (tagName.equalsIgnoreCase("level")) {
334 level = UtilLoggingLevel.toLevel(getCData(list.item(y)));
335 }
336
337 if (tagName.equalsIgnoreCase("thread")) {
338 threadName = getCData(list.item(y));
339 }
340
341 if (tagName.equalsIgnoreCase("sequence")) {
342 properties.put("log4jid", getCData(list.item(y)));
343 }
344
345 if (tagName.equalsIgnoreCase("message")) {
346 message = getCData(list.item(y));
347 }
348
349 if (tagName.equalsIgnoreCase("class")) {
350 className = getCData(list.item(y));
351 }
352
353 if (tagName.equalsIgnoreCase("method")) {
354 methodName = getCData(list.item(y));
355 }
356
357 if (tagName.equalsIgnoreCase("exception")) {
358 ArrayList exceptionList = new ArrayList();
359 NodeList exList = list.item(y).getChildNodes();
360 int exlistLength = exList.getLength();
361
362 for (int i2 = 0; i2 < exlistLength; i2++) {
363 Node exNode = exList.item(i2);
364 String exName = exList.item(i2).getNodeName();
365
366 if (exName.equalsIgnoreCase("message")) {
367 exceptionList.add(getCData(exList.item(i2)));
368 }
369
370 if (exName.equalsIgnoreCase("frame")) {
371 NodeList exList2 = exNode.getChildNodes();
372 int exlist2Length = exList2.getLength();
373
374 for (int i3 = 0; i3 < exlist2Length; i3++) {
375 exceptionList.add(getCData(exList2.item(i3)) + "\n");
376 }
377 }
378 }
379
380 exception =
381 (String[]) exceptionList.toArray(new String[exceptionList.size()]);
382 }
383 }
384
385 /***
386 * We add all the additional properties to the properties
387 * hashtable
388 */
389 if (additionalProperties.size() > 0) {
390 if (properties == null) {
391 properties = new Hashtable(additionalProperties);
392 } else {
393 Iterator i = additionalProperties.entrySet().iterator();
394 while (i.hasNext()) {
395 Map.Entry e = (Map.Entry) i.next();
396 if (!(properties.containsKey(e.getKey()))) {
397 properties.put(e.getKey(), e.getValue());
398 }
399 }
400 }
401 }
402
403 LocationInfo info = null;
404 if ((fileName != null)
405 || (className != null)
406 || (methodName != null)
407 || (lineNumber != null)) {
408 info = new LocationInfo(fileName, className, methodName, lineNumber);
409 } else {
410 info = LocationInfo.NA_LOCATION_INFO;
411 }
412
413 if (exception == null) {
414 exception = new String[]{""};
415 }
416
417 LoggingEvent loggingEvent = new LoggingEvent(null,
418 logger, timeStamp, level, message,
419 threadName,
420 new ThrowableInformation(exception),
421 ndc,
422 info,
423 properties);
424
425 events.add(loggingEvent);
426
427 }
428 return events;
429 }
430
431 /***
432 * Get contents of CDATASection.
433 * @param n CDATASection
434 * @return text content of all text or CDATA children of node.
435 */
436 private String getCData(final Node n) {
437 StringBuffer buf = new StringBuffer();
438 NodeList nl = n.getChildNodes();
439
440 for (int x = 0; x < nl.getLength(); x++) {
441 Node innerNode = nl.item(x);
442
443 if (
444 (innerNode.getNodeType() == Node.TEXT_NODE)
445 || (innerNode.getNodeType() == Node.CDATA_SECTION_NODE)) {
446 buf.append(innerNode.getNodeValue());
447 }
448 }
449
450 return buf.toString();
451 }
452 }