package alma.acs.logging.adapters;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.DefaultRepositorySelector;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.RepositorySelector;
import org.apache.log4j.spi.RootLogger;
import org.apache.log4j.varia.NullAppender;
import alma.acs.logging.AcsLogger;
import alma.acs.logging.ClientLogManager;
/**
* Redirects log4j logs to ACS logging, see http://jira.alma.cl/browse/COMP-8423
*/
public class Log4jFactory implements LoggerFactory
{
/**
* Laser alarm system logger name base.
*/
public static final String LASER_LOGGER_NAME_PREFIX = "laser";
/**
* @see Log4jLogger#enableAcsLogging()
*/
private static class MyLog4jHierarchy extends Hierarchy {
private final LoggerFactory myFactory;
public MyLog4jHierarchy() {
super(new RootLogger(Level.DEBUG));
myFactory = new Log4jFactory();
}
public Logger getLogger(String name) {
return getLogger(name, myFactory);
}
}
/**
* This method must be called once in order to enable ACS logging behind the scenes of log4j logging.
* <p>
* The log4j framework is quite resistant against being substituted with a different logging framework.
* Even though it is possible to configure a custom logger factory using <code>log4j.loggerFactory</code>,
* that factory will not be used when 3rd party code calls the usual <code>Logger.getLogger(name)</code>.
* It seems to make sense only for cases where the custom logger is used as in <code>MyLogger.getLogger(name)</code>.
* log4j-over-slf4j (http://www.slf4j.org/legacy.html) simply re-implements the relevant log4j classes,
* which is too much trouble here for us because only basic log4j features are being used.
* <p>
* We make use of the RepositorySelector mechanism, which log4j foresees for a different purpose,
* to separate logging contexts in an application server that does not have classloader separation.
* (See also http://articles.qos.ch/sc.html.)
* It is not possible to configure this externally, so that an application must call this method.
* See also http://mail-archives.apache.org/mod_mbox/logging-log4j-user/200904.mbox/%3Ca44e15a30904020424g4b7d7fcx63ca32152c81f80d@mail.gmail.com%3E
* <p>
* @TODO: In the future we could let ClientLogManager call this method,
* but currently we are afraid of side effects with frameworks other than the laser alarm system
* that also use log4j (see http://jira.alma.cl/browse/COMP-8423).
*/
public static void enableAcsLogging() {
System.setProperty("log4j.defaultInitOverride", "true");
// System.setProperty("log4j.debug", "true");
Hierarchy h = new MyLog4jHierarchy();
RepositorySelector repositorySelector = new DefaultRepositorySelector(h);
LogManager.setRepositorySelector(repositorySelector, null);
Logger rootLogger = Logger.getRootLogger();
rootLogger.removeAllAppenders();
rootLogger.addAppender(new NullAppender()); // to avoid "log4j:WARN No appenders could be found for logger (root)."
rootLogger.setLevel(Level.ALL);
}
/**
* key = framework name (e.g. "laser"), value = Logger
*/
private final Map<String, Logger> loggerMap = new HashMap<String, Logger>();
@Override
public synchronized Logger makeNewLoggerInstance(String name) {
// System.out.println("*** makeNewLoggerInstance called, name=" + name + " ***");
// Check which framework is requesting the log4j logger
String loggerNameBase = "unknown";
StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if (stackTraceElement.getClassName().contains("cern.laser.")) {
loggerNameBase = LASER_LOGGER_NAME_PREFIX;
break;
}
// TODO: add check for other frameworks that use log4j, once we have those
}
// Check if we already have a logger for the client framework, and create it if needed.
Logger myLogger = loggerMap.get(loggerNameBase);
if (myLogger == null) {
AcsLogger delegate = ClientLogManager.getAcsLogManager().getLoggerForCorba(loggerNameBase, true);
myLogger = new Log4jLogger(loggerNameBase, delegate);
loggerMap.put(loggerNameBase, myLogger);
}
return myLogger;
}
}