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    }