/* * Chrysalix * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * See the AUTHORS.txt file in the distribution for a full listing of * individual contributors. * * Chrysalix is free software. Unless otherwise indicated, all code in Chrysalix * is licensed to you under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Chrysalix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.chrysalix.common; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.NotFoundException; import org.chrysalix.common.logging.JdkLoggerFactory; import org.chrysalix.common.logging.Log4jLoggerFactory; import org.chrysalix.common.logging.SLF4JLoggerFactory; /** * The abstract class for the LogFactory, which is called to create a specific implementation of the {@link Logger}. * <p> * Several LogFactory implementations are provided out-of-the-box that work with common log frameworks: * <ol> * <li>SLF4J (which sits atop several logging frameworks)</li> * <li>Log4J</li> * <li>JDK Util Logging</li> * </ol> * The static initializer for this class checks the classpath for the availability of these frameworks, and as soon as one is found * the LogFactory implementation for that framework is instantiated and used for all logging. * </p> * <p> * However, since this facility can be embedded into any application, it is possible that applications use a logging framework other * than those listed above. So before falling back to the JDK logging, this facility looks for the * <code>org.chrysalix.common.logging.CustomLoggerFactory</code> class, and if found attempts to instantiate and use it. But this * facility does not provide this class out of the box; rather an application that is embedding this facility can provide its own * version of that class that should extend {@link LogFactory} and create an appropriate implementation of {@link Logger} that * forwards log messages to the application's logging framework. * </p> */ public abstract class LogFactory { /** * The name of the {@link LogFactory} implementation that is not provided out of the box but can be created, implemented, and * placed on the classpath to have this facility send log messages to a custom framework. */ public static final String CUSTOM_LOG_FACTORY_CLASSNAME = "org.chrysalix.common.logging.CustomLoggerFactory"; private static LogFactory LOGFACTORY; static { if ( customLoggerAvailable() ) try { @SuppressWarnings( "unchecked" ) final Class< LogFactory > customClass = ( Class< LogFactory > ) Class.forName( CUSTOM_LOG_FACTORY_CLASSNAME ); LOGFACTORY = customClass.newInstance(); } catch ( final Throwable e ) { // We're going to fallback to the JDK logger anyway, so use it and log this problem ... LOGFACTORY = new JdkLoggerFactory(); final java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger( LogFactory.class.getName() ); final String msg = CommonI18n.localize( "Error loading and/or instantiating the \"%s\" implementation, which is used " + "to tie into a custom logging framework (other than SLF4J, Log4J or the JDK " + "Logging). Falling back to JDK logging.", CUSTOM_LOG_FACTORY_CLASSNAME ); jdkLogger.log( java.util.logging.Level.WARNING, msg, e ); } else if ( slf4jAvailable() ) LOGFACTORY = new SLF4JLoggerFactory(); else if ( log4jAvailable() ) LOGFACTORY = new Log4jLoggerFactory(); else LOGFACTORY = new JdkLoggerFactory(); } private static boolean customLoggerAvailable() { try { // Check if a custom log factory implementation is in the classpath and initialize the class Class.forName( CUSTOM_LOG_FACTORY_CLASSNAME ); return true; } catch ( final ClassNotFoundException e ) { return false; } } private static boolean log4jAvailable() { try { // Check if the Log4J main interface is in the classpath and initialize the class Class.forName( "org.apache.log4j.Logger" ); return true; } catch ( final ClassNotFoundException e ) { return false; } } static LogFactory logFactory() { return LOGFACTORY; } private static boolean slf4jAvailable() { try { // check if the api is in the classpath and initialize the classes Class.forName( "org.slf4j.Logger" ); Class.forName( "org.slf4j.LoggerFactory" ); // check if there's at least one implementation and initialize the classes Class.forName( "org.slf4j.impl.StaticLoggerBinder" ); return true; } catch ( final ClassNotFoundException e ) { return false; } } String context() { try { final StackTraceElement trace = Thread.currentThread().getStackTrace()[ 3 ]; final CtClass ctClass = ClassPool.getDefault().get( trace.getClassName() ); final CtConstructor classInitializer = ctClass.getClassInitializer(); if ( classInitializer != null && trace.getMethodName().equals( classInitializer.getName() ) ) return classInitializer.getLongName(); CtMethod ctMethod = null; int delta = Short.MAX_VALUE; // // jpav: remove // System.out.println( trace.getMethodName() ); // // jpav: remove // System.out.println( ctClass.getClassInitializer().getName() ); for ( final CtMethod method : ctClass.getDeclaredMethods() ) { final int methodDelta = trace.getLineNumber() - method.getMethodInfo().getLineNumber( 0 ); if ( methodDelta >= 0 && methodDelta < delta ) { delta = methodDelta; ctMethod = method; } } if ( ctMethod == null ) throw new RuntimeException(); return ctMethod.getLongName(); } catch ( final NotFoundException e ) { throw new RuntimeException( e ); } } /** * Return a logger for the context identified by the calling method * * @param i18nClass * the internationalization class used to localize logged messages * @return logger */ Logger logger( final Class< ? > i18nClass ) { return logger( i18nClass, context() ); } /** * Return a logger for the supplied context * * @param i18nClass * the internationalization class used to localize logged messages * @param context * The context of the logger. * @return logger */ protected abstract Logger logger( Class< ? > i18nClass, String context ); }