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