blob: a21c0222cb953724f14e3fc6efae1a9e7a253a44 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j.xml;
import org.apache.log4j.Layout;
import org.apache.log4j.LayoutTest;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LoggingEvent;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.io.Reader;
import java.io.StringReader;
import java.util.Hashtable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
/**
* Test for XMLLayout.
*
* @author Curt Arnold
*/
public class XMLLayoutTest extends LayoutTest {
/**
* Construct new instance of XMLLayoutTest.
*
* @param testName test name.
*/
public XMLLayoutTest(final String testName) {
super(testName, "text/plain", false, null, null);
}
/**
* Clear MDC and NDC before test.
*/
public void setUp() {
NDC.clear();
if (MDC.getContext() != null) {
MDC.getContext().clear();
}
}
/**
* Clear MDC and NDC after test.
*/
public void tearDown() {
setUp();
}
/**
* @{inheritDoc}
*/
protected Layout createLayout() {
return new XMLLayout();
}
/**
* Parses the string as the body of an XML document and returns the document element.
* @param source source string.
* @return document element.
* @throws Exception if parser can not be constructed or source is not a valid XML document.
*/
private Element parse(final String source) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
factory.setCoalescing(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Reader reader = new StringReader(source);
Document doc = builder.parse(new InputSource(reader));
return doc.getDocumentElement();
}
/**
* Checks a log4j:event element against expectations.
* @param element element, may not be null.
* @param event event, may not be null.
*/
private void checkEventElement(
final Element element, final LoggingEvent event) {
assertEquals("log4j:event", element.getTagName());
assertEquals(
event.getLoggerName(), element.getAttribute("logger"));
assertEquals(
Long.toString(event.timeStamp), element.getAttribute("timestamp"));
assertEquals(event.getLevel().toString(), element.getAttribute("level"));
assertEquals(event.getThreadName(), element.getAttribute("thread"));
}
/**
* Checks a log4j:message element against expectations.
* @param element element, may not be null.
* @param message expected message.
*/
private void checkMessageElement(
final Element element, final String message) {
assertEquals("log4j:message", element.getTagName());
Node messageNode = element.getFirstChild();
assertNotNull(messageNode);
assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
assertEquals(message, messageNode.getNodeValue());
assertNull(messageNode.getNextSibling());
}
/**
* Checks a log4j:message element against expectations.
* @param element element, may not be null.
* @param message expected message.
*/
private void checkNDCElement(final Element element, final String message) {
assertEquals("log4j:NDC", element.getTagName());
Node messageNode = element.getFirstChild();
assertNotNull(messageNode);
assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
assertEquals(message, messageNode.getNodeValue());
assertNull(messageNode.getNextSibling());
}
/**
* Checks a log4j:throwable element against expectations.
* @param element element, may not be null.
* @param ex exception, may not be null.
*/
private void checkThrowableElement(
final Element element, final Exception ex) {
assertEquals("log4j:throwable", element.getTagName());
Node messageNode = element.getFirstChild();
assertNotNull(messageNode);
assertEquals(Node.TEXT_NODE, messageNode.getNodeType());
String msg = ex.toString();
assertEquals(msg, messageNode.getNodeValue().substring(0, msg.length()));
assertNull(messageNode.getNextSibling());
}
/**
* Checks a log4j:properties element against expectations.
* @param element element, may not be null.
* @param key key.
* @param value value.
*/
private void checkPropertiesElement(
final Element element, final String key, final String value) {
assertEquals("log4j:properties", element.getTagName());
int childNodeCount = 0;
for(Node child = element.getFirstChild();
child != null;
child = child.getNextSibling()) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
assertEquals("log4j:data", child.getNodeName());
Element childElement = (Element) child;
assertEquals(key, childElement.getAttribute("name"));
assertEquals(value, childElement.getAttribute("value"));
childNodeCount++;
}
}
assertEquals(1, childNodeCount);
}
/**
* Tests formatted results.
* @throws Exception if parser can not be constructed or source is not a valid XML document.
*/
public void testFormat() throws Exception {
Logger logger = Logger.getLogger("org.apache.log4j.xml.XMLLayoutTest");
LoggingEvent event =
new LoggingEvent(
"org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
XMLLayout layout = (XMLLayout) createLayout();
String result = layout.format(event);
Element parsedResult = parse(result);
checkEventElement(parsedResult, event);
int childElementCount = 0;
for (
Node node = parsedResult.getFirstChild(); node != null;
node = node.getNextSibling()) {
switch (node.getNodeType()) {
case Node.ELEMENT_NODE:
childElementCount++;
checkMessageElement((Element) node, "Hello, World");
break;
case Node.COMMENT_NODE:
break;
case Node.TEXT_NODE:
// should only be whitespace
break;
default:
fail("Unexpected node type");
break;
}
}
assertEquals(1, childElementCount);
}
/**
* Tests formatted results with an exception.
* @throws Exception if parser can not be constructed or source is not a valid XML document.
*/
public void testFormatWithException() throws Exception {
Logger logger = Logger.getLogger("org.apache.log4j.xml.XMLLayoutTest");
Exception ex = new IllegalArgumentException("'foo' is not a valid name");
LoggingEvent event =
new LoggingEvent(
"org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", ex);
XMLLayout layout = (XMLLayout) createLayout();
String result = layout.format(event);
Element parsedResult = parse(result);
checkEventElement(parsedResult, event);
int childElementCount = 0;
for (
Node node = parsedResult.getFirstChild(); node != null;
node = node.getNextSibling()) {
switch (node.getNodeType()) {
case Node.ELEMENT_NODE:
childElementCount++;
if (childElementCount == 1) {
checkMessageElement((Element) node, "Hello, World");
} else {
checkThrowableElement((Element) node, ex);
}
break;
case Node.COMMENT_NODE:
break;
case Node.TEXT_NODE:
// should only be whitespace
break;
default:
fail("Unexpected node type");
break;
}
}
assertEquals(2, childElementCount);
}
/**
* Tests formatted results with an exception.
* @throws Exception if parser can not be constructed or source is not a valid XML document.
*/
public void testFormatWithNDC() throws Exception {
Logger logger = Logger.getLogger("org.apache.log4j.xml.XMLLayoutTest");
NDC.push("NDC goes here");
LoggingEvent event =
new LoggingEvent(
"org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
XMLLayout layout = (XMLLayout) createLayout();
String result = layout.format(event);
NDC.pop();
Element parsedResult = parse(result);
checkEventElement(parsedResult, event);
int childElementCount = 0;
for (
Node node = parsedResult.getFirstChild(); node != null;
node = node.getNextSibling()) {
switch (node.getNodeType()) {
case Node.ELEMENT_NODE:
childElementCount++;
if (childElementCount == 1) {
checkMessageElement((Element) node, "Hello, World");
} else {
checkNDCElement((Element) node, "NDC goes here");
}
break;
case Node.COMMENT_NODE:
break;
case Node.TEXT_NODE:
// should only be whitespace
break;
default:
fail("Unexpected node type");
break;
}
}
assertEquals(2, childElementCount);
}
/**
* Tests getLocationInfo and setLocationInfo.
*/
public void testGetSetLocationInfo() {
XMLLayout layout = new XMLLayout();
assertEquals(false, layout.getLocationInfo());
layout.setLocationInfo(true);
assertEquals(true, layout.getLocationInfo());
layout.setLocationInfo(false);
assertEquals(false, layout.getLocationInfo());
}
/**
* Tests activateOptions().
*/
public void testActivateOptions() {
XMLLayout layout = new XMLLayout();
layout.activateOptions();
}
/**
* Level with arbitrary toString value.
*/
private static final class ProblemLevel extends Level {
private static final long serialVersionUID = 1L;
/**
* Construct new instance.
* @param levelName level name, may not be null.
*/
public ProblemLevel(final String levelName) {
super(6000, levelName, 6);
}
}
/**
* Tests problematic characters in multiple fields.
* @throws Exception if parser can not be constructed or source is not a valid XML document.
*/
public void testProblemCharacters() throws Exception {
String problemName = "com.example.bar<>&\"'";
Logger logger = Logger.getLogger(problemName);
Level level = new ProblemLevel(problemName);
Exception ex = new IllegalArgumentException(problemName);
String threadName = Thread.currentThread().getName();
Thread.currentThread().setName(problemName);
NDC.push(problemName);
Hashtable mdcMap = MDC.getContext();
if (mdcMap != null) {
mdcMap.clear();
}
MDC.put(problemName, problemName);
LoggingEvent event =
new LoggingEvent(
problemName, logger, level, problemName, ex);
XMLLayout layout = (XMLLayout) createLayout();
layout.setProperties(true);
String result = layout.format(event);
mdcMap = MDC.getContext();
if (mdcMap != null) {
mdcMap.clear();
}
Thread.currentThread().setName(threadName);
Element parsedResult = parse(result);
checkEventElement(parsedResult, event);
int childElementCount = 0;
for (
Node node = parsedResult.getFirstChild(); node != null;
node = node.getNextSibling()) {
switch (node.getNodeType()) {
case Node.ELEMENT_NODE:
childElementCount++;
switch(childElementCount) {
case 1:
checkMessageElement((Element) node, problemName);
break;
case 2:
checkNDCElement((Element) node, problemName);
break;
case 3:
checkThrowableElement((Element) node, ex);
break;
case 4:
checkPropertiesElement((Element) node, problemName, problemName);
break;
default:
fail("Unexpected element");
break;
}
break;
case Node.COMMENT_NODE:
break;
case Node.TEXT_NODE:
// should only be whitespace
break;
default:
fail("Unexpected node type");
break;
}
}
}
/**
* Tests CDATA element within NDC content. See bug 37560.
*/
public void testNDCWithCDATA() throws Exception {
Logger logger = Logger.getLogger("com.example.bar");
Level level = Level.INFO;
String ndcMessage ="<envelope><faultstring><![CDATA[The EffectiveDate]]></faultstring><envelope>";
NDC.push(ndcMessage);
LoggingEvent event =
new LoggingEvent(
"com.example.bar", logger, level, "Hello, World", null);
Layout layout = createLayout();
String result = layout.format(event);
NDC.clear();
Element parsedResult = parse(result);
NodeList ndcs = parsedResult.getElementsByTagName("log4j:NDC");
assertEquals(1, ndcs.getLength());
StringBuffer buf = new StringBuffer();
for(Node child = ndcs.item(0).getFirstChild();
child != null;
child = child.getNextSibling()) {
buf.append(child.getNodeValue());
}
assertEquals(ndcMessage, buf.toString());
}
/**
* Tests CDATA element within exception. See bug 37560.
*/
public void testExceptionWithCDATA() throws Exception {
Logger logger = Logger.getLogger("com.example.bar");
Level level = Level.INFO;
String exceptionMessage ="<envelope><faultstring><![CDATA[The EffectiveDate]]></faultstring><envelope>";
LoggingEvent event =
new LoggingEvent(
"com.example.bar", logger, level, "Hello, World", new Exception(exceptionMessage));
Layout layout = createLayout();
String result = layout.format(event);
Element parsedResult = parse(result);
NodeList throwables = parsedResult.getElementsByTagName("log4j:throwable");
assertEquals(1, throwables.getLength());
StringBuffer buf = new StringBuffer();
for(Node child = throwables.item(0).getFirstChild();
child != null;
child = child.getNextSibling()) {
buf.append(child.getNodeValue());
}
assertTrue(buf.toString().indexOf(exceptionMessage) != -1);
}
}