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.Layout;
21  import org.apache.log4j.LayoutTest;
22  import org.apache.log4j.Level;
23  import org.apache.log4j.Logger;
24  import org.apache.log4j.NDC;
25  import org.apache.log4j.MDC;
26  import org.apache.log4j.PropertyConfigurator;
27  import org.apache.log4j.util.Transformer;
28  import org.apache.log4j.util.Filter;
29  import org.apache.log4j.util.LineNumberFilter;
30  import org.apache.log4j.util.JunitTestRunnerFilter;
31  import org.apache.log4j.util.XMLTimestampFilter;
32  import org.apache.log4j.util.Compare;
33  import org.apache.log4j.util.XMLDateFilter;
34  import org.apache.log4j.helpers.MDCKeySetExtractor;
35  import org.apache.log4j.spi.LoggingEvent;
36  
37  import org.w3c.dom.Document;
38  import org.w3c.dom.Element;
39  import org.w3c.dom.Node;
40  import org.w3c.dom.NodeList;
41  
42  import org.xml.sax.InputSource;
43  
44  import java.io.Reader;
45  import java.io.StringReader;
46  import java.io.InputStream;
47  import java.io.FileNotFoundException;
48  import java.util.Set;
49  import java.util.Iterator;
50  import java.util.Properties;
51  import java.util.Hashtable;
52  
53  import javax.xml.parsers.DocumentBuilder;
54  import javax.xml.parsers.DocumentBuilderFactory;
55  
56  
57  /***
58   * Test for XSLTLayout.
59   *
60   */
61  public class XSLTLayoutTest extends LayoutTest {
62    /***
63     * Construct new instance of XSLTLayoutTest.
64     *
65     * @param testName test name.
66     */
67    public XSLTLayoutTest(final String testName) {
68      super(testName, "text/plain", false, null, null);
69    }
70  
71    /***
72     * @{inheritDoc}
73     */
74    protected Layout createLayout() {
75      return new XSLTLayout();
76    }
77  
78    /***
79     * Parses the string as the body of an XML document and returns the document element.
80     * @param source source string.
81     * @return document element.
82     * @throws Exception if parser can not be constructed or source is not a valid XML document.
83     */
84    private Element parse(final String source) throws Exception {
85      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
86      factory.setNamespaceAware(false);
87      factory.setCoalescing(true);
88  
89      DocumentBuilder builder = factory.newDocumentBuilder();
90      Reader reader = new StringReader(source);
91      Document doc = builder.parse(new InputSource(reader));
92  
93      return doc.getDocumentElement();
94    }
95  
96    /***
97     * Checks a log4j:event element against expectations.
98     * @param element element, may not be null.
99     * @param event event, may not be null.
100    */
101   private void checkEventElement(
102     final Element element, final LoggingEvent event) {
103     assertEquals("log4j:event", element.getTagName());
104     assertEquals(
105       "org.apache.log4j.xml.XSLTLayoutTest", element.getAttribute("logger"));
106     assertEquals(
107       Long.toString(event.timeStamp), element.getAttribute("timestamp"));
108     assertEquals("INFO", element.getAttribute("level"));
109     assertEquals(event.getThreadName(), element.getAttribute("thread"));
110   }
111 
112   /***
113    * Checks a log4j:message element against expectations.
114    * @param element element, may not be null.
115    * @param message expected message.
116    */
117   private void checkMessageElement(
118     final Element element, final String message) {
119     assertEquals("log4j:message", element.getTagName());
120 
121     Node messageNode = element.getFirstChild();
122     assertNotNull(messageNode);
123     assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
124     assertEquals(message, messageNode.getNodeValue());
125     assertNull(messageNode.getNextSibling());
126   }
127 
128   /***
129    * Checks a log4j:message element against expectations.
130    * @param element element, may not be null.
131    * @param message expected message.
132    */
133   private void checkNDCElement(final Element element, final String message) {
134     assertEquals("log4j:NDC", element.getTagName());
135 
136     Node messageNode = element.getFirstChild();
137     assertNotNull(messageNode);
138     assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
139     assertEquals(message, messageNode.getNodeValue());
140     assertNull(messageNode.getNextSibling());
141   }
142 
143   /***
144    * Checks a log4j:throwable element against expectations.
145    * @param element element, may not be null.
146    * @param ex exception, may not be null.
147    */
148   private void checkThrowableElement(
149     final Element element, final Exception ex) {
150     assertEquals("log4j:throwable", element.getTagName());
151 
152     Node messageNode = element.getFirstChild();
153     assertNotNull(messageNode);
154     assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
155 
156     String msg = ex.toString();
157     assertEquals("Got " + messageNode.getNodeValue(), msg, messageNode.getNodeValue().substring(0, msg.length()));
158     assertNull(messageNode.getNextSibling());
159   }
160 
161   /***
162    * Tests formatted results.
163    * @throws Exception if parser can not be constructed or source is not a valid XML document.
164    */
165   public void testFormat() throws Exception {
166     clearMDC();
167     NDC.clear();
168     Logger logger = Logger.getLogger("org.apache.log4j.xml.XSLTLayoutTest");
169     LoggingEvent event =
170       new LoggingEvent(
171         "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
172     XSLTLayout layout = (XSLTLayout) createLayout();
173     String result = layout.format(event);
174     Element parsedResult = parse(result);
175     checkEventElement(parsedResult, event);
176 
177     int childElementCount = 0;
178 
179     for (
180       Node node = parsedResult.getFirstChild(); node != null;
181         node = node.getNextSibling()) {
182       switch (node.getNodeType()) {
183       case Node.ELEMENT_NODE:
184         childElementCount++;
185         checkMessageElement((Element) node, "Hello, World");
186 
187         break;
188 
189       case Node.COMMENT_NODE:
190         break;
191 
192       case Node.TEXT_NODE:
193 
194         //  should only be whitespace
195         break;
196 
197       default:
198         fail("Unexpected node type");
199 
200         break;
201       }
202     }
203 
204     assertEquals(1, childElementCount);
205   }
206 
207   /***
208    * Tests formatted results with an exception.
209    * @throws Exception if parser can not be constructed or source is not a valid XML document.
210    */
211   public void testFormatWithException() throws Exception {
212     clearMDC();
213     NDC.clear();
214     Logger logger = Logger.getLogger("org.apache.log4j.xml.XSLTLayoutTest");
215     Exception ex = new IllegalArgumentException("'foo' is not a valid name");
216     LoggingEvent event =
217       new LoggingEvent(
218         "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", ex);
219     XSLTLayout layout = (XSLTLayout) createLayout();
220     String result = layout.format(event);
221     Element parsedResult = parse(result);
222     checkEventElement(parsedResult, event);
223 
224     int childElementCount = 0;
225 
226     for (
227       Node node = parsedResult.getFirstChild(); node != null;
228         node = node.getNextSibling()) {
229       switch (node.getNodeType()) {
230       case Node.ELEMENT_NODE:
231         childElementCount++;
232 
233         if (childElementCount == 1) {
234           checkMessageElement((Element) node, "Hello, World");
235         } else {
236           checkThrowableElement((Element) node, ex);
237         }
238 
239         break;
240 
241       case Node.COMMENT_NODE:
242         break;
243 
244       case Node.TEXT_NODE:
245 
246         //  should only be whitespace
247         break;
248 
249       default:
250         fail("Unexpected node type");
251 
252         break;
253       }
254     }
255 
256     assertEquals(2, childElementCount);
257   }
258 
259   /***
260    * Tests formatted results with an exception.
261    * @throws Exception if parser can not be constructed or source is not a valid XML document.
262    */
263   public void testFormatWithNDC() throws Exception {
264     clearMDC();
265     NDC.clear();
266     Logger logger = Logger.getLogger("org.apache.log4j.xml.XSLTLayoutTest");
267     NDC.push("NDC goes here");
268 
269     LoggingEvent event =
270       new LoggingEvent(
271         "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
272     XSLTLayout layout = (XSLTLayout) createLayout();
273     String result = layout.format(event);
274     NDC.pop();
275 
276     Element parsedResult = parse(result);
277     checkEventElement(parsedResult, event);
278 
279     int childElementCount = 0;
280 
281     for (
282       Node node = parsedResult.getFirstChild(); node != null;
283         node = node.getNextSibling()) {
284       switch (node.getNodeType()) {
285       case Node.ELEMENT_NODE:
286         childElementCount++;
287 
288         if (childElementCount == 1) {
289           checkMessageElement((Element) node, "Hello, World");
290         } else {
291           checkNDCElement((Element) node, "NDC goes here");
292         }
293 
294         break;
295 
296       case Node.COMMENT_NODE:
297         break;
298 
299       case Node.TEXT_NODE:
300 
301         //  should only be whitespace
302         break;
303 
304       default:
305         fail("Unexpected node type");
306 
307         break;
308       }
309     }
310 
311     assertEquals(2, childElementCount);
312   }
313 
314   /***
315    * Tests getLocationInfo and setLocationInfo.
316    */
317   public void testGetSetLocationInfo() {
318     XSLTLayout layout = new XSLTLayout();
319     assertEquals(false, layout.getLocationInfo());
320     layout.setLocationInfo(true);
321     assertEquals(true, layout.getLocationInfo());
322     layout.setLocationInfo(false);
323     assertEquals(false, layout.getLocationInfo());
324   }
325 
326   /***
327    * Tests activateOptions().
328    */
329   public void testActivateOptions() {
330     XSLTLayout layout = new XSLTLayout();
331     layout.activateOptions();
332   }
333 
334     /***
335      * Tests XML configuration and atom layout.
336      */
337   public void testAtom() throws Exception {
338         InputStream is =
339                 XSLTLayoutTest.class.getResourceAsStream("xsltLayout4.xml");
340         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
341         factory.setNamespaceAware(false);
342         DocumentBuilder builder = factory.newDocumentBuilder();
343         Document doc = builder.parse(is);
344         org.apache.log4j.extras.DOMConfigurator.configure(
345                 doc.getDocumentElement());
346         Logger logger = Logger.getLogger("org.apache.log4j.xml.XSLTLayoutTest");
347         logger.debug("DEBUG message");
348         logger.info("INFO message");
349         Transformer.transform(
350           "temp", "filtered",
351           new Filter[] { new LineNumberFilter(),
352               new JunitTestRunnerFilter(),
353               new XMLTimestampFilter(),
354               new XMLDateFilter()});
355         assertTrue(Compare.compare(XSLTLayoutTest.class,
356                 "filtered", "witness/xml/xsltLayout.4"));
357   }
358 
359     /***
360      * Tests XML configuration and atom layout.
361      */
362   public void testAtomNS() throws Exception {
363         InputStream is =
364                 XSLTLayoutTest.class.getResourceAsStream("xsltLayout4.xml");
365         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
366         factory.setNamespaceAware(true);
367         DocumentBuilder builder = factory.newDocumentBuilder();
368         Document doc = builder.parse(is);
369         org.apache.log4j.extras.DOMConfigurator.configure(
370                 doc.getDocumentElement());
371         Logger logger = Logger.getLogger("org.apache.log4j.xml.XSLTLayoutTest");
372         logger.debug("DEBUG message");
373         logger.info("INFO message");
374         Transformer.transform(
375           "temp", "filtered",
376           new Filter[] { new LineNumberFilter(),
377               new JunitTestRunnerFilter(),
378               new XMLTimestampFilter(),
379               new XMLDateFilter()});
380         assertTrue(Compare.compare(XSLTLayoutTest.class,
381                 "filtered", "witness/xml/xsltLayout.4"));
382   }
383 
384     /***
385       * Tests CDATA element within NDC content.  See bug 37560.
386       */
387     public void testNDCWithCDATA() throws Exception {
388         Logger logger = Logger.getLogger("com.example.bar");
389         Level level = Level.INFO;
390         String ndcMessage ="<envelope><faultstring><![CDATA[The EffectiveDate]]></faultstring><envelope>";
391         NDC.push(ndcMessage);
392         LoggingEvent event =
393           new LoggingEvent(
394             "com.example.bar", logger, level, "Hello, World", null);
395         Layout layout = createLayout();
396         String result = layout.format(event);
397         NDC.clear();
398         Element parsedResult = parse(result);
399         NodeList ndcs = parsedResult.getElementsByTagName("log4j:NDC");
400         assertEquals(1, ndcs.getLength());
401         StringBuffer buf = new StringBuffer();
402         for(Node child = ndcs.item(0).getFirstChild();
403                 child != null;
404                 child = child.getNextSibling()) {
405             buf.append(child.getNodeValue());
406         }
407         assertEquals(ndcMessage, buf.toString());
408 
409     }
410 
411     /***
412       * Tests CDATA element within exception.  See bug 37560.
413       */
414     public void testExceptionWithCDATA() throws Exception {
415         Logger logger = Logger.getLogger("com.example.bar");
416         Level level = Level.INFO;
417         String exceptionMessage ="<envelope><faultstring><![CDATA[The EffectiveDate]]></faultstring><envelope>";
418         LoggingEvent event =
419           new LoggingEvent(
420             "com.example.bar", logger, level, "Hello, World", new Exception(exceptionMessage));
421         Layout layout = createLayout();
422         String result = layout.format(event);
423         Element parsedResult = parse(result);
424         NodeList throwables = parsedResult.getElementsByTagName("log4j:throwable");
425         assertEquals(1, throwables.getLength());
426         StringBuffer buf = new StringBuffer();
427         for(Node child = throwables.item(0).getFirstChild();
428                 child != null;
429                 child = child.getNextSibling()) {
430             buf.append(child.getNodeValue());
431         }
432         assertTrue(buf.toString().indexOf(exceptionMessage) != -1);
433    }
434 
435 
436   private static void clearMDC() {
437       Hashtable context = MDC.getContext();
438       if (context != null) {
439           context.clear();
440       }
441   }
442 }