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
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 */
046public 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<>();
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 if (factories.size() == 1) {
108                                        factory = factories.get(factories.lastKey());
109                } else {
110                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
111                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
112                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
113                        sb.append(", Weighting: ").append(entry.getKey()).append('\n');
114                    }
115                    factory = factories.get(factories.lastKey());
116                    sb.append("Using factory: ").append(factory.getClass().getName());
117                    LOGGER.warn(sb.toString());
118
119                }
120            } else {
121                LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
122                factory = new SimpleLoggerContextFactory();
123            }
124        }
125    }
126
127    /**
128     * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
129     *
130     * @param name
131     *            The Logger name to search for.
132     * @return true if the Logger exists, false otherwise.
133     * @see LoggerContext#hasLogger(String)
134     */
135    public static boolean exists(final String name) {
136        return getContext().hasLogger(name);
137    }
138
139    /**
140     * Returns the current LoggerContext.
141     * <p>
142     * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
143     * for the calling class.
144     * </p>
145     * @return  The current LoggerContext.
146     */
147    public static LoggerContext getContext() {
148        return factory.getContext(FQCN, null, null, true);
149    }
150
151    /**
152     * Returns a LoggerContext.
153     *
154     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
155     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
156     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
157     * returned. If true then only a single LoggerContext will be returned.
158     * @return a LoggerContext.
159     */
160    public static LoggerContext getContext(final boolean currentContext) {
161        // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
162        return factory.getContext(FQCN, null, null, currentContext, null, null);
163    }
164
165    /**
166     * Returns a LoggerContext.
167     *
168     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
169     * ClassLoader.
170     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
171     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
172     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
173     * returned. If true then only a single LoggerContext will be returned.
174     * @return a LoggerContext.
175     */
176    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
177        return factory.getContext(FQCN, loader, null, currentContext);
178    }
179
180    /**
181     * Returns a LoggerContext.
182     *
183     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
184     * ClassLoader.
185     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
186     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
187     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
188     * returned. If true then only a single LoggerContext will be returned.
189     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
190     * @return a LoggerContext.
191     */
192    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
193                                           final Object externalContext) {
194        return factory.getContext(FQCN, loader, externalContext, currentContext);
195    }
196
197    /**
198     * Returns a LoggerContext.
199     *
200     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
201     * ClassLoader.
202     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
203     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
204     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
205     * returned. If true then only a single LoggerContext will be returned.
206     * @param configLocation The URI for the configuration to use.
207     * @return a LoggerContext.
208     */
209    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
210                                           final URI configLocation) {
211        return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
212    }
213
214
215    /**
216     * Returns a LoggerContext.
217     *
218     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
219     * ClassLoader.
220     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
221     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
222     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
223     * returned. If true then only a single LoggerContext will be returned.
224     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
225     * @param configLocation The URI for the configuration to use.
226     * @return a LoggerContext.
227     */
228    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
229                                           final Object externalContext, final URI configLocation) {
230        return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
231    }
232
233
234    /**
235     * Returns a LoggerContext.
236     *
237     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
238     * ClassLoader.
239     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
240     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
241     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
242     * returned. If true then only a single LoggerContext will be returned.
243     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
244     * @param configLocation The URI for the configuration to use.
245     * @param name The LoggerContext name.
246     * @return a LoggerContext.
247     */
248    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
249                                           final Object externalContext, final URI configLocation,
250                                           final String name) {
251        return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
252    }
253
254    /**
255     * Returns a LoggerContext
256     * @param fqcn The fully qualified class name of the Class that this method is a member of.
257     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
258     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
259     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
260     * returned. If true then only a single LoggerContext will be returned.
261     * @return a LoggerContext.
262     */
263    protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
264        return factory.getContext(fqcn, null, null, currentContext);
265    }
266
267    /**
268     * Returns a LoggerContext
269     * @param fqcn The fully qualified class name of the Class that this method is a member of.
270     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
271     * ClassLoader.
272     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
273     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
274     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
275     * returned. If true then only a single LoggerContext will be returned.
276     * @return a LoggerContext.
277     */
278    protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
279                                              final boolean currentContext) {
280        return factory.getContext(fqcn, loader, null, currentContext);
281    }
282
283    /**
284     * Returns the current LoggerContextFactory.
285     * @return The LoggerContextFactory.
286     */
287    public static LoggerContextFactory getFactory() {
288        return factory;
289    }
290
291    /**
292     * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
293     * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
294     * Thus, an alternative LoggerContextFactory can be set at runtime.
295     *
296     * <p>
297     * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
298     * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
299     * Generally, this method should be used only during startup before any code starts caching Logger objects.
300     * </p>
301     *
302     * @param factory the LoggerContextFactory to use.
303     */
304    // FIXME: should we allow only one update of the factory?
305    public static void setFactory(final LoggerContextFactory factory) {
306        LogManager.factory = factory;
307    }
308
309    /**
310     * Returns a formatter Logger using the fully qualified name of the calling Class as the Logger name.
311     * <p>
312     * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters.
313     * </p>
314     * @return The Logger for the calling class.
315     * @throws UnsupportedOperationException if the calling class cannot be determined.
316     * @since 2.4
317     */
318    public static Logger getFormatterLogger() {
319        return getFormatterLogger(ReflectionUtil.getCallerClass(2));
320    }
321
322
323    /**
324     * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
325     * <p>
326     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
327     * </p>
328     * <p>
329     * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
330     * </p>
331     *
332     * @param clazz
333     *            The Class whose name should be used as the Logger name.
334     * @return The Logger, created with a {@link StringFormatterMessageFactory}
335     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
336     * @see Logger#fatal(Marker, String, Object...)
337     * @see Logger#fatal(String, Object...)
338     * @see Logger#error(Marker, String, Object...)
339     * @see Logger#error(String, Object...)
340     * @see Logger#warn(Marker, String, Object...)
341     * @see Logger#warn(String, Object...)
342     * @see Logger#info(Marker, String, Object...)
343     * @see Logger#info(String, Object...)
344     * @see Logger#debug(Marker, String, Object...)
345     * @see Logger#debug(String, Object...)
346     * @see Logger#trace(Marker, String, Object...)
347     * @see Logger#trace(String, Object...)
348     * @see StringFormatterMessageFactory
349     */
350    public static Logger getFormatterLogger(final Class<?> clazz) {
351        return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2),
352            StringFormatterMessageFactory.INSTANCE);
353    }
354
355    /**
356     * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
357     * <p>
358     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
359     * </p>
360     * <p>
361     * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
362     * </p>
363     *
364     * @param value
365     *            The value's whose class name should be used as the Logger name.
366     * @return The Logger, created with a {@link StringFormatterMessageFactory}
367     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
368     * @see Logger#fatal(Marker, String, Object...)
369     * @see Logger#fatal(String, Object...)
370     * @see Logger#error(Marker, String, Object...)
371     * @see Logger#error(String, Object...)
372     * @see Logger#warn(Marker, String, Object...)
373     * @see Logger#warn(String, Object...)
374     * @see Logger#info(Marker, String, Object...)
375     * @see Logger#info(String, Object...)
376     * @see Logger#debug(Marker, String, Object...)
377     * @see Logger#debug(String, Object...)
378     * @see Logger#trace(Marker, String, Object...)
379     * @see Logger#trace(String, Object...)
380     * @see StringFormatterMessageFactory
381     */
382    public static Logger getFormatterLogger(final Object value) {
383        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2),
384            StringFormatterMessageFactory.INSTANCE);
385    }
386
387    /**
388     * Returns a formatter Logger with the specified name.
389     * <p>
390     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
391     * </p>
392     * <p>
393     * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
394     * </p>
395     *
396     * @param name The logger name. If null it will default to the name of the calling class.
397     * @return The Logger, created with a {@link StringFormatterMessageFactory}
398     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
399     * @see Logger#fatal(Marker, String, Object...)
400     * @see Logger#fatal(String, Object...)
401     * @see Logger#error(Marker, String, Object...)
402     * @see Logger#error(String, Object...)
403     * @see Logger#warn(Marker, String, Object...)
404     * @see Logger#warn(String, Object...)
405     * @see Logger#info(Marker, String, Object...)
406     * @see Logger#info(String, Object...)
407     * @see Logger#debug(Marker, String, Object...)
408     * @see Logger#debug(String, Object...)
409     * @see Logger#trace(Marker, String, Object...)
410     * @see Logger#trace(String, Object...)
411     * @see StringFormatterMessageFactory
412     */
413    public static Logger getFormatterLogger(final String name) {
414        return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name,
415            StringFormatterMessageFactory.INSTANCE);
416    }
417
418    private static Class<?> callerClass(final Class<?> clazz) {
419        if (clazz != null) {
420            return clazz;
421        }
422        final Class<?> candidate = ReflectionUtil.getCallerClass(3);
423        if (candidate == null) {
424            throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
425        }
426        return candidate;
427    }
428
429    /**
430     * Returns a Logger with the name of the calling class.
431     * @return The Logger for the calling class.
432     * @throws UnsupportedOperationException if the calling class cannot be determined.
433     */
434    public static Logger getLogger() {
435        return getLogger(ReflectionUtil.getCallerClass(2));
436    }
437
438    /**
439     * Returns a Logger using the fully qualified name of the Class as the Logger name.
440     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
441     *              class.
442     * @return The Logger.
443     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
444     */
445    public static Logger getLogger(final Class<?> clazz) {
446        final Class<?> cls = callerClass(clazz);
447        return getContext(cls.getClassLoader(), false).getLogger(cls.getName());
448    }
449
450    /**
451     * Returns a Logger using the fully qualified name of the Class as the Logger name.
452     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
453     *              class.
454     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
455     *                       the logger but will log a warning if mismatched.
456     * @return The Logger.
457     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined.
458     */
459    public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
460        final Class<?> cls = callerClass(clazz);
461        return getContext(cls.getClassLoader(), false).getLogger(cls.getName(), messageFactory);
462    }
463
464    /**
465     * Returns a Logger with the name of the calling class.
466     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
467     *                       the logger but will log a warning if mismatched.
468     * @return The Logger for the calling class.
469     * @throws UnsupportedOperationException if the calling class cannot be determined.
470     */
471    public static Logger getLogger(final MessageFactory messageFactory) {
472        return getLogger(ReflectionUtil.getCallerClass(2), messageFactory);
473    }
474
475    /**
476     * Returns a Logger using the fully qualified class name of the value as the Logger name.
477     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
478     *              class will be used as the logger name.
479     * @return The Logger.
480     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
481     */
482    public static Logger getLogger(final Object value) {
483        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2));
484    }
485
486    /**
487     * Returns a Logger using the fully qualified class name of the value as the Logger name.
488     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
489     *              class will be used as the logger name.
490     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
491     *                       the logger but will log a warning if mismatched.
492     * @return The Logger.
493     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined.
494     */
495    public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
496        return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory);
497    }
498
499    /**
500     * Returns a Logger with the specified name.
501     *
502     * @param name The logger name. If null the name of the calling class will be used.
503     * @return The Logger.
504     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
505     */
506    public static Logger getLogger(final String name) {
507        return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2));
508    }
509
510    /**
511     * Returns a Logger with the specified name.
512     *
513     * @param name The logger name. If null the name of the calling class will be used.
514     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
515     *                       the logger but will log a warning if mismatched.
516     * @return The Logger.
517     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
518     */
519    public static Logger getLogger(final String name, final MessageFactory messageFactory) {
520        return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
521            ReflectionUtil.getCallerClass(2), messageFactory);
522    }
523
524    /**
525     * Returns a Logger with the specified name.
526     *
527     * @param fqcn The fully qualified class name of the class that this method is a member of.
528     * @param name The logger name.
529     * @return The Logger.
530     */
531    protected static Logger getLogger(final String fqcn, final String name) {
532        return factory.getContext(fqcn, null, null, false).getLogger(name);
533    }
534
535    /**
536     * Returns the root logger.
537     *
538     * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
539     */
540    public static Logger getRootLogger() {
541        return getLogger(ROOT_LOGGER_NAME);
542    }
543
544    /**
545     * Prevents instantiation
546     */
547    protected LogManager() {
548    }
549
550}