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.Strings;
035
036/**
037 * The anchor point for the logging system. The most common usage of this class is to obtain a named
038 * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger
039 * based on the calling class name. This class also provides method for obtaining named Loggers that use
040 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages.
041 * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods
042 * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not
043 * normally useful for typical usage of Log4j.
044 */
045public class LogManager {
046
047    private static volatile LoggerContextFactory factory;
048
049    /**
050     * Log4j property to set to the fully qualified class name of a custom implementation of
051     * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
052     */
053    public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
054
055    private static final Logger LOGGER = StatusLogger.getLogger();
056
057    /**
058     * The name of the root Logger.
059     */
060    public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
061
062    /**
063     * Scans the classpath to find all logging implementation. Currently, only one will
064     * be used but this could be extended to allow multiple implementations to be used.
065     */
066    static {
067        // Shortcut binding to force a specific logging implementation.
068        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
069        final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
070        final ClassLoader cl = LoaderUtil.getThreadContextClassLoader();
071        if (factoryClassName != null) {
072            try {
073                final Class<?> clazz = cl.loadClass(factoryClassName);
074                if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
075                    factory = (LoggerContextFactory) clazz.newInstance();
076                }
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<Integer, LoggerContextFactory>();
086
087            if (ProviderUtil.hasProviders()) {
088                for (final Provider provider : ProviderUtil.getProviders()) {
089                    final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
090                    if (factoryClass != null) {
091                        try {
092                            factories.put(provider.getPriority(), factoryClass.newInstance());
093                        } catch (final Exception e) {
094                            LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
095                                provider.getUrl().toString(), e);
096                        }
097                    }
098                }
099
100                if (factories.isEmpty()) {
101                    LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
102                    factory = new SimpleLoggerContextFactory();
103                } else {
104                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
105                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
106                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
107                        sb.append(", Weighting: ").append(entry.getKey()).append('\n');
108                    }
109                    factory = factories.get(factories.lastKey());
110                    sb.append("Using factory: ").append(factory.getClass().getName());
111                    LOGGER.warn(sb.toString());
112
113                }
114            } else {
115                LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
116                factory = new SimpleLoggerContextFactory();
117            }
118        }
119    }
120
121    /**
122     * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
123     *
124     * @param name
125     *            The Logger name to search for.
126     * @return true if the Logger exists, false otherwise.
127     * @see LoggerContext#hasLogger(String)
128     */
129    public static boolean exists(final String name) {
130        return getContext().hasLogger(name);
131    }
132
133    /**
134     * Gets the class name of the caller in the current stack at the given {@code depth}.
135     *
136     * @param depth a 0-based index in the current stack.
137     * @return a class name
138     */
139    private static String getClassName(final int depth) {
140        return new Throwable().getStackTrace()[depth].getClassName();
141    }
142
143    /**
144     * Returns the current LoggerContext.
145     * <p>
146     * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
147     * for the calling class.
148     * </p>
149     * @return  The current LoggerContext.
150     */
151    public static LoggerContext getContext() {
152        return factory.getContext(LogManager.class.getName(), null, null, true);
153    }
154
155    /**
156     * Returns a LoggerContext.
157     *
158     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
159     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
160     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
161     * returned. If true then only a single LoggerContext will be returned.
162     * @return a LoggerContext.
163     */
164    public static LoggerContext getContext(final boolean currentContext) {
165        return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
166    }
167
168    /**
169     * Returns a LoggerContext.
170     *
171     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
172     * ClassLoader.
173     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
174     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
175     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
176     * returned. If true then only a single LoggerContext will be returned.
177     * @return a LoggerContext.
178     */
179    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
180        return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
181    }
182
183    /**
184     * Returns a LoggerContext.
185     *
186     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
187     * ClassLoader.
188     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
189     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
190     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
191     * returned. If true then only a single LoggerContext will be returned.
192     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
193     * @return a LoggerContext.
194     */
195    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
196                                           final Object externalContext) {
197        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
198    }
199
200    /**
201     * Returns a LoggerContext.
202     *
203     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
204     * ClassLoader.
205     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
206     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
207     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
208     * returned. If true then only a single LoggerContext will be returned.
209     * @param configLocation The URI for the configuration to use.
210     * @return a LoggerContext.
211     */
212    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
213                                           final URI configLocation) {
214        return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
215    }
216
217
218    /**
219     * Returns a LoggerContext.
220     *
221     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
222     * ClassLoader.
223     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
224     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
225     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
226     * returned. If true then only a single LoggerContext will be returned.
227     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
228     * @param configLocation The URI for the configuration to use.
229     * @return a LoggerContext.
230     */
231    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
232                                           final Object externalContext, final URI configLocation) {
233        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
234            null);
235    }
236
237
238    /**
239     * Returns a LoggerContext.
240     *
241     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
242     * ClassLoader.
243     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
244     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
245     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
246     * returned. If true then only a single LoggerContext will be returned.
247     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
248     * @param configLocation The URI for the configuration to use.
249     * @param name The LoggerContext name.
250     * @return a LoggerContext.
251     */
252    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
253                                           final Object externalContext, final URI configLocation,
254                                           final String name) {
255        return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
256            name);
257    }
258
259    /**
260     * Returns a LoggerContext
261     * @param fqcn The fully qualified class name of the Class that this method is a member of.
262     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
263     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
264     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
265     * returned. If true then only a single LoggerContext will be returned.
266     * @return a LoggerContext.
267     */
268    protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
269        return factory.getContext(fqcn, null, null, currentContext);
270    }
271
272    /**
273     * Returns a LoggerContext
274     * @param fqcn The fully qualified class name of the Class that this method is a member of.
275     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
276     * ClassLoader.
277     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
278     * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
279     * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
280     * returned. If true then only a single LoggerContext will be returned.
281     * @return a LoggerContext.
282     */
283    protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
284                                              final boolean currentContext) {
285        return factory.getContext(fqcn, loader, null, currentContext);
286    }
287
288    /**
289     * Returns the current LoggerContextFactory.
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 Class as the Logger name.
316     * <p>
317     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
318     * </p>
319     * <p>
320     * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
321     * </p>
322     *
323     * @param clazz
324     *            The Class whose name should be used as the Logger name.
325     * @return The Logger, created with a {@link StringFormatterMessageFactory}
326     * @see Logger#fatal(Marker, String, Object...)
327     * @see Logger#fatal(String, Object...)
328     * @see Logger#error(Marker, String, Object...)
329     * @see Logger#error(String, Object...)
330     * @see Logger#warn(Marker, String, Object...)
331     * @see Logger#warn(String, Object...)
332     * @see Logger#info(Marker, String, Object...)
333     * @see Logger#info(String, Object...)
334     * @see Logger#debug(Marker, String, Object...)
335     * @see Logger#debug(String, Object...)
336     * @see Logger#trace(Marker, String, Object...)
337     * @see Logger#trace(String, Object...)
338     * @see StringFormatterMessageFactory
339     */
340    public static Logger getFormatterLogger(final Class<?> clazz) {
341        return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
342    }
343
344    /**
345     * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
346     * <p>
347     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
348     * </p>
349     * <p>
350     * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
351     * </p>
352     *
353     * @param value
354     *            The value's whose class name should be used as the Logger name.
355     * @return The Logger, created with a {@link StringFormatterMessageFactory}
356     * @see Logger#fatal(Marker, String, Object...)
357     * @see Logger#fatal(String, Object...)
358     * @see Logger#error(Marker, String, Object...)
359     * @see Logger#error(String, Object...)
360     * @see Logger#warn(Marker, String, Object...)
361     * @see Logger#warn(String, Object...)
362     * @see Logger#info(Marker, String, Object...)
363     * @see Logger#info(String, Object...)
364     * @see Logger#debug(Marker, String, Object...)
365     * @see Logger#debug(String, Object...)
366     * @see Logger#trace(Marker, String, Object...)
367     * @see Logger#trace(String, Object...)
368     * @see StringFormatterMessageFactory
369     */
370    public static Logger getFormatterLogger(final Object value) {
371        return getLogger(value != null ? value.getClass().getName() : getClassName(2),
372                StringFormatterMessageFactory.INSTANCE);
373    }
374
375    /**
376     * Returns a formatter Logger with the specified name.
377     * <p>
378     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
379     * </p>
380     * <p>
381     * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
382     * </p>
383     *
384     * @param name The logger name. If null it will default to the name of the calling class.
385     * @return The Logger, created with a {@link StringFormatterMessageFactory}
386     * @see Logger#fatal(Marker, String, Object...)
387     * @see Logger#fatal(String, Object...)
388     * @see Logger#error(Marker, String, Object...)
389     * @see Logger#error(String, Object...)
390     * @see Logger#warn(Marker, String, Object...)
391     * @see Logger#warn(String, Object...)
392     * @see Logger#info(Marker, String, Object...)
393     * @see Logger#info(String, Object...)
394     * @see Logger#debug(Marker, String, Object...)
395     * @see Logger#debug(String, Object...)
396     * @see Logger#trace(Marker, String, Object...)
397     * @see Logger#trace(String, Object...)
398     * @see StringFormatterMessageFactory
399     */
400    public static Logger getFormatterLogger(final String name) {
401        return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
402    }
403
404    /**
405     * Returns a Logger with the name of the calling class.
406     * @return The Logger for the calling class.
407     */
408    public static Logger getLogger() {
409        return getLogger(getClassName(2));
410    }
411
412    /**
413     * Returns a Logger using the fully qualified name of the Class as the Logger name.
414     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
415     *              class.
416     * @return The Logger.
417     */
418    public static Logger getLogger(final Class<?> clazz) {
419        return getLogger(clazz != null ? clazz.getName() : getClassName(2));
420    }
421
422    /**
423     * Returns a Logger using the fully qualified name of the Class as the Logger name.
424     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
425     *              class.
426     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
427     *                       the logger but will log a warning if mismatched.
428     * @return The Logger.
429     */
430    public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
431        return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
432    }
433
434    /**
435     * Returns a Logger with the name of the calling class.
436     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
437     *                       the logger but will log a warning if mismatched.
438     * @return The Logger for the calling class.
439     */
440    public static Logger getLogger(final MessageFactory messageFactory) {
441        return getLogger(getClassName(2), messageFactory);
442    }
443
444    /**
445     * Returns a Logger using the fully qualified class name of the value as the Logger name.
446     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
447     *              class will be used as the logger name.
448     * @return The Logger.
449     */
450    public static Logger getLogger(final Object value) {
451        return getLogger(value != null ? value.getClass().getName() : getClassName(2));
452    }
453
454    /**
455     * Returns a Logger using the fully qualified class name of the value as the Logger name.
456     * @param value The value whose class name should be used as the Logger name. If null the name of the calling
457     *              class will be used as the logger name.
458     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
459     *                       the logger but will log a warning if mismatched.
460     * @return The Logger.
461     */
462    public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
463        return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
464    }
465
466    /**
467     * Returns a Logger with the specified name.
468     *
469     * @param name The logger name. If null the name of the calling class will be used.
470     * @return The Logger.
471     */
472    public static Logger getLogger(final String name) {
473        final String actualName = name != null ? name : getClassName(2);
474        return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
475    }
476
477    /**
478     * Returns a Logger with the specified name.
479     *
480     * @param name The logger name. If null the name of the calling class will be used.
481     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
482     *                       the logger but will log a warning if mismatched.
483     * @return The Logger.
484     */
485    public static Logger getLogger(final String name, final MessageFactory messageFactory) {
486        final String actualName = name != null ? name : getClassName(2);
487        return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
488    }
489
490    /**
491     * Returns a Logger with the specified name.
492     *
493     * @param fqcn The fully qualified class name of the class that this method is a member of.
494     * @param name The logger name.
495     * @return The Logger.
496     */
497    protected static Logger getLogger(final String fqcn, final String name) {
498        return factory.getContext(fqcn, null, null, false).getLogger(name);
499    }
500
501    /**
502     * Returns the root logger.
503     *
504     * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
505     */
506    public static Logger getRootLogger() {
507        return getLogger(ROOT_LOGGER_NAME);
508    }
509
510    /**
511     * Prevents instantiation
512     */
513    protected LogManager() {
514    }
515
516}