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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2002-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 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 Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero 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.core.test;

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

import junit.framework.AssertionFailedError;

import org.slf4j.LoggerFactory;

/**
 * <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 {
    private static final LoggingEvent[] EMPTY_LOGGING_EVENT = new LoggingEvent[0];

    private static List<LoggingEvent> s_events;
    private static Level s_highestLoggedLevel = Level.TRACE;
    private static String s_defaultLevel = "DEBUG";
    private static MockLogAppender s_instance = null;

    /**
     * <p>Constructor for MockLogAppender.</p>
     */
    protected MockLogAppender() {
        MockLoggerFactory.setAppender(this);
        resetEvents();
        resetLogLevel();
    }

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

    /**
     * <p>getEvents</p>
     *
     */
    public static LoggingEvent[] getEvents() {
        return (LoggingEvent[]) s_events.toArray(EMPTY_LOGGING_EVENT);
    }

    /**
     * <p>getEventsGreaterOrEqual</p>
     *
     */
    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().ge(level)) {
                    matching.add(event);
                }
            }
        }

        return matching.toArray(EMPTY_LOGGING_EVENT);
    }

    /**
     * <p>getEventsAtLevel</p>
     *
     * Returns events that were logged at the specified level
     * 
     */
    public static LoggingEvent[] getEventsAtLevel(final Level level) {
        final LinkedList<LoggingEvent> matching = new LinkedList<LoggingEvent>();

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

        return matching.toArray(EMPTY_LOGGING_EVENT);
    }

    /**
     * <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(MockLogger.DEFAULT_LOG_LEVEL_KEY, s_defaultLevel);
        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, String level, final Properties config) {
        s_defaultLevel = level;
        resetLogLevel();
        resetEvents();

        setProperty(MockLogger.DEFAULT_LOG_LEVEL_KEY, level);
        setProperty(MockLogger.LOG_KEY_PREFIX + "com.mchange", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "com.mchange.v2", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "httpclient", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.apache.aries.blueprint.container", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.apache.bsf", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.apache.http", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.apache.commons.httpclient.HttpMethodBase", "ERROR");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.exolab.castor", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.gwtwidgets", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.hibernate", "INFO");
        // One of these is probably unused...
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.hibernate.sql", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.hibernate.SQL", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.hibernate.cfg.AnnotationBinder", "ERROR");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.hibernate.cfg.annotations.EntityBinder", "ERROR");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.quartz", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.snmp4j", "ERROR");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.snmp4j.agent", "ERROR");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework.beans.factory.support", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework.context.support", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework.jdbc.datasource", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework.test.context.support", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "org.springframework.security", "INFO");
        setProperty(MockLogger.LOG_KEY_PREFIX + "com.mchange.v2", "WARN");
        setProperty(MockLogger.LOG_KEY_PREFIX + "snaq.db", "INFO");

        for (final Object oKey : config.keySet()) {
            final String key = ((String) oKey).replaceAll("^log4j.logger.", MockLogger.LOG_KEY_PREFIX);
            setProperty(key, config.getProperty((String) oKey));
        }
    }

    private static void setProperty(final String key, final String value) {
        System.setProperty(key, System.getProperty(key, value));
    }

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

    /**
     * <p>receivedLogLevel</p>
     *
     * @param level a {@link Level} object.
     */
    public static void receivedLogLevel(final Level level) {
        if (level.gt(s_highestLoggedLevel)) {
            //System.err.println("MockLogAppender: current level: " + s_highestLoggedLevel + ", new level: " + level);
            s_highestLoggedLevel = level;
        }
    }

    /**
     * <p>resetLogLevel</p>
     */
    public static void resetLogLevel() {
        s_highestLoggedLevel = Level.TRACE;
        System.setProperty(MockLogger.DEFAULT_LOG_LEVEL_KEY, s_defaultLevel);
    }

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

    /**
     * <p>assertNotGreaterOrEqual</p>
     *
     * @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);
    }

    public static void assertNoErrorOrGreater() throws AssertionFailedError {
        assertNotGreaterOrEqual(Level.ERROR);
    }

    public static void assertNoFatalOrGreater() throws AssertionFailedError {
        assertNotGreaterOrEqual(Level.FATAL);
    }

    /**
     * <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)
     *
     * @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);
        }

    }

    public static void addEvent(final LoggingEvent loggingEvent) {
        s_events.add(loggingEvent);
        receivedLogLevel(loggingEvent.getLevel());
    }

    public static MockLogAppender getInstance() {
        setupLogging();

        if (s_instance == null) {
            s_instance = new MockLogAppender();
        }

        return s_instance;
    }

    public static void assertNoLogging() throws AssertionFailedError {
        if (s_events.size() > 0) {
            throw new AssertionFailedError("Unhandled logging occurred.");
        }
    }

    public static void assertLogMatched(final Level level, final String message) {
        for (final LoggingEvent event : s_events) {
            if (event.getLevel().eq(level) && event.getMessage().contains(message)) {
                return;
            }
        }
        throw new AssertionFailedError(
                "No log message matched for log level " + level + ", message '" + message + "'");
    }
}