org.opennms.test.mock.MockLogAppender.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.test.mock.MockLogAppender.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2011-2012 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2012 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.test.mock;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import junit.framework.AssertionFailedError;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;

/**
 * <p>MockLogAppender class. If you do not specify the log level specifically, the level
 * will default to DEBUG and you can control the level by setting the <code>mock.logLevel</code>
 * system property.</p>
 * 
 * Used in unit tests to check that the level of logging produced by a test was suitable.
 * In some cases, the test will be that no messages were logged at a higher priority than
 * specified, e.g. Error messages logged when only Notice were expected.
 * 
 * Some other tests may wish to ensure that an Error or Warn message was indeed logged as expected
 * 
 * Remember: "Greater" in regards to level relates to priority; the higher the level, the less should
 * usually be logged (e.g. Errors (highest) should be only the highest priority messages about really bad things, 
 * as compared to Debug (lowest) which is any old rubbish that might be interesting to a developer)
 *
 * @author brozow
 * @version $Id: $
 */
public class MockLogAppender extends AppenderSkeleton {
    private static List<LoggingEvent> s_events = null;

    private static Level s_logLevel = Level.ALL;

    /**
     * <p>Constructor for MockLogAppender.</p>
     */
    public MockLogAppender() {
        super();
        resetEvents();
        resetLogLevel();
    }

    /** {@inheritDoc} */
    public synchronized void doAppend(final LoggingEvent event) {
        super.doAppend(event);
        receivedLogLevel(event.getLevel());
    }

    /** {@inheritDoc} */
    protected void append(final LoggingEvent event) {
        s_events.add(event);
    }

    /**
     * <p>close</p>
     */
    public void close() {
    }

    /**
     * <p>requiresLayout</p>
     *
     * @return a boolean.
     */
    public boolean requiresLayout() {
        return false;
    }

    /**
     * <p>resetEvents</p>
     */
    public static void resetEvents() {
        s_events = Collections.synchronizedList(new LinkedList<LoggingEvent>());
    }

    /**
     * <p>getEvents</p>
     *
     * @return an array of {@link org.apache.log4j.spi.LoggingEvent} objects.
     */
    public static LoggingEvent[] getEvents() {
        return (LoggingEvent[]) s_events.toArray(new LoggingEvent[0]);
    }

    /**
     * <p>getEventsGreaterOrEqual</p>
     *
     * @param level a {@link org.apache.log4j.Level} object.
     * @return an array of {@link org.apache.log4j.spi.LoggingEvent} objects.
     */
    public static LoggingEvent[] getEventsGreaterOrEqual(final Level level) {
        LinkedList<LoggingEvent> matching = new LinkedList<LoggingEvent>();

        synchronized (s_events) {
            for (final LoggingEvent event : s_events) {
                if (event.getLevel().isGreaterOrEqual(level)) {
                    matching.add(event);
                }
            }
        }

        return matching.toArray(new LoggingEvent[0]);
    }

    /**
     * <p>getEventsAtLevel</p>
     *
     * Returns events that were logged at the specified level
     * 
     * @param level a {@link org.apache.log4j.Level} object.
     * @return an array of {@link org.apache.log4j.spi.LoggingEvent} objects.
     */
    public static LoggingEvent[] getEventsAtLevel(final Level level) {
        LinkedList<LoggingEvent> matching = new LinkedList<LoggingEvent>();

        synchronized (s_events) {
            for (final LoggingEvent event : s_events) {
                if (event.getLevel().isGreaterOrEqual(level)) {
                    matching.add(event);
                }
            }
        }

        return matching.toArray(new LoggingEvent[0]);
    }

    /**
     * <p>setupLogging</p>
     */
    public static void setupLogging() {
        setupLogging(new Properties());
    }

    /**
     * <p>setupLogging</p>
     *
     * @param config a {@link java.util.Properties} object.
     */
    public static void setupLogging(final Properties config) {
        setupLogging(true, config);
    }

    /**
     * <p>setupLogging</p>
     *
     * @param toConsole a boolean.
     */
    public static void setupLogging(final boolean toConsole) {
        setupLogging(toConsole, new Properties());
    }

    /**
     * <p>setupLogging</p>
     *
     * @param toConsole a boolean.
     * @param props a {@link java.util.Properties} object.
     */
    public static void setupLogging(final boolean toConsole, final Properties props) {
        final String level = System.getProperty("mock.logLevel", "DEBUG");
        setupLogging(toConsole, level, props);
    }

    /**
     * <p>setupLogging</p>
     *
     * @param toConsole a boolean.
     * @param level a {@link java.lang.String} object.
     */
    public static void setupLogging(final boolean toConsole, final String level) {
        setupLogging(toConsole, level, new Properties());
    }

