All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.org.lidalia.slf4jtest.TestLogger Maven / Gradle / Ivy

package uk.org.lidalia.slf4jtest;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import org.slf4j.Logger;
import org.slf4j.MDC;
import org.slf4j.Marker;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

import uk.org.lidalia.lang.ThreadLocal;
import uk.org.lidalia.slf4jext.Level;

import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Optional.of;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.Sets.immutableEnumSet;
import static java.util.Arrays.asList;
import static uk.org.lidalia.slf4jext.Level.DEBUG;
import static uk.org.lidalia.slf4jext.Level.ERROR;
import static uk.org.lidalia.slf4jext.Level.INFO;
import static uk.org.lidalia.slf4jext.Level.TRACE;
import static uk.org.lidalia.slf4jext.Level.WARN;
import static uk.org.lidalia.slf4jext.Level.enablableValueSet;

/**
 * Implementation of {@link Logger} which stores {@link LoggingEvent}s in memory and provides methods
 * to access and remove them in order to facilitate writing tests that assert particular logging calls were made.
 * 

* {@link LoggingEvent}s are stored in both an {@link ThreadLocal} and a normal {@link List}. The {@link #getLoggingEvents()} * and {@link #clear()} methods reference the {@link ThreadLocal} events. The {@link #getAllLoggingEvents()} and * {@link #clearAll()} methods reference all events logged on this Logger. This is in order to facilitate parallelising * tests - tests that use the thread local methods can be parallelised. *

* By default all Levels are enabled. It is important to note that the conventional hierarchical notion of Levels, where * info being enabled implies warn and error being enabled, is not a requirement of the SLF4J API, so the * {@link #setEnabledLevels(ImmutableSet)}, {@link #setEnabledLevels(Level...)}, * {@link #setEnabledLevelsForAllThreads(ImmutableSet)}, {@link #setEnabledLevelsForAllThreads(Level...)} and the various * isXxxxxEnabled() methods make no assumptions about this hierarchy. If you wish to use traditional hierarchical setups you may * do so by passing the constants in {@link uk.org.lidalia.slf4jext.ConventionalLevelHierarchy} to * {@link #setEnabledLevels(ImmutableSet)} or {@link #setEnabledLevelsForAllThreads(ImmutableSet)}. */ @SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.TooManyMethods" }) public class TestLogger implements Logger { private final String name; private final TestLoggerFactory testLoggerFactory; private final ThreadLocal> loggingEvents = new ThreadLocal>( Suppliers.makeEmptyMutableList()); private final List allLoggingEvents = new CopyOnWriteArrayList(); private volatile ThreadLocal> enabledLevels = new ThreadLocal>(enablableValueSet()); TestLogger(final String name, final TestLoggerFactory testLoggerFactory) { this.name = name; this.testLoggerFactory = testLoggerFactory; } public String getName() { return name; } /** * Removes all {@link LoggingEvent}s logged by this thread and resets the enabled levels of the logger * to {@link uk.org.lidalia.slf4jext.Level#enablableValueSet()} for this thread. */ public void clear() { loggingEvents.get().clear(); enabledLevels.remove(); } /** * Removes ALL {@link LoggingEvent}s logged on this logger, regardless of thread, * and resets the enabled levels of the logger to {@link uk.org.lidalia.slf4jext.Level#enablableValueSet()} * for ALL threads. */ public void clearAll() { allLoggingEvents.clear(); loggingEvents.reset(); enabledLevels.reset(); } /** * @return all {@link LoggingEvent}s logged on this logger by this thread */ public ImmutableList getLoggingEvents() { return copyOf(loggingEvents.get()); } /** * @return all {@link LoggingEvent}s logged on this logger by ANY thread */ public ImmutableList getAllLoggingEvents() { return copyOf(allLoggingEvents); } /** * @return whether this logger is trace enabled in this thread */ @Override public boolean isTraceEnabled() { return enabledLevels.get().contains(TRACE); } @Override public void trace(final String message) { log(TRACE, message); } @Override public void trace(final String format, final Object arg) { log(TRACE, format, arg); } @Override public void trace(final String format, final Object arg1, final Object arg2) { log(TRACE, format, arg1, arg2); } @Override public void trace(final String format, final Object... args) { log(TRACE, format, args); } @Override public void trace(final String msg, final Throwable throwable) { log(TRACE, msg, throwable); } @Override public boolean isTraceEnabled(final Marker marker) { return enabledLevels.get().contains(TRACE); } @Override public void trace(final Marker marker, final String msg) { log(TRACE, marker, msg); } @Override public void trace(final Marker marker, final String format, final Object arg) { log(TRACE, marker, format, arg); } @Override public void trace(final Marker marker, final String format, final Object arg1, final Object arg2) { log(TRACE, marker, format, arg1, arg2); } @Override public void trace(final Marker marker, final String format, final Object... args) { log(TRACE, marker, format, args); } @Override public void trace(final Marker marker, final String msg, final Throwable throwable) { log(TRACE, marker, msg, throwable); } /** * @return whether this logger is debug enabled in this thread */ @Override public boolean isDebugEnabled() { return enabledLevels.get().contains(DEBUG); } @Override public void debug(final String message) { log(DEBUG, message); } @Override public void debug(final String format, final Object arg) { log(DEBUG, format, arg); } @Override public void debug(final String format, final Object arg1, final Object arg2) { log(DEBUG, format, arg1, arg2); } @Override public void debug(final String format, final Object... args) { log(DEBUG, format, args); } @Override public void debug(final String msg, final Throwable throwable) { log(DEBUG, msg, throwable); } @Override public boolean isDebugEnabled(final Marker marker) { return enabledLevels.get().contains(DEBUG); } @Override public void debug(final Marker marker, final String msg) { log(DEBUG, marker, msg); } @Override public void debug(final Marker marker, final String format, final Object arg) { log(DEBUG, marker, format, arg); } @Override public void debug(final Marker marker, final String format, final Object arg1, final Object arg2) { log(DEBUG, marker, format, arg1, arg2); } @Override public void debug(final Marker marker, final String format, final Object... args) { log(DEBUG, marker, format, args); } @Override public void debug(final Marker marker, final String msg, final Throwable throwable) { log(DEBUG, marker, msg, throwable); } /** * @return whether this logger is info enabled in this thread */ @Override public boolean isInfoEnabled() { return enabledLevels.get().contains(INFO); } @Override public void info(final String message) { log(INFO, message); } @Override public void info(final String format, final Object arg) { log(INFO, format, arg); } @Override public void info(final String format, final Object arg1, final Object arg2) { log(INFO, format, arg1, arg2); } @Override public void info(final String format, final Object... args) { log(INFO, format, args); } @Override public void info(final String msg, final Throwable throwable) { log(INFO, msg, throwable); } @Override public boolean isInfoEnabled(final Marker marker) { return enabledLevels.get().contains(INFO); } @Override public void info(final Marker marker, final String msg) { log(INFO, marker, msg); } @Override public void info(final Marker marker, final String format, final Object arg) { log(INFO, marker, format, arg); } @Override public void info(final Marker marker, final String format, final Object arg1, final Object arg2) { log(INFO, marker, format, arg1, arg2); } @Override public void info(final Marker marker, final String format, final Object... args) { log(INFO, marker, format, args); } @Override public void info(final Marker marker, final String msg, final Throwable throwable) { log(INFO, marker, msg, throwable); } /** * @return whether this logger is warn enabled in this thread */ @Override public boolean isWarnEnabled() { return enabledLevels.get().contains(WARN); } @Override public void warn(final String message) { log(WARN, message); } @Override public void warn(final String format, final Object arg) { log(WARN, format, arg); } @Override public void warn(final String format, final Object arg1, final Object arg2) { log(WARN, format, arg1, arg2); } @Override public void warn(final String format, final Object... args) { log(WARN, format, args); } @Override public void warn(final String msg, final Throwable throwable) { log(WARN, msg, throwable); } @Override public boolean isWarnEnabled(final Marker marker) { return enabledLevels.get().contains(WARN); } @Override public void warn(final Marker marker, final String msg) { log(WARN, marker, msg); } @Override public void warn(final Marker marker, final String format, final Object arg) { log(WARN, marker, format, arg); } @Override public void warn(final Marker marker, final String format, final Object arg1, final Object arg2) { log(WARN, marker, format, arg1, arg2); } @Override public void warn(final Marker marker, final String format, final Object... args) { log(WARN, marker, format, args); } @Override public void warn(final Marker marker, final String msg, final Throwable throwable) { log(WARN, marker, msg, throwable); } /** * @return whether this logger is error enabled in this thread */ @Override public boolean isErrorEnabled() { return enabledLevels.get().contains(ERROR); } @Override public void error(final String message) { log(ERROR, message); } @Override public void error(final String format, final Object arg) { log(ERROR, format, arg); } @Override public void error(final String format, final Object arg1, final Object arg2) { log(ERROR, format, arg1, arg2); } @Override public void error(final String format, final Object... args) { log(ERROR, format, args); } @Override public void error(final String msg, final Throwable throwable) { log(ERROR, msg, throwable); } @Override public boolean isErrorEnabled(final Marker marker) { return enabledLevels.get().contains(ERROR); } @Override public void error(final Marker marker, final String msg) { log(ERROR, marker, msg); } @Override public void error(final Marker marker, final String format, final Object arg) { log(ERROR, marker, format, arg); } @Override public void error(final Marker marker, final String format, final Object arg1, final Object arg2) { log(ERROR, marker, format, arg1, arg2); } @Override public void error(final Marker marker, final String format, final Object... args) { log(ERROR, marker, format, args); } @Override public void error(final Marker marker, final String msg, final Throwable throwable) { log(ERROR, marker, msg, throwable); } private void log(final Level level, final String format, final Object... args) { addLoggingEvent(level, Optional.absent(), Optional.absent(), format, args); } private void log(final Level level, final String msg, final Throwable throwable) { //NOPMD PMD wrongly thinks unused... addLoggingEvent(level, Optional.absent(), fromNullable(throwable), msg); } private void log(final Level level, final Marker marker, final String format, final Object... args) { addLoggingEvent(level, fromNullable(marker), Optional.absent(), format, args); } private void log(final Level level, final Marker marker, final String msg, final Throwable throwable) { addLoggingEvent(level, fromNullable(marker), fromNullable(throwable), msg); } private void addLoggingEvent( final Level level, final Optional marker, final Optional throwable, final String format, final Object... args) { if (enabledLevels.get().contains(level)) { final LoggingEvent event = new LoggingEvent(of(this), level, mdc(), marker, throwable, format, args); allLoggingEvents.add(event); loggingEvents.get().add(event); testLoggerFactory.addLoggingEvent(event); optionallyPrint(event); } } @SuppressWarnings("unchecked") private Map mdc() { return fromNullable(MDC.getCopyOfContextMap()).or(Collections.emptyMap()); } private void optionallyPrint(final LoggingEvent event) { if (testLoggerFactory.getPrintLevel().compareTo(event.getLevel()) <= 0) { event.print(); } } /** * @return the set of levels enabled for this logger on this thread */ public ImmutableSet getEnabledLevels() { return enabledLevels.get(); } /** * The conventional hierarchical notion of Levels, where info being enabled implies warn and error being enabled, is not a * requirement of the SLF4J API, so all levels you wish to enable must be passed explicitly to this method. If you wish to * use traditional hierarchical setups you may conveniently do so by using the constants in * {@link uk.org.lidalia.slf4jext.ConventionalLevelHierarchy} * * @param enabledLevels levels which will be considered enabled for this logger IN THIS THREAD; * does not affect enabled levels for this logger in other threads */ public void setEnabledLevels(final ImmutableSet enabledLevels) { this.enabledLevels.set(enabledLevels); } /** * The conventional hierarchical notion of Levels, where info being enabled implies warn and error being enabled, is not a * requirement of the SLF4J API, so all levels you wish to enable must be passed explicitly to this method. If you wish to * use traditional hierarchical setups you may conveniently do so by passing the constants in * {@link uk.org.lidalia.slf4jext.ConventionalLevelHierarchy} to {@link #setEnabledLevels(ImmutableSet)} * * @param enabledLevels levels which will be considered enabled for this logger IN THIS THREAD; * does not affect enabled levels for this logger in other threads */ public void setEnabledLevels(final Level... enabledLevels) { setEnabledLevels(immutableEnumSet(asList(enabledLevels))); } /** * The conventional hierarchical notion of Levels, where info being enabled implies warn and error being enabled, is not a * requirement of the SLF4J API, so all levels you wish to enable must be passed explicitly to this method. If you wish to * use traditional hierarchical setups you may conveniently do so by using the constants in * {@link uk.org.lidalia.slf4jext.ConventionalLevelHierarchy} * * @param enabledLevelsForAllThreads levels which will be considered enabled for this logger IN ALL THREADS */ public void setEnabledLevelsForAllThreads(final ImmutableSet enabledLevelsForAllThreads) { this.enabledLevels = new ThreadLocal>(enabledLevelsForAllThreads); } /** * The conventional hierarchical notion of Levels, where info being enabled implies warn and error being enabled, is not a * requirement of the SLF4J API, so all levels you wish to enable must be passed explicitly to this method. If you wish to * use traditional hierarchical setups you may conveniently do so by passing the constants in * {@link uk.org.lidalia.slf4jext.ConventionalLevelHierarchy} to {@link #setEnabledLevelsForAllThreads(ImmutableSet)} * * @param enabledLevelsForAllThreads levels which will be considered enabled for this logger IN ALL THREADS */ public void setEnabledLevelsForAllThreads(final Level... enabledLevelsForAllThreads) { setEnabledLevelsForAllThreads(ImmutableSet.copyOf(enabledLevelsForAllThreads)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy