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 */
017package org.apache.logging.log4j;
018
019import java.net.URI;
020import java.util.Map;
021import java.util.SortedMap;
022import java.util.TreeMap;
023
024import org.apache.logging.log4j.message.MessageFactory;
025import org.apache.logging.log4j.message.StringFormatterMessageFactory;
026import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
027import org.apache.logging.log4j.spi.LoggerContext;
028import org.apache.logging.log4j.spi.LoggerContextFactory;
029import org.apache.logging.log4j.spi.Provider;
030import org.apache.logging.log4j.status.StatusLogger;
031import org.apache.logging.log4j.util.LoaderUtil;
032import org.apache.logging.log4j.util.PropertiesUtil;
033import org.apache.logging.log4j.util.ProviderUtil;
034import org.apache.logging.log4j.util.ReflectionUtil;
035import 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 {@link Logger}. The
039 * method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based on the calling
040 * 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. These
042 * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are
043 * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally
044 * useful for typical usage of Log4j.
045 */
046public class LogManager {
047
048    /**
049     * Log4j property to set to the fully qualified class name of a custom implementation of
050     * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
051     */
052    public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
053
054    /**
055     * The name of the root Logger.
056     */
057    public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
058
059    private static final Logger LOGGER = StatusLogger.getLogger();
060
061    // for convenience
062    private static final String FQCN = LogManager.class.getName();
063
064    private static volatile LoggerContextFactory factory;
065
066    /**
067     * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
068     * 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                factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
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<>();
086            // note that the following initial call to ProviderUtil may block until a Provider has been installed when
087            // running in an OSGi environment
088            if (ProviderUtil.hasProviders()) {
089                for (final Provider provider : ProviderUtil.getProviders()) {
090                    final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
091                    if (factoryClass != null) {
092                        try {
093                            factories.put(provider.getPriority(), factoryClass.newInstance());
094                        } catch (final Exception e) {
095                            LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), provider
096                                    .getUrl().toString(), e);
097                        }
098                    }
099                }
100
101                if (factories.isEmpty()) {
102                    LOGGER.error("Log4j2 could not find a logging implementation. "
103                            + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
104                    factory = new SimpleLoggerContextFactory();
105                } else if (factories.size() == 1) {
106                    factory = factories.get(factories.lastKey());
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. "
120                        + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
121                factory = new SimpleLoggerContextFactory();
122            }
123        }
124    }
125
126    /**
127     * Prevents instantiation
128     */
129    protected LogManager() {
130    }
131
132    /**
133     * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
134     *
135     * @param name The Logger name to search for.
136     * @return true if the Logger exists, false otherwise.
137     * @see LoggerContext#hasLogger(String)
138     */
139    public static boolean exists(final String name) {
140        return getContext().hasLogger(name);
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 for the
147     * calling class.
148     * </p>
149     * 
150     * @return The current LoggerContext.
151     */
152    public static LoggerContext getContext() {
153        return factory.getContext(FQCN, null, null, true);
154    }
155
156    /**
157     * Returns a LoggerContext.
158     *
159     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
160     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
161     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
162     *            be returned. If true then only a single LoggerContext will be returned.
163     * @return a LoggerContext.
164     */
165    public static LoggerContext getContext(final boolean currentContext) {
166        // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
167        return factory.getContext(FQCN, null, null, currentContext, null, null);
168    }
169
170    /**
171     * Returns a LoggerContext.
172     *
173     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
174     *            ClassLoader.
175     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
176     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
177     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
178     *            be returned. If true then only a single LoggerContext will be returned.
179     * @return a LoggerContext.
180     */
181    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
182        return factory.getContext(FQCN, loader, null, currentContext);
183    }
184
185    /**
186     * Returns a LoggerContext.
187     *
188     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
189     *            ClassLoader.
190     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
191     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
192     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
193     *            be returned. If true then only a single LoggerContext will be returned.
194     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
195     * @return a LoggerContext.
196     */
197    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
198            final Object externalContext) {
199        return factory.getContext(FQCN, loader, externalContext, currentContext);
200    }
201
202    /**
203     * Returns a LoggerContext.
204     *
205     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
206     *            ClassLoader.
207     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
208     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
209     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
210     *            be returned. If true then only a single LoggerContext will be returned.
211     * @param configLocation The URI for the configuration to use.
212     * @return a LoggerContext.
213     */
214    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
215            final URI configLocation) {
216        return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
217    }
218
219    /**
220     * Returns a LoggerContext.
221     *
222     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
223     *            ClassLoader.
224     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
225     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
226     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
227     *            be returned. If true then only a single LoggerContext will be returned.
228     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
229     * @param configLocation The URI for the configuration to use.
230     * @return a LoggerContext.
231     */
232    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
233            final Object externalContext, final URI configLocation) {
234        return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
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
245     *            be 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, final String name) {
253        return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
254    }
255
256    /**
257     * Returns a LoggerContext
258     * 
259     * @param fqcn The fully qualified class name of the Class that this method is a member of.
260     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
261     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
262     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
263     *            be returned. If true then only a single LoggerContext will be returned.
264     * @return a LoggerContext.
265     */
266    protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
267        return factory.getContext(fqcn, null, null, currentContext);
268    }
269
270    /**
271     * Returns a LoggerContext
272     * 
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
279     *            be 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     * 
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 calling Class as the Logger name.
316     * <p>
317     * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters.
318     * </p>
319     * 
320     * @return The Logger for the calling class.
321     * @throws UnsupportedOperationException if the calling class cannot be determined.
322     * @since 2.4
323     */
324    public static Logger getFormatterLogger() {
325        return getFormatterLogger(ReflectionUtil.getCallerClass(2));
326    }
327
328    /**
329     * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
330     * <p>
331     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
332     * </p>
333     * <p>
334     * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
335     * </p>
336     *
337     * @param clazz The Class whose name should be used as the Logger name.
338     * @return The Logger, created with a {@link StringFormatterMessageFactory}
339     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
340     *             determined.
341     * @see Logger#fatal(Marker, String, Object...)
342     * @see Logger#fatal(String, Object...)
343     * @see Logger#error(Marker, String, Object...)
344     * @see Logger#error(String, Object...)
345     * @see Logger#warn(Marker, String, Object...)
346     * @see Logger#warn(String, Object...)
347     * @see Logger#info(Marker, String, Object...)
348     * @see Logger#info(String, Object...)
349     * @see Logger#debug(Marker, String, Object...)
350     * @see Logger#debug(String, Object...)
351     * @see Logger#trace(Marker, String, Object...)
352     * @see Logger#trace(String, Object...)
353     * @see StringFormatterMessageFactory
354     */
355    public static Logger getFormatterLogger(final Class<?> clazz) {
356        return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2),
357                StringFormatterMessageFactory.INSTANCE);
358    }
359
360    /**
361     * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
362     * <p>
363     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
364     * </p>
365     * <p>
366     * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
367     * </p>
368     *
369     * @param value The value's whose class name should be used as the Logger name.
370     * @return The Logger, created with a {@link StringFormatterMessageFactory}
371     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
372     *             determined.
373     * @see Logger#fatal(Marker, String, Object...)
374     * @see Logger#fatal(String, Object...)
375     * @see Logger#error(Marker, String, Object...)
376     * @see Logger#error(String, Object...)
377     * @see Logger#warn(Marker, String, Object...)
378     * @see Logger#warn(String, Object...)
379     * @see Logger#info(Marker, String, Object...)
380     * @see Logger#info(String, Object...)
381     * @see Logger#debug(Marker, String, Object...)
382     * @see Logger#debug(String, Object...)
383     * @see Logger#trace(Marker, String, Object...)
384     * @see Logger#trace(String, Object...)
385     * @see StringFormatterMessageFactory
386     */
387    public static Logger getFormatterLogger(final Object value) {
388        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2),
389                StringFormatterMessageFactory.INSTANCE);
390    }
391
392    /**
393     * Returns a formatter Logger with the specified name.
394     * <p>
395     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
396     * </p>
397     * <p>
398     * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
399     * </p>
400     *
401     * @param name The logger name. If null it will default to the name of the calling class.
402     * @return The Logger, created with a {@link StringFormatterMessageFactory}
403     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
404     * @see Logger#fatal(Marker, String, Object...)
405     * @see Logger#fatal(String, Object...)
406     * @see Logger#error(Marker, String, Object...)
407     * @see Logger#error(String, Object...)
408     * @see Logger#warn(Marker, String, Object...)
409     * @see Logger#warn(String, Object...)
410     * @see Logger#info(Marker, String, Object...)
411     * @see Logger#info(String, Object...)
412     * @see Logger#debug(Marker, String, Object...)
413     * @see Logger#debug(String, Object...)
414     * @see Logger#trace(Marker, String, Object...)
415     * @see Logger#trace(String, Object...)
416     * @see StringFormatterMessageFactory
417     */
418    public static Logger getFormatterLogger(final String name) {
419        return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name,
420                StringFormatterMessageFactory.INSTANCE);
421    }
422
423    private static Class<?> callerClass(final Class<?> clazz) {
424        if (clazz != null) {
425            return clazz;
426        }
427        final Class<?> candidate = ReflectionUtil.getCallerClass(3);
428        if (candidate == null) {
429            throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
430        }
431        return candidate;
432    }
433
434    /**
435     * Returns a Logger with the name of the calling class.
436     * 
437     * @return The Logger for the calling class.
438     * @throws UnsupportedOperationException if the calling class cannot be determined.
439     */
440    public static Logger getLogger() {
441        return getLogger(ReflectionUtil.getCallerClass(2));
442    }
443
444    /**
445     * Returns a Logger using the fully qualified name of the Class as the Logger name.
446     * 
447     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
448     *            class.
449     * @return The Logger.
450     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
451     *             determined.
452     */
453    public static Logger getLogger(final Class<?> clazz) {
454        final Class<?> cls = callerClass(clazz);
455        return getContext(cls.getClassLoader(), false).getLogger(cls.getName());
456    }
457
458    /**
459     * Returns a Logger using the fully qualified name of the Class as the Logger name.
460     * 
461     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
462     *            class.
463     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
464     *            logger but will log a warning if mismatched.
465     * @return The Logger.
466     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
467     *             determined.
468     */
469    public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
470        final Class<?> cls = callerClass(clazz);
471        return getContext(cls.getClassLoader(), false).getLogger(cls.getName(), messageFactory);
472    }
473
474    /**
475     * Returns a Logger with the name of the calling class.
476     * 
477     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
478     *            logger but will log a warning if mismatched.
479     * @return The Logger for the calling class.
480     * @throws UnsupportedOperationException if the calling class cannot be determined.
481     */
482    public static Logger getLogger(final MessageFactory messageFactory) {
483        return getLogger(ReflectionUtil.getCallerClass(2), messageFactory);
484    }
485
486    /**
487     * Returns a Logger using the fully qualified class name of the value as the Logger name.
488     * 
489     * @param value The value whose class name should be used as the Logger name. If null the name of the calling class
490     *            will be used as the logger name.
491     * @return The Logger.
492     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
493     *             determined.
494     */
495    public static Logger getLogger(final Object value) {
496        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2));
497    }
498
499    /**
500     * Returns a Logger using the fully qualified class name of the value as the Logger name.
501     * 
502     * @param value The value whose class name should be used as the Logger name. If null the name of the calling class
503     *            will be used as the logger name.
504     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
505     *            logger but will log a warning if mismatched.
506     * @return The Logger.
507     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
508     *             determined.
509     */
510    public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
511        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory);
512    }
513
514    /**
515     * Returns a Logger with the specified name.
516     *
517     * @param name The logger name. If null the name of the calling class will be used.
518     * @return The Logger.
519     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
520     */
521    public static Logger getLogger(final String name) {
522        return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2));
523    }
524
525    /**
526     * Returns a Logger with the specified name.
527     *
528     * @param name The logger name. If null the name of the calling class will be used.
529     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
530     *            logger but will log a warning if mismatched.
531     * @return The Logger.
532     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
533     */
534    public static Logger getLogger(final String name, final MessageFactory messageFactory) {
535        return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
536                ReflectionUtil.getCallerClass(2), messageFactory);
537    }
538
539    /**
540     * Returns a Logger with the specified name.
541     *
542     * @param fqcn The fully qualified class name of the class that this method is a member of.
543     * @param name The logger name.
544     * @return The Logger.
545     */
546    protected static Logger getLogger(final String fqcn, final String name) {
547        return factory.getContext(fqcn, null, null, false).getLogger(name);
548    }
549
550    /**
551     * Returns the root logger.
552     *
553     * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
554     */
555    public static Logger getRootLogger() {
556        return getLogger(ROOT_LOGGER_NAME);
557    }
558}