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