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