/** * 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.hadoop.log; import junit.framework.TestCase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Appender; import org.apache.log4j.Category; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.WriterAppender; import org.apache.log4j.spi.HierarchyEventListener; import org.apache.log4j.spi.LoggerFactory; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.ThrowableInformation; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.MappingJsonFactory; import org.codehaus.jackson.node.ContainerNode; import org.junit.Test; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.net.NoRouteToHostException; import java.util.Enumeration; import java.util.Vector; public class TestLog4Json extends TestCase { private static final Log LOG = LogFactory.getLog(TestLog4Json.class); private static final JsonFactory factory = new MappingJsonFactory(); @Test public void testConstruction() throws Throwable { Log4Json l4j = new Log4Json(); String outcome = l4j.toJson(new StringWriter(), "name", 0, "DEBUG", "thread1", "hello, world", null).toString(); println("testConstruction", outcome); } @Test public void testException() throws Throwable { Exception e = new NoRouteToHostException("that box caught fire 3 years ago"); ThrowableInformation ti = new ThrowableInformation(e); Log4Json l4j = new Log4Json(); long timeStamp = System.currentTimeMillis(); String outcome = l4j.toJson(new StringWriter(), "testException", timeStamp, "INFO", "quoted\"", "new line\n and {}", ti) .toString(); println("testException", outcome); } @Test public void testNestedException() throws Throwable { Exception e = new NoRouteToHostException("that box caught fire 3 years ago"); Exception ioe = new IOException("Datacenter problems", e); ThrowableInformation ti = new ThrowableInformation(ioe); Log4Json l4j = new Log4Json(); long timeStamp = System.currentTimeMillis(); String outcome = l4j.toJson(new StringWriter(), "testNestedException", timeStamp, "INFO", "quoted\"", "new line\n and {}", ti) .toString(); println("testNestedException", outcome); ContainerNode rootNode = Log4Json.parse(outcome); assertEntryEquals(rootNode, Log4Json.LEVEL, "INFO"); assertEntryEquals(rootNode, Log4Json.NAME, "testNestedException"); assertEntryEquals(rootNode, Log4Json.TIME, timeStamp); assertEntryEquals(rootNode, Log4Json.EXCEPTION_CLASS, ioe.getClass().getName()); JsonNode node = assertNodeContains(rootNode, Log4Json.STACK); assertTrue("Not an array: " + node, node.isArray()); node = assertNodeContains(rootNode, Log4Json.DATE); assertTrue("Not a string: " + node, node.isTextual()); //rather than try and make assertions about the format of the text //message equalling another ISO date, this test asserts that the hypen //and colon characters are in the string. String dateText = node.getTextValue(); assertTrue("No '-' in " + dateText, dateText.contains("-")); assertTrue("No '-' in " + dateText, dateText.contains(":")); } /** * Create a log instance and and log to it * @throws Throwable if it all goes wrong */ @Test public void testLog() throws Throwable { String message = "test message"; Throwable throwable = null; String json = logOut(message, throwable); println("testLog", json); } /** * Create a log instance and and log to it * @throws Throwable if it all goes wrong */ @Test public void testLogExceptions() throws Throwable { String message = "test message"; Throwable inner = new IOException("Directory / not found"); Throwable throwable = new IOException("startup failure", inner); String json = logOut(message, throwable); println("testLogExceptions", json); } void assertEntryEquals(ContainerNode rootNode, String key, String value) { JsonNode node = assertNodeContains(rootNode, key); assertEquals(value, node.getTextValue()); } private JsonNode assertNodeContains(ContainerNode rootNode, String key) { JsonNode node = rootNode.get(key); if (node == null) { fail("No entry of name \"" + key + "\" found in " + rootNode.toString()); } return node; } void assertEntryEquals(ContainerNode rootNode, String key, long value) { JsonNode node = assertNodeContains(rootNode, key); assertEquals(value, node.getNumberValue()); } /** * Print out what's going on. The logging APIs aren't used and the text * delimited for more details * * @param name name of operation * @param text text to print */ private void println(String name, String text) { System.out.println(name + ": #" + text + "#"); } private String logOut(String message, Throwable throwable) { StringWriter writer = new StringWriter(); Logger logger = createLogger(writer); logger.info(message, throwable); //remove and close the appender logger.removeAllAppenders(); return writer.toString(); } public Logger createLogger(Writer writer) { TestLoggerRepository repo = new TestLoggerRepository(); Logger logger = repo.getLogger("test"); Log4Json layout = new Log4Json(); WriterAppender appender = new WriterAppender(layout, writer); logger.addAppender(appender); return logger; } /** * This test logger avoids integrating with the main runtimes Logger hierarchy * in ways the reader does not want to know. */ private static class TestLogger extends Logger { private TestLogger(String name, LoggerRepository repo) { super(name); repository = repo; setLevel(Level.INFO); } } public static class TestLoggerRepository implements LoggerRepository { @Override public void addHierarchyEventListener(HierarchyEventListener listener) { } @Override public boolean isDisabled(int level) { return false; } @Override public void setThreshold(Level level) { } @Override public void setThreshold(String val) { } @Override public void emitNoAppenderWarning(Category cat) { } @Override public Level getThreshold() { return Level.ALL; } @Override public Logger getLogger(String name) { return new TestLogger(name, this); } @Override public Logger getLogger(String name, LoggerFactory factory) { return new TestLogger(name, this); } @Override public Logger getRootLogger() { return new TestLogger("root", this); } @Override public Logger exists(String name) { return null; } @Override public void shutdown() { } @Override public Enumeration getCurrentLoggers() { return new Vector().elements(); } @Override public Enumeration getCurrentCategories() { return new Vector().elements(); } @Override public void fireAddAppenderEvent(Category logger, Appender appender) { } @Override public void resetConfiguration() { } } }