/*
* Licensed under the GPL License. You may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.googlecode.psiprobe.tools.logging.logback;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.beanutils.MethodUtils;
import com.googlecode.psiprobe.tools.logging.DefaultAccessor;
/**
* Wraps a Logback logger factory from a given web application class loader.
*
* <p>
* All Logback classes are loaded via the given class loader and not via psi-probe's own
* class loader. For this reasons, all methods on Logback objects are invoked via reflection.
* </p>
* <p>
* This way, we can even handle different versions of Logback embedded in different WARs.
* </p>
*
* @author Harald Wellmann
*/
public class LogbackFactoryAccessor extends DefaultAccessor {
private LogbackFactoryAccessor() {
}
/**
* Attempts to access a Logback logger factory via the given class loader.
*
* @param cl the ClassLoader to use when fetching the factory
* @return Logback factory wrapper, or {@code null} if the SLF4J binding is
* not Logback or if there is no SLF4J binding at all
*/
public static LogbackFactoryAccessor create(ClassLoader cl) {
try {
// Get the singleton SLF4J binding, which may or may not be Logback, depending on the
// binding.
Class clazz = cl.loadClass("org.slf4j.impl.StaticLoggerBinder");
Method m1 = MethodUtils.getAccessibleMethod(clazz, "getSingleton", new Class[]{});
Object singleton = m1.invoke(null, null);
Method m = MethodUtils.getAccessibleMethod(clazz, "getLoggerFactory", new Class[]{});
Object loggerFactory = m.invoke(singleton, null);
// Check if the binding is indeed logback, return null otherwise
Class loggerFactoryClass = cl.loadClass("ch.qos.logback.classic.LoggerContext");
if (loggerFactoryClass.isInstance(loggerFactory)) {
LogbackFactoryAccessor accessor = new LogbackFactoryAccessor();
accessor.setTarget(loggerFactory);
return accessor;
} else {
return null;
}
} catch (Exception e) {
return null;
}
}
/**
* Returns the Logback root logger.
*
* @return the root logger
*/
public LogbackLoggerAccessor getRootLogger() {
// Logback has no dedicated getRootLogger() method, so we simply access the root logger
// by its well-defined name.
return getLogger("ROOT");
}
/**
* Returns the Logback logger with a given name.
*
* @return the Logger with the given name
*/
public LogbackLoggerAccessor getLogger(String name) {
try {
Class clazz = getTarget().getClass();
Method m = MethodUtils.getAccessibleMethod(clazz, "getLogger", new Class[] {String.class});
Object logger = m.invoke(getTarget(), new Object[] {name});
LogbackLoggerAccessor accessor = new LogbackLoggerAccessor();
accessor.setTarget(logger);
accessor.setApplication(getApplication());
return accessor;
} catch (Exception e) {
log.error(getTarget() + ".getLogger(\"" + name + "\") failed", e);
return null;
}
}
/**
* Returns a list of wrappers for all Logback appenders that have an
* associated logger.
*
* @return a list of {@link LogbackAppenderAccessor}s representing all
* appenders that are in use
*/
public List getAppenders() {
List appenders = new ArrayList();
try {
Class clazz = getTarget().getClass();
Method m = MethodUtils.getAccessibleMethod(clazz, "getLoggerList", new Class[]{});
List loggers = (List) m.invoke(getTarget(), null);
Iterator it = loggers.iterator();
while (it.hasNext()) {
LogbackLoggerAccessor accessor = new LogbackLoggerAccessor();
accessor.setTarget(it.next());
accessor.setApplication(getApplication());
appenders.addAll(accessor.getAppenders());
}
} catch (Exception e) {
log.error(getTarget() + ".getLoggerList() failed", e);
}
return appenders;
}
}