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