package vroom.common.utilities.logging;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.AppenderAttachable;
/**
* This class wraps a <tt>org.apache.log4j.Logger</tt> and adds specific methods for the logging of formated strings
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a> - <a
* href="http://copa.uniandes.edu.co">Copa</a>, <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
*/
public class LoggerHelper implements AppenderAttachable {
public static Layout DEFAULT_CONSOLE_LAYOUT = new PatternLayout(
"%d{HH:mm:ss} %-5p %-15c [%-10t] : %m%n");
/**
* Level for low debugging log messages. Useful for instance for detailed trace
*/
public static final Level LEVEL_LOW_DEBUG = Level.toLevel(Level.DEBUG_INT / 2);
/** Level for common debugging log messages */
public static final Level LEVEL_DEBUG = Level.toLevel(Level.DEBUG_INT);
/** Level for information messages */
public static final Level LEVEL_INFO = Level.toLevel(Level.INFO_INT);
/** Level for warning messages */
public static final Level LEVEL_WARN = Level.toLevel(Level.WARN_INT);
/**
* Level for error messages, to be used for instance when catching an exception that interrupted a process
*/
public static final Level LEVEL_ERROR = Level.toLevel(Level.ERROR_INT);
/** Level for fatal messages, to be used when a event stopped a process */
public static final Level LEVEL_FATAL = Level.toLevel(Level.FATAL_INT);
/** The wrapped logger */
private final Logger mLogger;
private static final Map<String, LoggerHelper> LOGGERS = new HashMap<String, LoggerHelper>();
/** <code>true</code> if the stack trace should be logged for warn messages and higher */
public static boolean LOG_STACK = false;
/**
* Access to the underlying logger
*
* @return the wrapped {@link Logger}
*/
protected Logger getLogger() {
return mLogger;
}
/**
* Add the given <tt>appender</tt> to the wrapped logger
*
* @param appender
*/
@Override
public void addAppender(Appender appender) {
getLogger().addAppender(appender);
}
public Enumeration<?> getAllappenders() {
return getLogger().getAllAppenders();
}
/**
* Configure the logger with a default async appender
* <p/>
* Messages will be logged in the console using an {@link UnlimitedAsyncAppender} based on a {@link ConsoleAppender}
* with the {@link #DEFAULT_CONSOLE_LAYOUT}.
*
* @param loggerLevel
* the level of the root logger
* @param appenderLevel
* the level of the async console appender that will be added to the logger. All log events with lower
* level will be ignored. If <code>null</code> default value of {@link #LEVEL_ERROR} will be used
* @param async
* <code>true</code> if the default console appender should be wrapped in an
* {@link UnlimitedAsyncAppender}
*/
public static void setupRootLogger(Level loggerLevel, Level appenderLevel, boolean async) {
if (appenderLevel == null) {
appenderLevel = LEVEL_ERROR;
}
ConsoleAppender appender = new ConsoleAppender(DEFAULT_CONSOLE_LAYOUT);
appender.setThreshold(appenderLevel);
Appender mainAppender;
if (async) {
// The main async appender for the logging system
UnlimitedAsyncAppender asyncAppender = new UnlimitedAsyncAppender();
asyncAppender.addAppender(appender);
mainAppender = asyncAppender;
} else {
mainAppender = appender;
}
Logger.getRootLogger().addAppender(mainAppender);
Logger.getRootLogger().setLevel(loggerLevel);
}
/**
* Logging of a message
*
* @param logger
* the logger to which the message will be sent
* @param level
* the level of the message
* @param message
* the message to be logged
*/
private static void logMessage(Logger logger, Level level, Object message) {
logMessage(logger, level, message, null);
}
/**
* Logging of a message
*
* @param logger
* the logger to which the message will be sent
* @param level
* the level of the message
* @param message
* the message to be logged
*/
private static void logMessage(Logger logger, Level level, Object message, Throwable cause) {
logger.log(level, message, cause);
}
/**
* Logging of a formatted message
*
* @param logger
* the logger to which the message will be sent
* @param level
* the level of the message
* @param format
* the format string
* @param args
* the arguments of the format string
*/
private static void logMessage(Logger logger, Level level, String format, Object... args) {
logMessage(logger, level, format, null, args);
}
/**
* Logging of a formatted message
*
* @param logger
* the logger to which the message will be sent
* @param level
* the level of the message
* @param cause
* the {@link Throwable} that caused this logging
* @param NUMBER_FORMAT
* the format string
* @param args
* the arguments of the format string
*/
private static void logMessage(Logger logger, Level level, String f, Throwable cause,
Object... args) {
if (logger.isEnabledFor(level)) {
if (LOG_STACK && level.isGreaterOrEqual(LEVEL_WARN)) {
f += "\nStack: %s";
args = Arrays.copyOf(args, args.length + 1);
args[args.length - 1] = Thread.currentThread().getStackTrace();
}
if (args == null || args.length == 0) {
logger.log(level, f, cause);
} else {
logger.log(level, new FormattedLogMessage(f, args), cause);
}
}
}
/**
* Private constructor for a logger wrapper
*
* @param logger
* the logger to be wrapped
*/
private LoggerHelper(Logger logger) {
mLogger = logger;
}
/**
* Shorthand for <code>getLogger(clazz.getName())</code>.
*
* @param clazz
* The name of <code>clazz</code> will be used as the name of the logger to retrieve. See
* {@link #getLogger(String)} for more detailed information.
*/
public static LoggerHelper getLogger(Class<?> clazz) {
return getLogger(clazz.getName());
}
/**
* Retrieve a logger named according to the value of the <code>name</code> parameter. If the named logger already
* exists, then the existing instance will be returned. Otherwise, a new instance is created.
* <p>
* By default, loggers do not have a set level but inherit it from their neareast ancestor with a set level. This is
* one of the central features of log4j.
*
* @param name
* The name of the logger to retrieve.
* @see Logger#getLogger(String)
*/
public static LoggerHelper getLogger(String name) {
LoggerHelper log = LOGGERS.get(name);
if (log == null) {
log = new LoggerHelper(Logger.getLogger(name));
LOGGERS.put(name, log);
}
return log;
}
// ------------------------
// DEBUG LOW
// ------------------------
/**
* Static utility method used to log low level debug messages
*
* @param object
* the object logging the message
* @param format
* @param args
* @see #debug(String,Object[])
*/
public static void lowDebugMessage(Object object, String format, Object... args) {
getLogger(object.getClass()).debug(format, args);
}
/**
* Append <tt>message</tt> with level {@link #LEVEL_LOW_DEBUG}
*
* @param message
* the message to be logged
*/
public void lowDebug(Object message) {
logMessage(mLogger, LEVEL_LOW_DEBUG, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_LOW_DEBUG}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void lowDebug(String format, Object... args) {
logMessage(mLogger, LEVEL_LOW_DEBUG, format, args);
}
// ------------------------
// DEBUG
// ------------------------
/**
* Static utility method used to log debug messages
*
* @param object
* the object logging the message
* @param format
* @param args
* @see #debug(String,Object[])
*/
public static void debugMessage(Object object, String format, Object... args) {
getLogger(object.getClass()).debug(format, args);
}
/**
* Append <tt>message</tt> with level {@link #LEVEL_DEBUG}
*
* @param message
* the message to be logged
*/
public void debug(Object message) {
logMessage(mLogger, LEVEL_DEBUG, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_DEBUG}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void debug(String format, Object... args) {
logMessage(mLogger, LEVEL_DEBUG, format, args);
}
// ------------------------
// INFO
// ------------------------
/**
* Append <tt>message</tt> with level {@link #LEVEL_INFO}
*
* @param message
* the message to be logged
*/
public void info(Object message) {
logMessage(mLogger, LEVEL_INFO, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_INFO}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void info(String format, Object... args) {
logMessage(mLogger, LEVEL_INFO, format, args);
}
/**
* Append <tt>message</tt> with level {@link #LEVEL_WARN}
*
* @param message
* the message to be logged
*/
public void warn(Object message) {
logMessage(mLogger, LEVEL_WARN, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_WARN}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void warn(String format, Object... args) {
logMessage(mLogger, LEVEL_WARN, format, args);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_WARN}
*
* @param format
* a formatting string to produce the message
* @param cause
* the <code>Throwable</code> object that produced this message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void warn(String format, Throwable cause, Object... args) {
logMessage(mLogger, LEVEL_WARN, format, cause, args);
}
// ------------------------
// ERROR
// ------------------------
/**
* Append <tt>message</tt> with level {@link #LEVEL_ERROR}
*
* @param message
* the message to be logged
*/
public void error(Object message) {
logMessage(mLogger, LEVEL_ERROR, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_ERROR}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void error(String format, Object... args) {
logMessage(mLogger, LEVEL_ERROR, format, args);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_ERROR}
*
* @param format
* a formatting string to produce the message
* @param cause
* the <code>Throwable</code> object that produced this message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void error(String format, Throwable cause, Object... args) {
logMessage(mLogger, LEVEL_ERROR, format, cause, args);
}
/**
* Append an error message when an exception is caught of the form
* <code>"Exception caught in [context] [cause.getMessage]"</code>
*
* @param context
* a context string, can contain formating
* @param cause
* the {@link Throwable} that was caught
* @param args
* optional fomrating arguments for the context
*/
public void exception(String context, Throwable cause, Object... args) {
Object[] argst = new Object[args.length + 2];
argst[argst.length - 2] = cause != null ? cause.getClass().getSimpleName() : "na";
argst[argst.length - 1] = cause != null ? cause.getMessage() : "na";
for (int i = 0; i < args.length; i++) {
argst[i] = args[i];
}
logMessage(mLogger, LEVEL_ERROR, "Exception caught in " + context + " %s: %s", cause, argst);
}
/**
* Append an error message when an exception is caught of the form
* <code>"Exception caught in [context] [cause.getMessage]"</code>
*
* @param context
* a context string, can contain formating
* @param cause
* the {@link Throwable} that was caught
* @param args
* optional fomrating arguments for the context
*/
public void fatalException(String context, Throwable cause, Object... args) {
Object[] argst = new Object[args.length + 2];
argst[argst.length - 2] = cause != null ? cause.getClass().getSimpleName() : "na";
argst[argst.length - 1] = cause != null ? cause.getMessage() : "na";
for (int i = 0; i < args.length; i++) {
argst[i] = args[i];
}
logMessage(mLogger, LEVEL_FATAL, "Exception caught in " + context + " %s: %s", cause, argst);
}
// ------------------------
// FATAL
// ------------------------
/**
* Append <tt>message</tt> with level {@link #LEVEL_FATAL}
*
* @param message
* the message to be logged
*/
public void fatal(Object message) {
logMessage(mLogger, LEVEL_FATAL, message);
}
/**
* Append a message defined by <tt>format</tt> and <tt>args</tt>, with level {@link #LEVEL_FATAL}
*
* @param format
* a formatting string to produce the message
* @param args
* the arguments to be used with the formatted string
* @see String#format(String, Object[])
*/
public void fatal(String format, Object... args) {
logMessage(mLogger, LEVEL_FATAL, format, args);
}
/**
* Set the level of the wrapped logger
*
* @param level
*/
public void setLevel(Level level) {
getLogger().setLevel(level);
}
public boolean isEnabledFor(Level level) {
return getLogger().isEnabledFor(level);
}
@Override
public Enumeration<?> getAllAppenders() {
return getLogger().getAllAppenders();
}
@Override
public Appender getAppender(String name) {
return getLogger().getAppender(name);
}
@Override
public boolean isAttached(Appender appender) {
return getLogger().isAttached(appender);
}
@Override
public void removeAllAppenders() {
getLogger().removeAllAppenders();
}
@Override
public void removeAppender(Appender appender) {
getLogger().removeAppender(appender);
}
@Override
public void removeAppender(String name) {
getLogger().removeAppender(name);
}
}