001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.common.logging;
017
018import org.modeshape.common.CommonI18n;
019import org.modeshape.common.logging.jdk.JdkLoggerFactory;
020import org.modeshape.common.logging.log4j.Log4jLoggerFactory;
021import org.modeshape.common.logging.slf4j.SLF4JLoggerFactory;
022
023/**
024 * The abstract class for the LogFactory, which is called to create a specific implementation of the {@link Logger}.
025 * <p>
026 * ModeShape provides out of the box several LogFactory implementations that work with common log frameworks:
027 * <ol>
028 * <li>SLF4J (which sits atop several logging frameworks)</li>
029 * <li>Log4J</li>
030 * <li>JDK Util Logging</li>
031 * </ol>
032 * The static initializer for this class checks the classpath for the availability of these frameworks, and as soon as one is
033 * found the LogFactory implementation for that framework is instantiated and used for all ModeShape logging.
034 * </p>
035 * <p>
036 * However, since ModeShape can be embedded into any application, it is possible that applications use a logging framework other
037 * than those listed above. So before falling back to the JDK logging, ModeShape looks for the
038 * <code>org.modeshape.common.logging.CustomLoggerFactory</code> class, and if found attempts to instantiate and use it. But
039 * ModeShape does not provide this class out of the box; rather an application that is embedding ModeShape can provide its own
040 * version of that class that should extend {@link LogFactory} and create an appropriate implementation of {@link Logger} that
041 * forwards ModeShape log messages to the application's logging framework.
042 * </p>
043 */
044public abstract class LogFactory {
045
046    /**
047     * The name of the {@link LogFactory} implementation that is not provided out of the box but can be created, implemented, and
048     * placed on the classpath to have ModeShape send log messages to a custom framework.
049     */
050    public static final String CUSTOM_LOG_FACTORY_CLASSNAME = "org.modeshape.common.logging.CustomLoggerFactory";
051
052    private static LogFactory LOGFACTORY;
053
054    static {
055        Throwable customLoggingError = null;
056        boolean customLogging = false;
057        boolean slf4jLogging = false;
058        boolean log4jLogging = false;
059
060        if (isCustomLoggerAvailable()) {
061            try {
062                @SuppressWarnings( "unchecked" )
063                Class<LogFactory> customClass = (Class<LogFactory>)Class.forName(CUSTOM_LOG_FACTORY_CLASSNAME);
064                LOGFACTORY = customClass.newInstance();
065                customLogging = true;
066            } catch (Throwable throwable) {
067                customLoggingError = throwable;
068            }
069        }
070
071        if (LOGFACTORY == null) {
072            if (isSLF4JAvailable()) {
073                LOGFACTORY = new SLF4JLoggerFactory();
074                slf4jLogging = true;
075            } else if (isLog4jAvailable()) {
076                LOGFACTORY = new Log4jLoggerFactory();
077                log4jLogging = true;
078            } else {
079                LOGFACTORY = new JdkLoggerFactory();
080            }
081        }
082
083        Logger logger = LOGFACTORY.getLogger(LogFactory.class.getName());
084
085        if (customLogging) {
086            logger.info(CommonI18n.customLoggingAvailable, CUSTOM_LOG_FACTORY_CLASSNAME);
087        } else if (slf4jLogging) {
088            logger.info(CommonI18n.slf4jAvailable);
089        } else if (log4jLogging) {
090            logger.info(CommonI18n.log4jAvailable);
091        } else {
092            logger.info(CommonI18n.jdkFallback);
093        }
094
095        if (customLoggingError != null) {
096            // log the problem related to the custom logger
097            logger.warn(customLoggingError,
098                        CommonI18n.errorInitializingCustomLoggerFactory,
099                        CUSTOM_LOG_FACTORY_CLASSNAME);
100        }
101    }
102
103    private static boolean isSLF4JAvailable() {
104        try {
105            // check if the api is in the classpath and initialize the classes
106            Class.forName("org.slf4j.Logger");
107            Class.forName("org.slf4j.LoggerFactory");
108
109            // check if there's at least one implementation and initialize the classes
110            Class.forName("org.slf4j.impl.StaticLoggerBinder");
111            return true;
112        } catch (ClassNotFoundException e) {
113            return false;
114        }
115    }
116
117    private static boolean isLog4jAvailable() {
118        try {
119            // Check if the Log4J main interface is in the classpath and initialize the class
120            Class.forName("org.apache.log4j.Logger");
121            return true;
122        } catch (ClassNotFoundException e) {
123            return false;
124        }
125    }
126
127    private static boolean isCustomLoggerAvailable() {
128        try {
129            // Check if a custom log factory implementation is in the classpath and initialize the class
130            Class.forName(CUSTOM_LOG_FACTORY_CLASSNAME);
131            return true;
132        } catch (ClassNotFoundException e) {
133            return false;
134        }
135    }
136
137    static LogFactory getLogFactory() {
138        return LOGFACTORY;
139    }
140
141    /**
142     * Return a logger named corresponding to the class passed as parameter.
143     * 
144     * @param clazz the returned logger will be named after clazz
145     * @return logger
146     */
147    Logger getLogger( Class<?> clazz ) {
148        return Logger.getLogger(clazz.getName());
149    }
150
151    /**
152     * Return a logger named according to the name parameter.
153     * 
154     * @param name The name of the logger.
155     * @return logger
156     */
157    protected abstract Logger getLogger( String name );
158
159}