    /**
     * <p>setupLogging</p>
     *
     * @param toConsole a boolean.
     * @param level a {@link java.lang.String} object.
     * @param config a {@link java.util.Properties} object.
     */
    public static void setupLogging(final boolean toConsole, final String level, final Properties config) {
        resetLogLevel();

        final Properties logConfig = new Properties(config);
        final String consoleAppender = (toConsole ? ", CONSOLE" : "");

        setProperty(logConfig, "log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender");
        setProperty(logConfig, "log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout");
        setProperty(logConfig, "log4j.appender.CONSOLE.layout.ConversionPattern", "%d %-5p [%t] %c: %m%n");
        setProperty(logConfig, "log4j.appender.MOCK", MockLogAppender.class.getName());
        setProperty(logConfig, "log4j.appender.MOCK.layout", "org.apache.log4j.PatternLayout");
        setProperty(logConfig, "log4j.appender.MOCK.layout.ConversionPattern", "%-5p [%t] %c: %m%n");

        setProperty(logConfig, "log4j.rootCategory", level + consoleAppender + ", MOCK");
        setProperty(logConfig, "log4j.logger.org.apache.commons.httpclient.HttpMethodBase", "ERROR");
        setProperty(logConfig, "log4j.logger.org.exolab.castor", "INFO");
        setProperty(logConfig, "log4j.logger.org.snmp4j", "ERROR");
        setProperty(logConfig, "log4j.logger.org.snmp4j.agent", "ERROR");
        setProperty(logConfig, "log4j.logger.com.mchange.v2.c3p0.impl", "WARN");
        setProperty(logConfig, "log4j.logger.org.hibernate.cfg.AnnotationBinder",
                "ERROR" + consoleAppender + ", MOCK");

        PropertyConfigurator.configure(logConfig);
    }

    private static void setProperty(final Properties logConfig, final String key, final String value) {
        if (!logConfig.containsKey(key)) {
            logConfig.put(key, value);
        }
    }

    /**
     * <p>isLoggingSetup</p>
     *
     * @return a boolean.
     */
    public static boolean isLoggingSetup() {
        return s_events != null;
    }

    /**
     * <p>receivedLogLevel</p>
     *
     * @param level a {@link org.apache.log4j.Level} object.
     */
    public static void receivedLogLevel(final Level level) {
        if (level.isGreaterOrEqual(s_logLevel)) {
            s_logLevel = level;
        }
    }

    /**
     * <p>resetLogLevel</p>
     */
    public static void resetLogLevel() {
        s_logLevel = Level.ALL;
    }

    /**
     * <p>noWarningsOrHigherLogged</p>
     *
     * @return a boolean.
     */
    public static boolean noWarningsOrHigherLogged() {
        return Level.INFO.isGreaterOrEqual(s_logLevel);
    }

    /**
     * <p>assertNotGreaterOrEqual</p>
     *
     * @param level a {@link org.apache.log4j.Level} object.
     * @throws junit.framework.AssertionFailedError if any.
     */
    public static void assertNotGreaterOrEqual(final Level level) throws AssertionFailedError {
        if (!isLoggingSetup()) {
            throw new AssertionFailedError("MockLogAppender has not been initialized");
        }

        try {
            Thread.sleep(500);
        } catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        final LoggingEvent[] events = getEventsGreaterOrEqual(level);
        if (events.length == 0) {
            return;
        }

        StringBuffer message = new StringBuffer("Log messages at or greater than the log level ").append(level)
                .append(" received:");

        for (final LoggingEvent event : events) {
            message.append("\n\t[").append(event.getLevel()).append("] ").append(event.getLoggerName()).append(": ")
                    .append(event.getMessage());
        }

        throw new AssertionFailedError(message.toString());
    }

    /**
     * <p>assertNoWarningsOrGreater</p>
     *
     * @throws junit.framework.AssertionFailedError if any.
     */
    public static void assertNoWarningsOrGreater() throws AssertionFailedError {
        assertNotGreaterOrEqual(Level.WARN);
    }

    /**
     * <p>assertLogAtLevel</p>
     * Asserts that a message was logged at the requested level.
     * 
     * Useful for testing code that *should* have logged an error message 
     * (or a notice or some other special case)
     *
     * @param level a {@link org.apache.log4j.Level} object.
     * @throws junit.framework.AssertionFailedError if any.
     */
    public static void assertLogAtLevel(final Level level) throws AssertionFailedError {
        if (!isLoggingSetup()) {
            throw new AssertionFailedError("MockLogAppender has not been initialized");
        }

        try {
            Thread.sleep(500);
        } catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        final LoggingEvent[] events = getEventsAtLevel(level);
        if (events.length == 0) {
            throw new AssertionFailedError("No messages were received at level " + level);
        }

    }

}