    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements. See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache license, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License. You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the license for the specific language governing permissions and
    015     * limitations under the license.
    016     */
    017    package org.apache.logging.log4j;
    018
    019    import java.net.URI;
    020    import java.util.Map;
    021    import java.util.SortedMap;
    022    import java.util.TreeMap;
    023
    024    import org.apache.logging.log4j.message.MessageFactory;
    025    import org.apache.logging.log4j.message.StringFormatterMessageFactory;
    026    import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
    027    import org.apache.logging.log4j.spi.LoggerContext;
    028    import org.apache.logging.log4j.spi.LoggerContextFactory;
    029    import org.apache.logging.log4j.spi.Provider;
    030    import org.apache.logging.log4j.status.StatusLogger;
    031    import org.apache.logging.log4j.util.LoaderUtil;
    032    import org.apache.logging.log4j.util.PropertiesUtil;
    033    import org.apache.logging.log4j.util.ProviderUtil;
    034    import org.apache.logging.log4j.util.ReflectionUtil;
    035    import org.apache.logging.log4j.util.Strings;
    036
    037    /**
    038     * The anchor point for the logging system. The most common usage of this class is to obtain a named
    039     * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger
    040     * based on the calling class name. This class also provides method for obtaining named Loggers that use
    041     * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages.
    042     * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods
    043     * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not
    044     * normally useful for typical usage of Log4j.
    045     */
    046    public class LogManager {
    047
    048        private static volatile LoggerContextFactory factory;
    049
    050        /**
    051         * Log4j property to set to the fully qualified class name of a custom implementation of
    052         * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
    053         */
    054        public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
    055
    056        private static final Logger LOGGER = StatusLogger.getLogger();
    057
    058        /**
    059         * The name of the root Logger.
    060         */
    061        public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
    062
    063        // for convenience
    064        private static final String FQCN = LogManager.class.getName();
    065
    066        /**
    067         * Scans the classpath to find all logging implementation. Currently, only one will
    068         * be used but this could be extended to allow multiple implementations to be used.
    069         */
    070        static {
    071            // Shortcut binding to force a specific logging implementation.
    072            final PropertiesUtil managerProps = PropertiesUtil.getProperties();
    073            final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
    074            if (factoryClassName != null) {
    075                try {
    076                    final Class<?> clazz = LoaderUtil.loadClass(factoryClassName);
    077                    if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
    078                        factory = clazz.asSubclass(LoggerContextFactory.class).newInstance();
    079                    }
    080                } catch (final ClassNotFoundException cnfe) {
    081                    LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
    082                } catch (final Exception ex) {
    083                    LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
    084                }
    085            }
    086
    087            if (factory == null) {
    088                final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
    089                // note that the following initial call to ProviderUtil may block until a Provider has been installed when
    090                // running in an OSGi environment
    091                if (ProviderUtil.hasProviders()) {
    092                    for (final Provider provider : ProviderUtil.getProviders()) {
    093                        final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
    094                        if (factoryClass != null) {
    095                            try {
    096                                factories.put(provider.getPriority(), factoryClass.newInstance());
    097                            } catch (final Exception e) {
    098                                LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
    099                                    provider.getUrl().toString(), e);
    100                            }
    101                        }
    102                    }
    103
    104                    if (factories.isEmpty()) {
    105                        LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
    106                        factory = new SimpleLoggerContextFactory();
    107                    } else {
    108                        final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
    109                        for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
    110                            sb.append("Factory: ").append(entry.getValue().getClass().getName());
    111                            sb.append(", Weighting: ").append(entry.getKey()).append('\n');
    112                        }
    113                        factory = factories.get(factories.lastKey());
    114                        sb.append("Using factory: ").append(factory.getClass().getName());
    115                        LOGGER.warn(sb.toString());
    116
    117                    }
    118                } else {
    119                    LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
    120                    factory = new SimpleLoggerContextFactory();
    121                }
    122            }
    123        }
    124
    125        /**
    126         * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
    127         *
    128         * @param name
    129         *            The Logger name to search for.
    130         * @return true if the Logger exists, false otherwise.
    131         * @see LoggerContext#hasLogger(String)
    132         */
    133        public static boolean exists(final String name) {
    134            return getContext().hasLogger(name);
    135        }
    136
    137        /**
    138         * Returns the current LoggerContext.
    139         * <p>
    140         * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
    141         * for the calling class.
    142         * </p>
    143         * @return  The current LoggerContext.
    144         */
    145        public static LoggerContext getContext() {
    146            return factory.getContext(FQCN, null, null, true);
    147        }
    148
    149        /**
    150         * Returns a LoggerContext.
    151         *
    152         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    153         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    154         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    155         * returned. If true then only a single LoggerContext will be returned.
    156         * @return a LoggerContext.
    157         */
    158        public static LoggerContext getContext(final boolean currentContext) {
    159            // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
    160            return factory.getContext(FQCN, null, null, currentContext, null, null);
    161        }
    162
    163        /**
    164         * Returns a LoggerContext.
    165         *
    166         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    167         * ClassLoader.
    168         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    169         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    170         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    171         * returned. If true then only a single LoggerContext will be returned.
    172         * @return a LoggerContext.
    173         */
    174        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
    175            return factory.getContext(FQCN, loader, null, currentContext);
    176        }
    177
    178        /**
    179         * Returns a LoggerContext.
    180         *
    181         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    182         * ClassLoader.
    183         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    184         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    185         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    186         * returned. If true then only a single LoggerContext will be returned.
    187         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    188         * @return a LoggerContext.
    189         */
    190        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    191                                               final Object externalContext) {
    192            return factory.getContext(FQCN, loader, externalContext, currentContext);
    193        }
    194
    195        /**
    196         * Returns a LoggerContext.
    197         *
    198         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    199         * ClassLoader.
    200         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    201         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    202         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    203         * returned. If true then only a single LoggerContext will be returned.
    204         * @param configLocation The URI for the configuration to use.
    205         * @return a LoggerContext.
    206         */
    207        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    208                                               final URI configLocation) {
    209            return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
    210        }
    211
    212
    213        /**
    214         * Returns a LoggerContext.
    215         *
    216         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    217         * ClassLoader.
    218         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    219         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    220         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    221         * returned. If true then only a single LoggerContext will be returned.
    222         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    223         * @param configLocation The URI for the configuration to use.
    224         * @return a LoggerContext.
    225         */
    226        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    227                                               final Object externalContext, final URI configLocation) {
    228            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
    229        }
    230
    231
    232        /**
    233         * Returns a LoggerContext.
    234         *
    235         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    236         * ClassLoader.
    237         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    238         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    239         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    240         * returned. If true then only a single LoggerContext will be returned.
    241         * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
    242         * @param configLocation The URI for the configuration to use.
    243         * @param name The LoggerContext name.
    244         * @return a LoggerContext.
    245         */
    246        public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
    247                                               final Object externalContext, final URI configLocation,
    248                                               final String name) {
    249            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
    250        }
    251
    252        /**
    253         * Returns a LoggerContext
    254         * @param fqcn The fully qualified class name of the Class that this method is a member of.
    255         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    256         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    257         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    258         * returned. If true then only a single LoggerContext will be returned.
    259         * @return a LoggerContext.
    260         */
    261        protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
    262            return factory.getContext(fqcn, null, null, currentContext);
    263        }
    264
    265        /**
    266         * Returns a LoggerContext
    267         * @param fqcn The fully qualified class name of the Class that this method is a member of.
    268         * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
    269         * ClassLoader.
    270         * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
    271         * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
    272         * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
    273         * returned. If true then only a single LoggerContext will be returned.
    274         * @return a LoggerContext.
    275         */
    276        protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
    277                                                  final boolean currentContext) {
    278            return factory.getContext(fqcn, loader, null, currentContext);
    279        }
    280
    281        /**
    282         * Returns the current LoggerContextFactory.
    283         * @return The LoggerContextFactory.
    284         */
    285        public static LoggerContextFactory getFactory() {
    286            return factory;
    287        }
    288
    289        /**
    290         * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
    291         * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
    292         * Thus, an alternative LoggerContextFactory can be set at runtime.
    293         *
    294         * <p>
    295         * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
    296         * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
    297         * Generally, this method should be used only during startup before any code starts caching Logger objects.
    298         * </p>
    299         *
    300         * @param factory the LoggerContextFactory to use.
    301         */
    302        // FIXME: should we allow only one update of the factory?
    303        public static void setFactory(final LoggerContextFactory factory) {
    304            LogManager.factory = factory;
    305        }
    306
    307        /**
    308         * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
    309         * <p>
    310         * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    311         * </p>
    312         * <p>
    313         * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
    314         * </p>
    315         *
    316         * @param clazz
    317         *            The Class whose name should be used as the Logger name.
    318         * @return The Logger, created with a {@link StringFormatterMessageFactory}
    319         * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    320         * @see Logger#fatal(Marker, String, Object...)
    321         * @see Logger#fatal(String, Object...)
    322         * @see Logger#error(Marker, String, Object...)
    323         * @see Logger#error(String, Object...)
    324         * @see Logger#warn(Marker, String, Object...)
    325         * @see Logger#warn(String, Object...)
    326         * @see Logger#info(Marker, String, Object...)
    327         * @see Logger#info(String, Object...)
    328         * @see Logger#debug(Marker, String, Object...)
    329         * @see Logger#debug(String, Object...)
    330         * @see Logger#trace(Marker, String, Object...)
    331         * @see Logger#trace(String, Object...)
    332         * @see StringFormatterMessageFactory
    333         */
    334        public static Logger getFormatterLogger(final Class<?> clazz) {
    335            return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2),
    336                StringFormatterMessageFactory.INSTANCE);
    337        }
    338
    339        /**
    340         * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
    341         * <p>
    342         * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    343         * </p>
    344         * <p>
    345         * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
    346         * </p>
    347         *
    348         * @param value
    349         *            The value's whose class name should be used as the Logger name.
    350         * @return The Logger, created with a {@link StringFormatterMessageFactory}
    351         * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    352         * @see Logger#fatal(Marker, String, Object...)
    353         * @see Logger#fatal(String, Object...)
    354         * @see Logger#error(Marker, String, Object...)
    355         * @see Logger#error(String, Object...)
    356         * @see Logger#warn(Marker, String, Object...)
    357         * @see Logger#warn(String, Object...)
    358         * @see Logger#info(Marker, String, Object...)
    359         * @see Logger#info(String, Object...)
    360         * @see Logger#debug(Marker, String, Object...)
    361         * @see Logger#debug(String, Object...)
    362         * @see Logger#trace(Marker, String, Object...)
    363         * @see Logger#trace(String, Object...)
    364         * @see StringFormatterMessageFactory
    365         */
    366        public static Logger getFormatterLogger(final Object value) {
    367            return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2),
    368                StringFormatterMessageFactory.INSTANCE);
    369        }
    370
    371        /**
    372         * Returns a formatter Logger with the specified name.
    373         * <p>
    374         * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
    375         * </p>
    376         * <p>
    377         * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
    378         * </p>
    379         *
    380         * @param name The logger name. If null it will default to the name of the calling class.
    381         * @return The Logger, created with a {@link StringFormatterMessageFactory}
    382         * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    383         * @see Logger#fatal(Marker, String, Object...)
    384         * @see Logger#fatal(String, Object...)
    385         * @see Logger#error(Marker, String, Object...)
    386         * @see Logger#error(String, Object...)
    387         * @see Logger#warn(Marker, String, Object...)
    388         * @see Logger#warn(String, Object...)
    389         * @see Logger#info(Marker, String, Object...)
    390         * @see Logger#info(String, Object...)
    391         * @see Logger#debug(Marker, String, Object...)
    392         * @see Logger#debug(String, Object...)
    393         * @see Logger#trace(Marker, String, Object...)
    394         * @see Logger#trace(String, Object...)
    395         * @see StringFormatterMessageFactory
    396         */
    397        public static Logger getFormatterLogger(final String name) {
    398            return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name,
    399                StringFormatterMessageFactory.INSTANCE);
    400        }
    401
    402        /**
    403         * Returns a Logger with the name of the calling class.
    404         * @return The Logger for the calling class.
    405         * @throws UnsupportedOperationException if the calling class cannot be determined.
    406         */
    407        public static Logger getLogger() {
    408            return getLogger(ReflectionUtil.getCallerClass(2));
    409        }
    410
    411        /**
    412         * Returns a Logger using the fully qualified name of the Class as the Logger name.
    413         * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
    414         *              class.
    415         * @return The Logger.
    416         * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    417         */
    418        public static Logger getLogger(final Class<?> clazz) {
    419            if (clazz == null) {
    420                final Class<?> candidate = ReflectionUtil.getCallerClass(2);
    421                if (candidate == null) {
    422                    throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
    423                }
    424                return getLogger(candidate);
    425            }
    426            return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName());
    427        }
    428
    429        /**
    430         * Returns a Logger using the fully qualified name of the Class as the Logger name.
    431         * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
    432         *              class.
    433         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    434         *                       the logger but will log a warning if mismatched.
    435         * @return The Logger.
    436         * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
    437         */
    438        public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
    439            if (clazz == null) {
    440                final Class<?> candidate = ReflectionUtil.getCallerClass(2);
    441                if (candidate == null) {
    442                    throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
    443                }
    444                return getLogger(candidate, messageFactory);
    445            }
    446            return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory);
    447        }
    448
    449        /**
    450         * Returns a Logger with the name of the calling class.
    451         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    452         *                       the logger but will log a warning if mismatched.
    453         * @return The Logger for the calling class.
    454         * @throws UnsupportedOperationException if the calling class cannot be determined.
    455         */
    456        public static Logger getLogger(final MessageFactory messageFactory) {
    457            return getLogger(ReflectionUtil.getCallerClass(2), messageFactory);
    458        }
    459
    460        /**
    461         * Returns a Logger using the fully qualified class name of the value as the Logger name.
    462         * @param value The value whose class name should be used as the Logger name. If null the name of the calling
    463         *              class will be used as the logger name.
    464         * @return The Logger.
    465         * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    466         */
    467        public static Logger getLogger(final Object value) {
    468            return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2));
    469        }
    470
    471        /**
    472         * Returns a Logger using the fully qualified class name of the value as the Logger name.
    473         * @param value The value whose class name should be used as the Logger name. If null the name of the calling
    474         *              class will be used as the logger name.
    475         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    476         *                       the logger but will log a warning if mismatched.
    477         * @return The Logger.
    478         * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
    479         */
    480        public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
    481            return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory);
    482        }
    483
    484        /**
    485         * Returns a Logger with the specified name.
    486         *
    487         * @param name The logger name. If null the name of the calling class will be used.
    488         * @return The Logger.
    489         * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    490         */
    491        public static Logger getLogger(final String name) {
    492            return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2));
    493        }
    494
    495        /**
    496         * Returns a Logger with the specified name.
    497         *
    498         * @param name The logger name. If null the name of the calling class will be used.
    499         * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
    500         *                       the logger but will log a warning if mismatched.
    501         * @return The Logger.
    502         * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
    503         */
    504        public static Logger getLogger(final String name, final MessageFactory messageFactory) {
    505            return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
    506                ReflectionUtil.getCallerClass(2), messageFactory);
    507        }
    508
    509        /**
    510         * Returns a Logger with the specified name.
    511         *
    512         * @param fqcn The fully qualified class name of the class that this method is a member of.
    513         * @param name The logger name.
    514         * @return The Logger.
    515         */
    516        protected static Logger getLogger(final String fqcn, final String name) {
    517            return factory.getContext(fqcn, null, null, false).getLogger(name);
    518        }
    519
    520        /**
    521         * Returns the root logger.
    522         *
    523         * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
    524         */
    525        public static Logger getRootLogger() {
    526            return getLogger(ROOT_LOGGER_NAME);
    527        }
    528
    529        /**
    530         * Prevents instantiation
    531         */
    532        protected LogManager() {
    533        }
    534
    535    }