All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.slf4j.LoggerFactory Maven / Gradle / Ivy

There is a newer version: 2.1.0-alpha1
Show newest version
/**
 * Copyright (c) 2004-2011 QOS.ch
 * All rights reserved.
 *
 * Permission is hereby granted, free  of charge, to any person obtaining
 * a  copy  of this  software  and  associated  documentation files  (the
 * "Software"), to  deal in  the Software without  restriction, including
 * without limitation  the rights to  use, copy, modify,  merge, publish,
 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
 * permit persons to whom the Software  is furnished to do so, subject to
 * the following conditions:
 *
 * The  above  copyright  notice  and  this permission  notice  shall  be
 * included in all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package org.slf4j;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.event.SubstituteLoggingEvent;
import org.slf4j.helpers.NOP_FallbackServiceProvider;
import org.slf4j.helpers.Reporter;
import org.slf4j.helpers.SubstituteLogger;
import org.slf4j.helpers.SubstituteServiceProvider;
import org.slf4j.helpers.Util;
import org.slf4j.spi.SLF4JServiceProvider;

/**
 * The LoggerFactory is a utility class producing Loggers for
 * various logging APIs, e.g. logback, reload4j, log4j and JDK 1.4 logging.
 * Other implementations such as {@link org.slf4j.helpers.NOPLogger NOPLogger} and
 * SimpleLogger are also supported.
 *
 * 

LoggerFactory is essentially a wrapper around an * {@link ILoggerFactory} instance provided by a {@link SLF4JServiceProvider}. * *

* Please note that all methods in LoggerFactory are static. * * @author Alexander Dorokhine * @author Robert Elliot * @author Ceki Gülcü * */ public final class LoggerFactory { static final String CODES_PREFIX = "https://www.slf4j.org/codes.html"; static final String NO_PROVIDERS_URL = CODES_PREFIX + "#noProviders"; static final String IGNORED_BINDINGS_URL = CODES_PREFIX + "#ignoredBindings"; static final String MULTIPLE_BINDINGS_URL = CODES_PREFIX + "#multiple_bindings"; static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch"; static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger"; static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch"; static final String REPLAY_URL = CODES_PREFIX + "#replay"; static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit"; static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory in failed state. Original exception was thrown EARLIER. See also " + UNSUCCESSFUL_INIT_URL; static final String CONNECTED_WITH_MSG = "Connected with provider of type ["; /** * System property for explicitly setting the provider class. If set and the provider could be instantiated, * then the service loading mechanism will be bypassed. * * @since 2.0.9 */ static final public String PROVIDER_PROPERTY_KEY = "slf4j.provider"; static final int UNINITIALIZED = 0; static final int ONGOING_INITIALIZATION = 1; static final int FAILED_INITIALIZATION = 2; static final int SUCCESSFUL_INITIALIZATION = 3; static final int NOP_FALLBACK_INITIALIZATION = 4; static volatile int INITIALIZATION_STATE = UNINITIALIZED; static final SubstituteServiceProvider SUBST_PROVIDER = new SubstituteServiceProvider(); static final NOP_FallbackServiceProvider NOP_FALLBACK_SERVICE_PROVIDER = new NOP_FallbackServiceProvider(); // Support for detecting mismatched logger names. static final String DETECT_LOGGER_NAME_MISMATCH_PROPERTY = "slf4j.detectLoggerNameMismatch"; static final String JAVA_VENDOR_PROPERTY = "java.vendor.url"; static boolean DETECT_LOGGER_NAME_MISMATCH = Util.safeGetBooleanSystemProperty(DETECT_LOGGER_NAME_MISMATCH_PROPERTY); static volatile SLF4JServiceProvider PROVIDER; // Package access for tests static List findServiceProviders() { List providerList = new ArrayList<>(); // retain behaviour similar to that of 1.7 series and earlier. More specifically, use the class loader that // loaded the present class to search for services final ClassLoader classLoaderOfLoggerFactory = LoggerFactory.class.getClassLoader(); SLF4JServiceProvider explicitProvider = loadExplicitlySpecified(classLoaderOfLoggerFactory); if(explicitProvider != null) { providerList.add(explicitProvider); return providerList; } ServiceLoader serviceLoader = getServiceLoader(classLoaderOfLoggerFactory); Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { safelyInstantiate(providerList, iterator); } return providerList; } private static ServiceLoader getServiceLoader(final ClassLoader classLoaderOfLoggerFactory) { ServiceLoader serviceLoader; SecurityManager securityManager = System.getSecurityManager(); if(securityManager == null) { serviceLoader = ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory); } else { final PrivilegedAction> action = () -> ServiceLoader.load(SLF4JServiceProvider.class, classLoaderOfLoggerFactory); serviceLoader = AccessController.doPrivileged(action); } return serviceLoader; } private static void safelyInstantiate(List providerList, Iterator iterator) { try { SLF4JServiceProvider provider = iterator.next(); providerList.add(provider); } catch (ServiceConfigurationError e) { Reporter.error("A service provider failed to instantiate:\n" + e.getMessage()); } } /** * It is LoggerFactory's responsibility to track version changes and manage * the compatibility list. *

*/ static private final String[] API_COMPATIBILITY_LIST = new String[] { "2.0" }; // private constructor prevents instantiation private LoggerFactory() { } /** * Force LoggerFactory to consider itself uninitialized. *

*

* This method is intended to be called by classes (in the same package) for * testing purposes. This method is internal. It can be modified, renamed or * removed at any time without notice. *

*

* You are strongly discouraged from calling this method in production code. */ static void reset() { INITIALIZATION_STATE = UNINITIALIZED; } private final static void performInitialization() { bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { versionSanityCheck(); } } private final static void bind() { try { List providersList = findServiceProviders(); reportMultipleBindingAmbiguity(providersList); if (providersList != null && !providersList.isEmpty()) { PROVIDER = providersList.get(0); // SLF4JServiceProvider.initialize() is intended to be called here and nowhere else. PROVIDER.initialize(); INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION; reportActualBinding(providersList); } else { INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION; Reporter.warn("No SLF4J providers were found."); Reporter.warn("Defaulting to no-operation (NOP) logger implementation"); Reporter.warn("See " + NO_PROVIDERS_URL + " for further details."); Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet); } postBindCleanUp(); } catch (Exception e) { failedBinding(e); throw new IllegalStateException("Unexpected initialization failure", e); } } static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) { String explicitlySpecified = System.getProperty(PROVIDER_PROPERTY_KEY); if (null == explicitlySpecified || explicitlySpecified.isEmpty()) { return null; } try { String message = String.format("Attempting to load provider \"%s\" specified via \"%s\" system property", explicitlySpecified, PROVIDER_PROPERTY_KEY); Reporter.info(message); Class clazz = classLoader.loadClass(explicitlySpecified); Constructor constructor = clazz.getConstructor(); Object provider = constructor.newInstance(); return (SLF4JServiceProvider) provider; } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { String message = String.format("Failed to instantiate the specified SLF4JServiceProvider (%s)", explicitlySpecified); Reporter.error(message, e); return null; } catch (ClassCastException e) { String message = String.format("Specified SLF4JServiceProvider (%s) does not implement SLF4JServiceProvider interface", explicitlySpecified); Reporter.error(message, e); return null; } } private static void reportIgnoredStaticLoggerBinders(Set staticLoggerBinderPathSet) { if (staticLoggerBinderPathSet.isEmpty()) { return; } Reporter.warn("Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier."); for (URL path : staticLoggerBinderPathSet) { Reporter.warn("Ignoring binding found at [" + path + "]"); } Reporter.warn("See " + IGNORED_BINDINGS_URL + " for an explanation."); } // We need to use the name of the StaticLoggerBinder class, but we can't // reference the class itself. private static final String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; static Set findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order // during iteration Set staticLoggerBinderPathSet = new LinkedHashSet<>(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); Enumeration paths; if (loggerFactoryClassLoader == null) { paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); } else { paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); } while (paths.hasMoreElements()) { URL path = paths.nextElement(); staticLoggerBinderPathSet.add(path); } } catch (IOException ioe) { Reporter.error("Error getting resources from path", ioe); } return staticLoggerBinderPathSet; } private static void postBindCleanUp() { fixSubstituteLoggers(); replayEvents(); // release all resources in SUBST_FACTORY SUBST_PROVIDER.getSubstituteLoggerFactory().clear(); } private static void fixSubstituteLoggers() { synchronized (SUBST_PROVIDER) { SUBST_PROVIDER.getSubstituteLoggerFactory().postInitialization(); for (SubstituteLogger substLogger : SUBST_PROVIDER.getSubstituteLoggerFactory().getLoggers()) { Logger logger = getLogger(substLogger.getName()); substLogger.setDelegate(logger); } } } static void failedBinding(Throwable t) { INITIALIZATION_STATE = FAILED_INITIALIZATION; Reporter.error("Failed to instantiate SLF4J LoggerFactory", t); } private static void replayEvents() { final LinkedBlockingQueue queue = SUBST_PROVIDER.getSubstituteLoggerFactory().getEventQueue(); final int queueSize = queue.size(); int count = 0; final int maxDrain = 128; List eventList = new ArrayList<>(maxDrain); while (true) { int numDrained = queue.drainTo(eventList, maxDrain); if (numDrained == 0) break; for (SubstituteLoggingEvent event : eventList) { replaySingleEvent(event); if (count++ == 0) emitReplayOrSubstituionWarning(event, queueSize); } eventList.clear(); } } private static void emitReplayOrSubstituionWarning(SubstituteLoggingEvent event, int queueSize) { if (event.getLogger().isDelegateEventAware()) { emitReplayWarning(queueSize); } else if (event.getLogger().isDelegateNOP()) { // nothing to do } else { emitSubstitutionWarning(); } } private static void replaySingleEvent(SubstituteLoggingEvent event) { if (event == null) return; SubstituteLogger substLogger = event.getLogger(); String loggerName = substLogger.getName(); if (substLogger.isDelegateNull()) { throw new IllegalStateException("Delegate logger cannot be null at this state."); } if (substLogger.isDelegateNOP()) { // nothing to do } else if (substLogger.isDelegateEventAware()) { if(substLogger.isEnabledForLevel(event.getLevel())) { substLogger.log(event); } } else { Reporter.warn(loggerName); } } private static void emitSubstitutionWarning() { Reporter.warn("The following set of substitute loggers may have been accessed"); Reporter.warn("during the initialization phase. Logging calls during this"); Reporter.warn("phase were not honored. However, subsequent logging calls to these"); Reporter.warn("loggers will work as normally expected."); Reporter.warn("See also " + SUBSTITUTE_LOGGER_URL); } private static void emitReplayWarning(int eventCount) { Reporter.warn("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are"); Reporter.warn("now being replayed. These are subject to the filtering rules of the underlying logging system."); Reporter.warn("See also " + REPLAY_URL); } private final static void versionSanityCheck() { try { String requested = PROVIDER.getRequestedApiVersion(); boolean match = false; for (String aAPI_COMPATIBILITY_LIST : API_COMPATIBILITY_LIST) { if (requested.startsWith(aAPI_COMPATIBILITY_LIST)) { match = true; } } if (!match) { Reporter.warn("The requested version " + requested + " by your slf4j provider is not compatible with " + Arrays.asList(API_COMPATIBILITY_LIST).toString()); Reporter.warn("See " + VERSION_MISMATCH + " for further details."); } } catch (Throwable e) { // we should never reach here Reporter.error("Unexpected problem occurred during version sanity check", e); } } private static boolean isAmbiguousProviderList(List providerList) { return providerList.size() > 1; } /** * Prints a warning message on the console if multiple bindings were found * on the class path. No reporting is done otherwise. * */ private static void reportMultipleBindingAmbiguity(List providerList) { if (isAmbiguousProviderList(providerList)) { Reporter.warn("Class path contains multiple SLF4J providers."); for (SLF4JServiceProvider provider : providerList) { Reporter.warn("Found provider [" + provider + "]"); } Reporter.warn("See " + MULTIPLE_BINDINGS_URL + " for an explanation."); } } private static void reportActualBinding(List providerList) { // impossible since a provider has been found if (providerList.isEmpty()) { throw new IllegalStateException("No providers were found which is impossible after successful initialization."); } if (isAmbiguousProviderList(providerList)) { Reporter.info("Actual provider is of type [" + providerList.get(0) + "]"); } else { SLF4JServiceProvider provider = providerList.get(0); Reporter.debug(CONNECTED_WITH_MSG + provider.getClass().getName() + "]"); } } /** * Return a logger named according to the name parameter using the * statically bound {@link ILoggerFactory} instance. * * @param name * The name of the logger. * @return logger */ public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } /** * Return a logger named corresponding to the class passed as parameter, * using the statically bound {@link ILoggerFactory} instance. * *

* In case the clazz parameter differs from the name of the * caller as computed internally by SLF4J, a logger name mismatch warning * will be printed but only if the * slf4j.detectLoggerNameMismatch system property is set to * true. By default, this property is not set and no warnings will be * printed even in case of a logger name mismatch. * * @param clazz * the returned logger will be named after clazz * @return logger * * * @see Detected * logger name mismatch */ public static Logger getLogger(Class clazz) { Logger logger = getLogger(clazz.getName()); if (DETECT_LOGGER_NAME_MISMATCH) { Class autoComputedCallingClass = Util.getCallingClass(); if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) { Reporter.warn(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName())); Reporter.warn("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation"); } } return logger; } private static boolean nonMatchingClasses(Class clazz, Class autoComputedCallingClass) { return !autoComputedCallingClass.isAssignableFrom(clazz); } /** * Return the {@link ILoggerFactory} instance in use. *

*

* ILoggerFactory instance is bound with this class at compile time. * * @return the ILoggerFactory instance in use */ public static ILoggerFactory getILoggerFactory() { return getProvider().getLoggerFactory(); } /** * Return the {@link SLF4JServiceProvider} in use. * @return provider in use * @since 1.8.0 */ static SLF4JServiceProvider getProvider() { if (INITIALIZATION_STATE == UNINITIALIZED) { synchronized (LoggerFactory.class) { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; performInitialization(); } } } switch (INITIALIZATION_STATE) { case SUCCESSFUL_INITIALIZATION: return PROVIDER; case NOP_FALLBACK_INITIALIZATION: return NOP_FALLBACK_SERVICE_PROVIDER; case FAILED_INITIALIZATION: throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG); case ONGOING_INITIALIZATION: // support re-entrant behavior. // See also http://jira.qos.ch/browse/SLF4J-97 return SUBST_PROVIDER; } throw new IllegalStateException("Unreachable code"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy