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