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