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.spi.Terminable;
031import org.apache.logging.log4j.status.StatusLogger;
032import org.apache.logging.log4j.util.LoaderUtil;
033import org.apache.logging.log4j.util.PropertiesUtil;
034import org.apache.logging.log4j.util.ProviderUtil;
035import org.apache.logging.log4j.util.StackLocatorUtil;
036import org.apache.logging.log4j.util.Strings;
037
038/**
039 * The anchor point for the Log4j logging system. The most common usage of this class is to obtain a named
040 * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based
041 * on the calling class name. This class also provides method for obtaining named Loggers that use
042 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. These
043 * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are
044 * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally
045 * useful for typical usage of Log4j.
046 */
047public class LogManager {
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    /**
056     * The name of the root Logger.
057     */
058    public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
059
060    private static final Logger LOGGER = StatusLogger.getLogger();
061
062    // for convenience
063    private static final String FQCN = LogManager.class.getName();
064
065    private static volatile LoggerContextFactory factory;
066
067    /**
068     * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
069     * extended to allow multiple implementations to be used.
070     */
071    static {
072        // Shortcut binding to force a specific logging implementation.
073        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
074        final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
075        if (factoryClassName != null) {
076            try {
077                factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
078            } catch (final ClassNotFoundException cnfe) {
079                LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
080            } catch (final Exception ex) {
081                LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
082            }
083        }
084
085        if (factory == null) {
086            final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
087            // note that the following initial call to ProviderUtil may block until a Provider has been installed when
088            // running in an OSGi environment
089            if (ProviderUtil.hasProviders()) {
090                for (final Provider provider : ProviderUtil.getProviders()) {
091                    final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
092                    if (factoryClass != null) {
093                        try {
094                            factories.put(provider.getPriority(), factoryClass.newInstance());
095                        } catch (final Exception e) {
096                            LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
097                                    .getUrl(), e);
098                        }
099                    }
100                }
101
102                if (factories.isEmpty()) {
103                    LOGGER.error("Log4j2 could not find a logging implementation. "
104                            + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
105                    factory = new SimpleLoggerContextFactory();
106                } else if (factories.size() == 1) {
107                    factory = factories.get(factories.lastKey());
108                } else {
109                    final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
110                    for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
111                        sb.append("Factory: ").append(entry.getValue().getClass().getName());
112                        sb.append(", Weighting: ").append(entry.getKey()).append('\n');
113                    }
114                    factory = factories.get(factories.lastKey());
115                    sb.append("Using factory: ").append(factory.getClass().getName());
116                    LOGGER.warn(sb.toString());
117
118                }
119            } else {
120                LOGGER.error("Log4j2 could not find a logging implementation. "
121                        + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
122                factory = new SimpleLoggerContextFactory();
123            }
124        }
125    }
126
127    /**
128     * Prevents instantiation
129     */
130    protected LogManager() {
131    }
132
133    /**
134     * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
135     *
136     * @param name The Logger name to search for.
137     * @return true if the Logger exists, false otherwise.
138     * @see LoggerContext#hasLogger(String)
139     */
140    public static boolean exists(final String name) {
141        return getContext().hasLogger(name);
142    }
143
144    /**
145     * Returns the current LoggerContext.
146     * <p>
147     * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger for the
148     * calling class.
149     * </p>
150     *
151     * @return The current LoggerContext.
152     */
153    public static LoggerContext getContext() {
154        try {
155            return factory.getContext(FQCN, null, null, true);
156        } catch (final IllegalStateException ex) {
157            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
158            return new SimpleLoggerContextFactory().getContext(FQCN, null, null, true);
159        }
160    }
161
162    /**
163     * Returns a LoggerContext.
164     *
165     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
166     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
167     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
168     *            be returned. If true then only a single LoggerContext will be returned.
169     * @return a LoggerContext.
170     */
171    public static LoggerContext getContext(final boolean currentContext) {
172        // TODO: would it be a terrible idea to try and find the caller ClassLoader here?
173        try {
174            return factory.getContext(FQCN, null, null, currentContext, null, null);
175        } catch (final IllegalStateException ex) {
176            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
177            return new SimpleLoggerContextFactory().getContext(FQCN, null, null, currentContext, null, null);
178        }
179    }
180
181    /**
182     * Returns a LoggerContext.
183     *
184     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
185     *            ClassLoader.
186     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
187     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
188     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
189     *            be returned. If true then only a single LoggerContext will be returned.
190     * @return a LoggerContext.
191     */
192    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
193        try {
194            return factory.getContext(FQCN, loader, null, currentContext);
195        } catch (final IllegalStateException ex) {
196            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
197            return new SimpleLoggerContextFactory().getContext(FQCN, loader, null, currentContext);
198        }
199    }
200
201    /**
202     * Returns a LoggerContext.
203     *
204     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
205     *            ClassLoader.
206     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
207     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
208     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
209     *            be returned. If true then only a single LoggerContext will be returned.
210     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
211     * @return a LoggerContext.
212     */
213    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
214            final Object externalContext) {
215        try {
216            return factory.getContext(FQCN, loader, externalContext, currentContext);
217        } catch (final IllegalStateException ex) {
218            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
219            return new SimpleLoggerContextFactory().getContext(FQCN, loader, externalContext, currentContext);
220        }
221    }
222
223    /**
224     * Returns a LoggerContext.
225     *
226     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
227     *            ClassLoader.
228     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
229     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
230     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
231     *            be returned. If true then only a single LoggerContext will be returned.
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 URI configLocation) {
237        try {
238            return factory.getContext(FQCN, loader, null, currentContext, configLocation, null);
239        } catch (final IllegalStateException ex) {
240            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
241            return new SimpleLoggerContextFactory().getContext(FQCN, loader, null, currentContext, configLocation,
242                    null);
243        }
244    }
245
246    /**
247     * Returns a LoggerContext.
248     *
249     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
250     *            ClassLoader.
251     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
252     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
253     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
254     *            be returned. If true then only a single LoggerContext will be returned.
255     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
256     * @param configLocation The URI for the configuration to use.
257     * @return a LoggerContext.
258     */
259    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
260            final Object externalContext, final URI configLocation) {
261        try {
262            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null);
263        } catch (final IllegalStateException ex) {
264            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
265            return new SimpleLoggerContextFactory().getContext(FQCN, loader, externalContext, currentContext,
266                    configLocation, null);
267        }
268    }
269
270    /**
271     * Returns a LoggerContext.
272     *
273     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
274     *            ClassLoader.
275     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
276     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
277     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
278     *            be returned. If true then only a single LoggerContext will be returned.
279     * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
280     * @param configLocation The URI for the configuration to use.
281     * @param name The LoggerContext name.
282     * @return a LoggerContext.
283     */
284    public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
285            final Object externalContext, final URI configLocation, final String name) {
286        try {
287            return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name);
288        } catch (final IllegalStateException ex) {
289            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
290            return new SimpleLoggerContextFactory().getContext(FQCN, loader, externalContext, currentContext,
291                    configLocation, name);
292        }
293    }
294
295    /**
296     * Returns a LoggerContext
297     *
298     * @param fqcn The fully qualified class name of the Class that this method is a member of.
299     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
300     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
301     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
302     *            be returned. If true then only a single LoggerContext will be returned.
303     * @return a LoggerContext.
304     */
305    protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
306        try {
307            return factory.getContext(fqcn, null, null, currentContext);
308        } catch (final IllegalStateException ex) {
309            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
310            return new SimpleLoggerContextFactory().getContext(fqcn, null, null, currentContext);
311        }
312    }
313
314    /**
315     * Returns a LoggerContext
316     *
317     * @param fqcn The fully qualified class name of the Class that this method is a member of.
318     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
319     *            ClassLoader.
320     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
321     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
322     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
323     *            be returned. If true then only a single LoggerContext will be returned.
324     * @return a LoggerContext.
325     */
326    protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
327            final boolean currentContext) {
328        try {
329            return factory.getContext(fqcn, loader, null, currentContext);
330        } catch (final IllegalStateException ex) {
331            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
332            return new SimpleLoggerContextFactory().getContext(fqcn, loader, null, currentContext);
333        }
334    }
335
336
337    /**
338     * Returns a LoggerContext
339     *
340     * @param fqcn The fully qualified class name of the Class that this method is a member of.
341     * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
342     *            ClassLoader.
343     * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
344     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
345     *            returned and if the caller is a class in the container's classpath then a different LoggerContext may
346     *            be returned. If true then only a single LoggerContext will be returned.
347     * @param configLocation The URI for the configuration to use.
348     * @param name The LoggerContext name.
349     * @return a LoggerContext.
350     */
351    protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
352                                              final boolean currentContext, final URI configLocation, final String name) {
353        try {
354            return factory.getContext(fqcn, loader, null, currentContext, configLocation, name);
355        } catch (final IllegalStateException ex) {
356            LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
357            return new SimpleLoggerContextFactory().getContext(fqcn, loader, null, currentContext);
358        }
359    }
360
361    /**
362     * Shutdown using the LoggerContext appropriate for the caller of this method.
363     * This is equivalent to calling {@code LogManager.shutdown(false)}.
364     *
365     * This call is synchronous and will block until shut down is complete.
366     * This may include flushing pending log events over network connections.
367     *
368     * @since 2.6
369     */
370    public static void shutdown() {
371        shutdown(false);
372    }
373
374    /**
375     * Shutdown the logging system if the logging system supports it.
376     * This is equivalent to calling {@code LogManager.shutdown(LogManager.getContext(currentContext))}.
377     *
378     * This call is synchronous and will block until shut down is complete.
379     * This may include flushing pending log events over network connections.
380     *
381     * @param currentContext if true a default LoggerContext (may not be the LoggerContext used to create a Logger
382     *            for the calling class) will be used.
383     *            If false the LoggerContext appropriate for the caller of this method is used. For
384     *            example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
385     *            used and if the caller is a class in the container's classpath then a different LoggerContext may
386     *            be used.
387     * @since 2.6
388     */
389    public static void shutdown(final boolean currentContext) {
390        shutdown(getContext(currentContext));
391    }
392
393    /**
394     * Shutdown the logging system if the logging system supports it.
395     *
396     * This call is synchronous and will block until shut down is complete.
397     * This may include flushing pending log events over network connections.
398     *
399     * @param context the LoggerContext.
400     * @since 2.6
401     */
402    public static void shutdown(final LoggerContext context) {
403        if (context != null && context instanceof Terminable) {
404            ((Terminable) context).terminate();
405        }
406    }
407
408    private static String toLoggerName(final Class<?> cls) {
409        final String canonicalName = cls.getCanonicalName();
410        return canonicalName != null ? canonicalName : cls.getName();
411    }
412
413    /**
414     * Returns the current LoggerContextFactory.
415     *
416     * @return The LoggerContextFactory.
417     */
418    public static LoggerContextFactory getFactory() {
419        return factory;
420    }
421
422    /**
423     * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
424     * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
425     * Thus, an alternative LoggerContextFactory can be set at runtime.
426     *
427     * <p>
428     * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
429     * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
430     * Generally, this method should be used only during startup before any code starts caching Logger objects.
431     * </p>
432     *
433     * @param factory the LoggerContextFactory to use.
434     */
435    // FIXME: should we allow only one update of the factory?
436    public static void setFactory(final LoggerContextFactory factory) {
437        LogManager.factory = factory;
438    }
439
440    /**
441     * Returns a formatter Logger using the fully qualified name of the calling Class as the Logger name.
442     * <p>
443     * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters.
444     * </p>
445     *
446     * @return The Logger for the calling class.
447     * @throws UnsupportedOperationException if the calling class cannot be determined.
448     * @since 2.4
449     */
450    public static Logger getFormatterLogger() {
451        return getFormatterLogger(StackLocatorUtil.getCallerClass(2));
452    }
453
454    /**
455     * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
456     * <p>
457     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
458     * </p>
459     * <p>
460     * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
461     * </p>
462     *
463     * @param clazz The Class whose name should be used as the Logger name.
464     * @return The Logger, created with a {@link StringFormatterMessageFactory}
465     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
466     *             determined.
467     * @see Logger#fatal(Marker, String, Object...)
468     * @see Logger#fatal(String, Object...)
469     * @see Logger#error(Marker, String, Object...)
470     * @see Logger#error(String, Object...)
471     * @see Logger#warn(Marker, String, Object...)
472     * @see Logger#warn(String, Object...)
473     * @see Logger#info(Marker, String, Object...)
474     * @see Logger#info(String, Object...)
475     * @see Logger#debug(Marker, String, Object...)
476     * @see Logger#debug(String, Object...)
477     * @see Logger#trace(Marker, String, Object...)
478     * @see Logger#trace(String, Object...)
479     * @see StringFormatterMessageFactory
480     */
481    public static Logger getFormatterLogger(final Class<?> clazz) {
482        return getLogger(clazz != null ? clazz : StackLocatorUtil.getCallerClass(2),
483                StringFormatterMessageFactory.INSTANCE);
484    }
485
486    /**
487     * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
488     * <p>
489     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
490     * </p>
491     * <p>
492     * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
493     * </p>
494     *
495     * @param value The value's whose class name should be used as the Logger name.
496     * @return The Logger, created with a {@link StringFormatterMessageFactory}
497     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
498     *             determined.
499     * @see Logger#fatal(Marker, String, Object...)
500     * @see Logger#fatal(String, Object...)
501     * @see Logger#error(Marker, String, Object...)
502     * @see Logger#error(String, Object...)
503     * @see Logger#warn(Marker, String, Object...)
504     * @see Logger#warn(String, Object...)
505     * @see Logger#info(Marker, String, Object...)
506     * @see Logger#info(String, Object...)
507     * @see Logger#debug(Marker, String, Object...)
508     * @see Logger#debug(String, Object...)
509     * @see Logger#trace(Marker, String, Object...)
510     * @see Logger#trace(String, Object...)
511     * @see StringFormatterMessageFactory
512     */
513    public static Logger getFormatterLogger(final Object value) {
514        return getLogger(value != null ? value.getClass() : StackLocatorUtil.getCallerClass(2),
515                StringFormatterMessageFactory.INSTANCE);
516    }
517
518    /**
519     * Returns a formatter Logger with the specified name.
520     * <p>
521     * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
522     * </p>
523     * <p>
524     * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
525     * </p>
526     *
527     * @param name The logger name. If null it will default to the name of the calling class.
528     * @return The Logger, created with a {@link StringFormatterMessageFactory}
529     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
530     * @see Logger#fatal(Marker, String, Object...)
531     * @see Logger#fatal(String, Object...)
532     * @see Logger#error(Marker, String, Object...)
533     * @see Logger#error(String, Object...)
534     * @see Logger#warn(Marker, String, Object...)
535     * @see Logger#warn(String, Object...)
536     * @see Logger#info(Marker, String, Object...)
537     * @see Logger#info(String, Object...)
538     * @see Logger#debug(Marker, String, Object...)
539     * @see Logger#debug(String, Object...)
540     * @see Logger#trace(Marker, String, Object...)
541     * @see Logger#trace(String, Object...)
542     * @see StringFormatterMessageFactory
543     */
544    public static Logger getFormatterLogger(final String name) {
545        return name == null ? getFormatterLogger(StackLocatorUtil.getCallerClass(2)) : getLogger(name,
546                StringFormatterMessageFactory.INSTANCE);
547    }
548
549    private static Class<?> callerClass(final Class<?> clazz) {
550        if (clazz != null) {
551            return clazz;
552        }
553        final Class<?> candidate = StackLocatorUtil.getCallerClass(3);
554        if (candidate == null) {
555            throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found.");
556        }
557        return candidate;
558    }
559
560    /**
561     * Returns a Logger with the name of the calling class.
562     *
563     * @return The Logger for the calling class.
564     * @throws UnsupportedOperationException if the calling class cannot be determined.
565     */
566    public static Logger getLogger() {
567        return getLogger(StackLocatorUtil.getCallerClass(2));
568    }
569
570    /**
571     * Returns a Logger using the fully qualified name of the Class as the Logger name.
572     *
573     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
574     *            class.
575     * @return The Logger.
576     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
577     *             determined.
578     */
579    public static Logger getLogger(final Class<?> clazz) {
580        final Class<?> cls = callerClass(clazz);
581        return getContext(cls.getClassLoader(), false).getLogger(toLoggerName(cls));
582    }
583
584    /**
585     * Returns a Logger using the fully qualified name of the Class as the Logger name.
586     *
587     * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
588     *            class.
589     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
590     *            logger but will log a warning if mismatched.
591     * @return The Logger.
592     * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be
593     *             determined.
594     */
595    public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
596        final Class<?> cls = callerClass(clazz);
597        return getContext(cls.getClassLoader(), false).getLogger(toLoggerName(cls), messageFactory);
598    }
599
600    /**
601     * Returns a Logger with the name of the calling class.
602     *
603     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
604     *            logger but will log a warning if mismatched.
605     * @return The Logger for the calling class.
606     * @throws UnsupportedOperationException if the calling class cannot be determined.
607     */
608    public static Logger getLogger(final MessageFactory messageFactory) {
609        return getLogger(StackLocatorUtil.getCallerClass(2), messageFactory);
610    }
611
612    /**
613     * Returns a Logger using the fully qualified class name of the value as the Logger name.
614     *
615     * @param value The value whose class name should be used as the Logger name. If null the name of the calling class
616     *            will be used as the logger name.
617     * @return The Logger.
618     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
619     *             determined.
620     */
621    public static Logger getLogger(final Object value) {
622        return getLogger(value != null ? value.getClass() : StackLocatorUtil.getCallerClass(2));
623    }
624
625    /**
626     * Returns a Logger using the fully qualified class name of the value as the Logger name.
627     *
628     * @param value The value whose class name should be used as the Logger name. If null the name of the calling class
629     *            will be used as the logger name.
630     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
631     *            logger but will log a warning if mismatched.
632     * @return The Logger.
633     * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be
634     *             determined.
635     */
636    public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
637        return getLogger(value != null ? value.getClass() : StackLocatorUtil.getCallerClass(2), messageFactory);
638    }
639
640    /**
641     * Returns a Logger with the specified name.
642     *
643     * @param name The logger name. If null the name of the calling class will be used.
644     * @return The Logger.
645     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
646     */
647    public static Logger getLogger(final String name) {
648        return name != null ? getContext(false).getLogger(name) : getLogger(StackLocatorUtil.getCallerClass(2));
649    }
650
651    /**
652     * Returns a Logger with the specified name.
653     *
654     * @param name The logger name. If null the name of the calling class will be used.
655     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the
656     *            logger but will log a warning if mismatched.
657     * @return The Logger.
658     * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined.
659     */
660    public static Logger getLogger(final String name, final MessageFactory messageFactory) {
661        return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger(
662                StackLocatorUtil.getCallerClass(2), messageFactory);
663    }
664
665    /**
666     * Returns a Logger with the specified name.
667     *
668     * @param fqcn The fully qualified class name of the class that this method is a member of.
669     * @param name The logger name.
670     * @return The Logger.
671     */
672    protected static Logger getLogger(final String fqcn, final String name) {
673        return factory.getContext(fqcn, null, null, false).getLogger(name);
674    }
675
676    /**
677     * Returns the root logger.
678     *
679     * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
680     */
681    public static Logger getRootLogger() {
682        return getLogger(ROOT_LOGGER_NAME);
683    }
684}