Crud, I'm not sure why the change is coming through like this as opposed to the few lines I changed.
Gary ---------- Forwarded message ---------- From: <[email protected]> Date: Mon, Jul 27, 2015 at 10:03 AM Subject: logging-log4j2 git commit: [LOG4J2-1084] Misleading StatusLogger WARN event in LogManager with java.util.Map. To: [email protected] Repository: logging-log4j2 Updated Branches: refs/heads/master 753b79df2 -> a60368c01 [LOG4J2-1084] Misleading StatusLogger WARN event in LogManager with java.util.Map. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/a60368c0 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/a60368c0 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/a60368c0 Branch: refs/heads/master Commit: a60368c015b343b37c3537cb67d594b547a61cd5 Parents: 753b79d Author: ggregory <[email protected]> Authored: Mon Jul 27 10:02:56 2015 -0700 Committer: ggregory <[email protected]> Committed: Mon Jul 27 10:02:56 2015 -0700 ---------------------------------------------------------------------- .../org/apache/logging/log4j/LogManager.java | 1100 +++++++++--------- src/changes/changes.xml | 5 +- 2 files changed, 555 insertions(+), 550 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a60368c0/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java ---------------------------------------------------------------------- diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java index d190543..d5ad911 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/LogManager.java @@ -1,549 +1,551 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j; - -import java.net.URI; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import org.apache.logging.log4j.message.MessageFactory; -import org.apache.logging.log4j.message.StringFormatterMessageFactory; -import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; -import org.apache.logging.log4j.spi.LoggerContext; -import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.apache.logging.log4j.spi.Provider; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.LoaderUtil; -import org.apache.logging.log4j.util.PropertiesUtil; -import org.apache.logging.log4j.util.ProviderUtil; -import org.apache.logging.log4j.util.ReflectionUtil; -import org.apache.logging.log4j.util.Strings; - -/** - * The anchor point for the logging system. The most common usage of this class is to obtain a named - * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger - * based on the calling class name. This class also provides method for obtaining named Loggers that use - * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. - * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods - * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not - * normally useful for typical usage of Log4j. - */ -public class LogManager { - - private static volatile LoggerContextFactory factory; - - /** - * Log4j property to set to the fully qualified class name of a custom implementation of - * {@link org.apache.logging.log4j.spi.LoggerContextFactory}. - */ - public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; - - private static final Logger LOGGER = StatusLogger.getLogger(); - - /** - * The name of the root Logger. - */ - public static final String ROOT_LOGGER_NAME = Strings.EMPTY; - - // for convenience - private static final String FQCN = LogManager.class.getName(); - - /** - * Scans the classpath to find all logging implementation. Currently, only one will - * be used but this could be extended to allow multiple implementations to be used. - */ - static { - // Shortcut binding to force a specific logging implementation. - final PropertiesUtil managerProps = PropertiesUtil.getProperties(); - final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); - if (factoryClassName != null) { - try { - final Class<?> clazz = LoaderUtil.loadClass(factoryClassName); - if (LoggerContextFactory.class.isAssignableFrom(clazz)) { - factory = clazz.asSubclass(LoggerContextFactory.class).newInstance(); - } - } catch (final ClassNotFoundException cnfe) { - LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName); - } catch (final Exception ex) { - LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex); - } - } - - if (factory == null) { - final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>(); - // note that the following initial call to ProviderUtil may block until a Provider has been installed when - // running in an OSGi environment - if (ProviderUtil.hasProviders()) { - for (final Provider provider : ProviderUtil.getProviders()) { - final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory(); - if (factoryClass != null) { - try { - factories.put(provider.getPriority(), factoryClass.newInstance()); - } catch (final Exception e) { - LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), - provider.getUrl().toString(), e); - } - } - } - - if (factories.isEmpty()) { - LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); - factory = new SimpleLoggerContextFactory(); - } else { - final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); - for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { - sb.append("Factory: ").append(entry.getValue().getClass().getName()); - sb.append(", Weighting: ").append(entry.getKey()).append('\n'); - } - factory = factories.get(factories.lastKey()); - sb.append("Using factory: ").append(factory.getClass().getName()); - LOGGER.warn(sb.toString()); - - } - } else { - LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); - factory = new SimpleLoggerContextFactory(); - } - } - } - - /** - * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. - * - * @param name - * The Logger name to search for. - * @return true if the Logger exists, false otherwise. - * @see LoggerContext#hasLogger(String) - */ - public static boolean exists(final String name) { - return getContext().hasLogger(name); - } - - /** - * Returns the current LoggerContext. - * <p> - * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger - * for the calling class. - * </p> - * @return The current LoggerContext. - */ - public static LoggerContext getContext() { - return factory.getContext(FQCN, null, null, true); - } - - /** - * Returns a LoggerContext. - * - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final boolean currentContext) { - // TODO: would it be a terrible idea to try and find the caller ClassLoader here? - return factory.getContext(FQCN, null, null, currentContext, null, null); - } - - /** - * Returns a LoggerContext. - * - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { - return factory.getContext(FQCN, loader, null, currentContext); - } - - /** - * Returns a LoggerContext. - * - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, - final Object externalContext) { - return factory.getContext(FQCN, loader, externalContext, currentContext); - } - - /** - * Returns a LoggerContext. - * - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @param configLocation The URI for the configuration to use. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, - final URI configLocation) { - return factory.getContext(FQCN, loader, null, currentContext, configLocation, null); - } - - - /** - * Returns a LoggerContext. - * - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. - * @param configLocation The URI for the configuration to use. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, - final Object externalContext, final URI configLocation) { - return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null); - } - - - /** - * Returns a LoggerContext. - * - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. - * @param configLocation The URI for the configuration to use. - * @param name The LoggerContext name. - * @return a LoggerContext. - */ - public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, - final Object externalContext, final URI configLocation, - final String name) { - return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name); - } - - /** - * Returns a LoggerContext - * @param fqcn The fully qualified class name of the Class that this method is a member of. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @return a LoggerContext. - */ - protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { - return factory.getContext(fqcn, null, null, currentContext); - } - - /** - * Returns a LoggerContext - * @param fqcn The fully qualified class name of the Class that this method is a member of. - * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate - * ClassLoader. - * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For - * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be - * returned and if the caller is a class in the container's classpath then a different LoggerContext may be - * returned. If true then only a single LoggerContext will be returned. - * @return a LoggerContext. - */ - protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, - final boolean currentContext) { - return factory.getContext(fqcn, loader, null, currentContext); - } - - /** - * Returns the current LoggerContextFactory. - * @return The LoggerContextFactory. - */ - public static LoggerContextFactory getFactory() { - return factory; - } - - /** - * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at - * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. - * Thus, an alternative LoggerContextFactory can be set at runtime. - * - * <p> - * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be - * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! - * Generally, this method should be used only during startup before any code starts caching Logger objects. - * </p> - * - * @param factory the LoggerContextFactory to use. - */ - // FIXME: should we allow only one update of the factory? - public static void setFactory(final LoggerContextFactory factory) { - LogManager.factory = factory; - } - - /** - * Returns a formatter Logger using the fully qualified name of the calling Class as the Logger name. - * <p> - * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters. - * </p> - * @return The Logger for the calling class. - * @throws UnsupportedOperationException if the calling class cannot be determined. - * @since 2.4 - */ - public static Logger getFormatterLogger() { - return getFormatterLogger(ReflectionUtil.getCallerClass(2)); - } - - - /** - * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. - * <p> - * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. - * </p> - * <p> - * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} - * </p> - * - * @param clazz - * The Class whose name should be used as the Logger name. - * @return The Logger, created with a {@link StringFormatterMessageFactory} - * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. - * @see Logger#fatal(Marker, String, Object...) - * @see Logger#fatal(String, Object...) - * @see Logger#error(Marker, String, Object...) - * @see Logger#error(String, Object...) - * @see Logger#warn(Marker, String, Object...) - * @see Logger#warn(String, Object...) - * @see Logger#info(Marker, String, Object...) - * @see Logger#info(String, Object...) - * @see Logger#debug(Marker, String, Object...) - * @see Logger#debug(String, Object...) - * @see Logger#trace(Marker, String, Object...) - * @see Logger#trace(String, Object...) - * @see StringFormatterMessageFactory - */ - public static Logger getFormatterLogger(final Class<?> clazz) { - return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2), - StringFormatterMessageFactory.INSTANCE); - } - - /** - * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. - * <p> - * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. - * </p> - * <p> - * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} - * </p> - * - * @param value - * The value's whose class name should be used as the Logger name. - * @return The Logger, created with a {@link StringFormatterMessageFactory} - * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. - * @see Logger#fatal(Marker, String, Object...) - * @see Logger#fatal(String, Object...) - * @see Logger#error(Marker, String, Object...) - * @see Logger#error(String, Object...) - * @see Logger#warn(Marker, String, Object...) - * @see Logger#warn(String, Object...) - * @see Logger#info(Marker, String, Object...) - * @see Logger#info(String, Object...) - * @see Logger#debug(Marker, String, Object...) - * @see Logger#debug(String, Object...) - * @see Logger#trace(Marker, String, Object...) - * @see Logger#trace(String, Object...) - * @see StringFormatterMessageFactory - */ - public static Logger getFormatterLogger(final Object value) { - return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), - StringFormatterMessageFactory.INSTANCE); - } - - /** - * Returns a formatter Logger with the specified name. - * <p> - * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. - * </p> - * <p> - * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} - * </p> - * - * @param name The logger name. If null it will default to the name of the calling class. - * @return The Logger, created with a {@link StringFormatterMessageFactory} - * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. - * @see Logger#fatal(Marker, String, Object...) - * @see Logger#fatal(String, Object...) - * @see Logger#error(Marker, String, Object...) - * @see Logger#error(String, Object...) - * @see Logger#warn(Marker, String, Object...) - * @see Logger#warn(String, Object...) - * @see Logger#info(Marker, String, Object...) - * @see Logger#info(String, Object...) - * @see Logger#debug(Marker, String, Object...) - * @see Logger#debug(String, Object...) - * @see Logger#trace(Marker, String, Object...) - * @see Logger#trace(String, Object...) - * @see StringFormatterMessageFactory - */ - public static Logger getFormatterLogger(final String name) { - return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name, - StringFormatterMessageFactory.INSTANCE); - } - - /** - * Returns a Logger with the name of the calling class. - * @return The Logger for the calling class. - * @throws UnsupportedOperationException if the calling class cannot be determined. - */ - public static Logger getLogger() { - return getLogger(ReflectionUtil.getCallerClass(2)); - } - - /** - * Returns a Logger using the fully qualified name of the Class as the Logger name. - * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling - * class. - * @return The Logger. - * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final Class<?> clazz) { - if (clazz == null) { - final Class<?> candidate = ReflectionUtil.getCallerClass(2); - if (candidate == null) { - throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); - } - return getLogger(candidate); - } - return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName()); - } - - /** - * Returns a Logger using the fully qualified name of the Class as the Logger name. - * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling - * class. - * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change - * the logger but will log a warning if mismatched. - * @return The Logger. - * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { - if (clazz == null) { - final Class<?> candidate = ReflectionUtil.getCallerClass(2); - if (candidate == null) { - throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); - } - return getLogger(candidate, messageFactory); - } - return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory); - } - - /** - * Returns a Logger with the name of the calling class. - * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change - * the logger but will log a warning if mismatched. - * @return The Logger for the calling class. - * @throws UnsupportedOperationException if the calling class cannot be determined. - */ - public static Logger getLogger(final MessageFactory messageFactory) { - return getLogger(ReflectionUtil.getCallerClass(2), messageFactory); - } - - /** - * Returns a Logger using the fully qualified class name of the value as the Logger name. - * @param value The value whose class name should be used as the Logger name. If null the name of the calling - * class will be used as the logger name. - * @return The Logger. - * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final Object value) { - return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2)); - } - - /** - * Returns a Logger using the fully qualified class name of the value as the Logger name. - * @param value The value whose class name should be used as the Logger name. If null the name of the calling - * class will be used as the logger name. - * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change - * the logger but will log a warning if mismatched. - * @return The Logger. - * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final Object value, final MessageFactory messageFactory) { - return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory); - } - - /** - * Returns a Logger with the specified name. - * - * @param name The logger name. If null the name of the calling class will be used. - * @return The Logger. - * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final String name) { - return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2)); - } - - /** - * Returns a Logger with the specified name. - * - * @param name The logger name. If null the name of the calling class will be used. - * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change - * the logger but will log a warning if mismatched. - * @return The Logger. - * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. - */ - public static Logger getLogger(final String name, final MessageFactory messageFactory) { - return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger( - ReflectionUtil.getCallerClass(2), messageFactory); - } - - /** - * Returns a Logger with the specified name. - * - * @param fqcn The fully qualified class name of the class that this method is a member of. - * @param name The logger name. - * @return The Logger. - */ - protected static Logger getLogger(final String fqcn, final String name) { - return factory.getContext(fqcn, null, null, false).getLogger(name); - } - - /** - * Returns the root logger. - * - * @return the root logger, named {@link #ROOT_LOGGER_NAME}. - */ - public static Logger getRootLogger() { - return getLogger(ROOT_LOGGER_NAME); - } - - /** - * Prevents instantiation - */ - protected LogManager() { - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j; + +import java.net.URI; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.message.StringFormatterMessageFactory; +import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; +import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.spi.Provider; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.ProviderUtil; +import org.apache.logging.log4j.util.ReflectionUtil; +import org.apache.logging.log4j.util.Strings; + +/** + * The anchor point for the logging system. The most common usage of this class is to obtain a named + * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger + * based on the calling class name. This class also provides method for obtaining named Loggers that use + * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. + * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods + * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not + * normally useful for typical usage of Log4j. + */ +public class LogManager { + + private static volatile LoggerContextFactory factory; + + /** + * Log4j property to set to the fully qualified class name of a custom implementation of + * {@link org.apache.logging.log4j.spi.LoggerContextFactory}. + */ + public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; + + private static final Logger LOGGER = StatusLogger.getLogger(); + + /** + * The name of the root Logger. + */ + public static final String ROOT_LOGGER_NAME = Strings.EMPTY; + + // for convenience + private static final String FQCN = LogManager.class.getName(); + + /** + * Scans the classpath to find all logging implementation. Currently, only one will + * be used but this could be extended to allow multiple implementations to be used. + */ + static { + // Shortcut binding to force a specific logging implementation. + final PropertiesUtil managerProps = PropertiesUtil.getProperties(); + final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); + if (factoryClassName != null) { + try { + final Class<?> clazz = LoaderUtil.loadClass(factoryClassName); + if (LoggerContextFactory.class.isAssignableFrom(clazz)) { + factory = clazz.asSubclass(LoggerContextFactory.class).newInstance(); + } + } catch (final ClassNotFoundException cnfe) { + LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName); + } catch (final Exception ex) { + LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex); + } + } + + if (factory == null) { + final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>(); + // note that the following initial call to ProviderUtil may block until a Provider has been installed when + // running in an OSGi environment + if (ProviderUtil.hasProviders()) { + for (final Provider provider : ProviderUtil.getProviders()) { + final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory(); + if (factoryClass != null) { + try { + factories.put(provider.getPriority(), factoryClass.newInstance()); + } catch (final Exception e) { + LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), + provider.getUrl().toString(), e); + } + } + } + + if (factories.isEmpty()) { + LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); + factory = new SimpleLoggerContextFactory(); + } else if (factories.size() == 1) { + factory = factories.get(factories.lastKey()); + } else { + final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); + for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { + sb.append("Factory: ").append(entry.getValue().getClass().getName()); + sb.append(", Weighting: ").append(entry.getKey()).append('\n'); + } + factory = factories.get(factories.lastKey()); + sb.append("Using factory: ").append(factory.getClass().getName()); + LOGGER.warn(sb.toString()); + + } + } else { + LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); + factory = new SimpleLoggerContextFactory(); + } + } + } + + /** + * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. + * + * @param name + * The Logger name to search for. + * @return true if the Logger exists, false otherwise. + * @see LoggerContext#hasLogger(String) + */ + public static boolean exists(final String name) { + return getContext().hasLogger(name); + } + + /** + * Returns the current LoggerContext. + * <p> + * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger + * for the calling class. + * </p> + * @return The current LoggerContext. + */ + public static LoggerContext getContext() { + return factory.getContext(FQCN, null, null, true); + } + + /** + * Returns a LoggerContext. + * + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final boolean currentContext) { + // TODO: would it be a terrible idea to try and find the caller ClassLoader here? + return factory.getContext(FQCN, null, null, currentContext, null, null); + } + + /** + * Returns a LoggerContext. + * + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { + return factory.getContext(FQCN, loader, null, currentContext); + } + + /** + * Returns a LoggerContext. + * + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, + final Object externalContext) { + return factory.getContext(FQCN, loader, externalContext, currentContext); + } + + /** + * Returns a LoggerContext. + * + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @param configLocation The URI for the configuration to use. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, + final URI configLocation) { + return factory.getContext(FQCN, loader, null, currentContext, configLocation, null); + } + + + /** + * Returns a LoggerContext. + * + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. + * @param configLocation The URI for the configuration to use. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, + final Object externalContext, final URI configLocation) { + return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null); + } + + + /** + * Returns a LoggerContext. + * + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. + * @param configLocation The URI for the configuration to use. + * @param name The LoggerContext name. + * @return a LoggerContext. + */ + public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, + final Object externalContext, final URI configLocation, + final String name) { + return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name); + } + + /** + * Returns a LoggerContext + * @param fqcn The fully qualified class name of the Class that this method is a member of. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @return a LoggerContext. + */ + protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { + return factory.getContext(fqcn, null, null, currentContext); + } + + /** + * Returns a LoggerContext + * @param fqcn The fully qualified class name of the Class that this method is a member of. + * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate + * ClassLoader. + * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For + * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be + * returned and if the caller is a class in the container's classpath then a different LoggerContext may be + * returned. If true then only a single LoggerContext will be returned. + * @return a LoggerContext. + */ + protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, + final boolean currentContext) { + return factory.getContext(fqcn, loader, null, currentContext); + } + + /** + * Returns the current LoggerContextFactory. + * @return The LoggerContextFactory. + */ + public static LoggerContextFactory getFactory() { + return factory; + } + + /** + * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at + * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. + * Thus, an alternative LoggerContextFactory can be set at runtime. + * + * <p> + * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be + * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! + * Generally, this method should be used only during startup before any code starts caching Logger objects. + * </p> + * + * @param factory the LoggerContextFactory to use. + */ + // FIXME: should we allow only one update of the factory? + public static void setFactory(final LoggerContextFactory factory) { + LogManager.factory = factory; + } + + /** + * Returns a formatter Logger using the fully qualified name of the calling Class as the Logger name. + * <p> + * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters. + * </p> + * @return The Logger for the calling class. + * @throws UnsupportedOperationException if the calling class cannot be determined. + * @since 2.4 + */ + public static Logger getFormatterLogger() { + return getFormatterLogger(ReflectionUtil.getCallerClass(2)); + } + + + /** + * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. + * <p> + * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. + * </p> + * <p> + * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} + * </p> + * + * @param clazz + * The Class whose name should be used as the Logger name. + * @return The Logger, created with a {@link StringFormatterMessageFactory} + * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. + * @see Logger#fatal(Marker, String, Object...) + * @see Logger#fatal(String, Object...) + * @see Logger#error(Marker, String, Object...) + * @see Logger#error(String, Object...) + * @see Logger#warn(Marker, String, Object...) + * @see Logger#warn(String, Object...) + * @see Logger#info(Marker, String, Object...) + * @see Logger#info(String, Object...) + * @see Logger#debug(Marker, String, Object...) + * @see Logger#debug(String, Object...) + * @see Logger#trace(Marker, String, Object...) + * @see Logger#trace(String, Object...) + * @see StringFormatterMessageFactory + */ + public static Logger getFormatterLogger(final Class<?> clazz) { + return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2), + StringFormatterMessageFactory.INSTANCE); + } + + /** + * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. + * <p> + * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. + * </p> + * <p> + * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} + * </p> + * + * @param value + * The value's whose class name should be used as the Logger name. + * @return The Logger, created with a {@link StringFormatterMessageFactory} + * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. + * @see Logger#fatal(Marker, String, Object...) + * @see Logger#fatal(String, Object...) + * @see Logger#error(Marker, String, Object...) + * @see Logger#error(String, Object...) + * @see Logger#warn(Marker, String, Object...) + * @see Logger#warn(String, Object...) + * @see Logger#info(Marker, String, Object...) + * @see Logger#info(String, Object...) + * @see Logger#debug(Marker, String, Object...) + * @see Logger#debug(String, Object...) + * @see Logger#trace(Marker, String, Object...) + * @see Logger#trace(String, Object...) + * @see StringFormatterMessageFactory + */ + public static Logger getFormatterLogger(final Object value) { + return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), + StringFormatterMessageFactory.INSTANCE); + } + + /** + * Returns a formatter Logger with the specified name. + * <p> + * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. + * </p> + * <p> + * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} + * </p> + * + * @param name The logger name. If null it will default to the name of the calling class. + * @return The Logger, created with a {@link StringFormatterMessageFactory} + * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. + * @see Logger#fatal(Marker, String, Object...) + * @see Logger#fatal(String, Object...) + * @see Logger#error(Marker, String, Object...) + * @see Logger#error(String, Object...) + * @see Logger#warn(Marker, String, Object...) + * @see Logger#warn(String, Object...) + * @see Logger#info(Marker, String, Object...) + * @see Logger#info(String, Object...) + * @see Logger#debug(Marker, String, Object...) + * @see Logger#debug(String, Object...) + * @see Logger#trace(Marker, String, Object...) + * @see Logger#trace(String, Object...) + * @see StringFormatterMessageFactory + */ + public static Logger getFormatterLogger(final String name) { + return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name, + StringFormatterMessageFactory.INSTANCE); + } + + /** + * Returns a Logger with the name of the calling class. + * @return The Logger for the calling class. + * @throws UnsupportedOperationException if the calling class cannot be determined. + */ + public static Logger getLogger() { + return getLogger(ReflectionUtil.getCallerClass(2)); + } + + /** + * Returns a Logger using the fully qualified name of the Class as the Logger name. + * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling + * class. + * @return The Logger. + * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final Class<?> clazz) { + if (clazz == null) { + final Class<?> candidate = ReflectionUtil.getCallerClass(2); + if (candidate == null) { + throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); + } + return getLogger(candidate); + } + return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName()); + } + + /** + * Returns a Logger using the fully qualified name of the Class as the Logger name. + * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling + * class. + * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change + * the logger but will log a warning if mismatched. + * @return The Logger. + * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { + if (clazz == null) { + final Class<?> candidate = ReflectionUtil.getCallerClass(2); + if (candidate == null) { + throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); + } + return getLogger(candidate, messageFactory); + } + return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory); + } + + /** + * Returns a Logger with the name of the calling class. + * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change + * the logger but will log a warning if mismatched. + * @return The Logger for the calling class. + * @throws UnsupportedOperationException if the calling class cannot be determined. + */ + public static Logger getLogger(final MessageFactory messageFactory) { + return getLogger(ReflectionUtil.getCallerClass(2), messageFactory); + } + + /** + * Returns a Logger using the fully qualified class name of the value as the Logger name. + * @param value The value whose class name should be used as the Logger name. If null the name of the calling + * class will be used as the logger name. + * @return The Logger. + * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final Object value) { + return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2)); + } + + /** + * Returns a Logger using the fully qualified class name of the value as the Logger name. + * @param value The value whose class name should be used as the Logger name. If null the name of the calling + * class will be used as the logger name. + * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change + * the logger but will log a warning if mismatched. + * @return The Logger. + * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final Object value, final MessageFactory messageFactory) { + return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory); + } + + /** + * Returns a Logger with the specified name. + * + * @param name The logger name. If null the name of the calling class will be used. + * @return The Logger. + * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final String name) { + return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2)); + } + + /** + * Returns a Logger with the specified name. + * + * @param name The logger name. If null the name of the calling class will be used. + * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change + * the logger but will log a warning if mismatched. + * @return The Logger. + * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. + */ + public static Logger getLogger(final String name, final MessageFactory messageFactory) { + return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger( + ReflectionUtil.getCallerClass(2), messageFactory); + } + + /** + * Returns a Logger with the specified name. + * + * @param fqcn The fully qualified class name of the class that this method is a member of. + * @param name The logger name. + * @return The Logger. + */ + protected static Logger getLogger(final String fqcn, final String name) { + return factory.getContext(fqcn, null, null, false).getLogger(name); + } + + /** + * Returns the root logger. + * + * @return the root logger, named {@link #ROOT_LOGGER_NAME}. + */ + public static Logger getRootLogger() { + return getLogger(ROOT_LOGGER_NAME); + } + + /** + * Prevents instantiation + */ + protected LogManager() { + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/a60368c0/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 6114e94..35bfbd1 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -50,7 +50,10 @@ </action> <action issue="LOG4J2-1066" dev="ggregory" type="add" due-to="Charles Allen"> Expose Log4jContextFactory's ShutdownCallbackRegistry. - </action> + </action> + <action issue="LOG4J2-1084" dev="ggregory" type="fix" due-to="Philipp Schneider"> + Misleading StatusLogger WARN event in LogManager with java.util.Map. + </action> <action issue="LOG4J2-1051" dev="ggregory" type="fix" due-to="Lukasz Lenart"> NoClassDefFoundError when starting app on Google App Engine. </action> -- E-Mail: [email protected] | [email protected] Java Persistence with Hibernate, Second Edition <http://www.manning.com/bauer3/> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> Spring Batch in Action <http://www.manning.com/templier/> Blog: http://garygregory.wordpress.com Home: http://garygregory.com/ Tweet! http://twitter.com/GaryGregory
