/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.utilities;
import java.util.logging.Filter;
import java.util.logging.LogRecord;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
/**
* A Java Logging <code>Filter</code> that redirects <code>LogRecord</code>s
* to a Log4J <code>Logger</code>. This class is useful if your project wants
* consistent logging configuration based on Log4J, but must work with a library
* that uses the Java Logging API. By using this filter's convenience method,
* <code>apply</code>, messages that would otherwise go to Java Logging
* handler(s) are instead sent to the configured Log4J appender(s). Example use:
*
* <pre>
* // Causes all messages generated by the org.example.Example
* // Java Logger to be sent to an equivalently-named Log4J appender.
* Log4JRedirectFilter.apply("org.example.Example");
* </pre>
*
* The log level mapping used is as follows:
*
* <pre>
* Log4J Java Logging
* ------------ ------------
* OFF OFF
* ERROR, FATAL SEVERE
* WARN WARNING
* INFO INFO, CONFIG
* DEBUG FINER, FINE
* TRACE FINEST
* ALL ALL
* </pre>
*
* @author Chris Wilper
*/
public class Log4JRedirectFilter
implements Filter {
private static final String FQCN = Log4JRedirectFilter.class.getName();
private final Logger _log4jLogger;
public Log4JRedirectFilter(Logger log4jLogger) {
_log4jLogger = log4jLogger;
}
/**
* Convenience method for applying a <code>Log4JRedirectFilter</code> to a
* Java Logger, by name. Log records destined for the given Java logger will
* be redirected to the Log4J logger of the same name.
*/
public static void apply(String javaLoggerName) {
java.util.logging.Logger javaLogger =
java.util.logging.Logger.getLogger(javaLoggerName);
Logger log4jLogger = Logger.getLogger(javaLoggerName);
// set the level of the java logger according to the level
// of the equivalent log4j logger. By doing this, we are overriding
// the level currently in play for the Java Logger with that configured
// for the equivalently-named Log4J logger.
javaLogger.setLevel(getJavaLogLevel(log4jLogger.getLevel()));
// set the filter to one that redirects messages to a Log4J logger
// of the same name
javaLogger.setFilter(new Log4JRedirectFilter(log4jLogger));
}
/**
* Always returns false, but before doing so, sends the given record to the
* Log4J Logger.
*/
public boolean isLoggable(LogRecord record) {
_log4jLogger.callAppenders(getLoggingEvent(record));
return false;
}
/**
* Translates the given Java Logging <code>LogRecord</code> to a
* roughly-equivalent Log4J <code>LoggingEvent</code>.
*/
private LoggingEvent getLoggingEvent(LogRecord record) {
Level log4jLevel = getLog4jLevel(record.getLevel());
return new CustomLoggingEvent(FQCN, _log4jLogger, log4jLevel, record);
}
/**
* Gets the Java Logging Level analogous to the given Log4J Level.
*/
private static java.util.logging.Level getJavaLogLevel(Level log4jLevel) {
if (log4jLevel == null) {
return java.util.logging.Level.SEVERE;
} else if (log4jLevel == Level.OFF) {
return java.util.logging.Level.OFF;
} else if (log4jLevel == Level.ERROR || log4jLevel == Level.FATAL) {
return java.util.logging.Level.SEVERE;
} else if (log4jLevel == Level.WARN) {
return java.util.logging.Level.WARNING;
} else if (log4jLevel == Level.INFO) {
return java.util.logging.Level.CONFIG;
} else if (log4jLevel == Level.DEBUG) {
return java.util.logging.Level.FINER;
} else if (log4jLevel == Level.TRACE) {
return java.util.logging.Level.FINEST;
} else if (log4jLevel == Level.ALL) {
return java.util.logging.Level.ALL;
} else {
throw new IllegalArgumentException("Unrecognized Log4J Level: "
+ log4jLevel.getClass().getName());
}
}
/**
* Gets the Log4J Level analogous to the given Java Logging level.
*/
private static Level getLog4jLevel(java.util.logging.Level javaLevel) {
if (javaLevel == null) {
return Level.ERROR;
} else if (javaLevel == java.util.logging.Level.OFF) {
return Level.OFF;
} else if (javaLevel == java.util.logging.Level.SEVERE) {
return Level.ERROR;
} else if (javaLevel == java.util.logging.Level.WARNING) {
return Level.WARN;
} else if (javaLevel == java.util.logging.Level.INFO) {
return Level.INFO;
} else if (javaLevel == java.util.logging.Level.CONFIG) {
return Level.INFO;
} else if (javaLevel == java.util.logging.Level.FINE) {
return Level.DEBUG;
} else if (javaLevel == java.util.logging.Level.FINER) {
return Level.DEBUG;
} else if (javaLevel == java.util.logging.Level.FINEST) {
return Level.TRACE;
} else if (javaLevel == java.util.logging.Level.ALL) {
return Level.ALL;
} else {
throw new IllegalArgumentException("Unrecognized Java Logging "
+ "Level: " + javaLevel.getClass().getName());
}
}
/**
* This subclass is necessary because we need to force the source classname
* and source methodname into the Log4J LoggingEvent.
*/
class CustomLoggingEvent
extends LoggingEvent {
private static final long serialVersionUID = 1L;
private final LogRecord _record;
private LocationInfo _locationInfo;
public CustomLoggingEvent(String callerFQCN,
Logger logger,
Level level,
LogRecord record) {
super(callerFQCN, logger, level, record.getMessage(), record
.getThrown());
_record = record;
}
@Override
public synchronized LocationInfo getLocationInformation() {
if (_locationInfo == null) {
Throwable stackInfo = new Throwable();
stackInfo.fillInStackTrace();
_locationInfo =
new CustomLocationInfo(stackInfo, _record
.getSourceClassName(), _record
.getSourceMethodName());
}
return _locationInfo;
}
}
/**
* Supports forcing the classname and method name for our
* <code>CustomLoggingEvent</code> class.
*/
class CustomLocationInfo
extends LocationInfo {
private static final long serialVersionUID = 1L;
private final String _sourceClassName;
private final String _sourceMethodName;
public CustomLocationInfo(Throwable stackInfo,
String sourceClassName,
String sourceMethodName) {
super(stackInfo, sourceClassName);
_sourceClassName = sourceClassName;
_sourceMethodName = sourceMethodName;
}
@Override
public String getClassName() {
return _sourceClassName;
}
@Override
public String getMethodName() {
return _sourceMethodName;
}
@Override
public String getFileName() {
// Java Logging doesn't provide this, so we don't know
return NA;
}
@Override
public String getLineNumber() {
// Java Logging doesn't provide this, so we don't know
return NA;
}
}
}