View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j;
18  
19  import java.net.URI;
20  import java.util.Map;
21  import java.util.SortedMap;
22  import java.util.TreeMap;
23  
24  import org.apache.logging.log4j.message.MessageFactory;
25  import org.apache.logging.log4j.message.StringFormatterMessageFactory;
26  import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
27  import org.apache.logging.log4j.spi.LoggerContext;
28  import org.apache.logging.log4j.spi.LoggerContextFactory;
29  import org.apache.logging.log4j.spi.Provider;
30  import org.apache.logging.log4j.spi.Terminable;
31  import org.apache.logging.log4j.status.StatusLogger;
32  import org.apache.logging.log4j.util.LoaderUtil;
33  import org.apache.logging.log4j.util.PropertiesUtil;
34  import org.apache.logging.log4j.util.ProviderUtil;
35  import org.apache.logging.log4j.util.StackLocatorUtil;
36  import org.apache.logging.log4j.util.Strings;
37  
38  /**
39   * The anchor point for the Log4j logging system. The most common usage of this class is to obtain a named
40   * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based
41   * on the calling class name. This class also provides method for obtaining named Loggers that use
42   * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. These
43   * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are
44   * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally
45   * useful for typical usage of Log4j.
46   */
47  public class LogManager {
48  
49      /**
50       * Log4j property to set to the fully qualified class name of a custom implementation of
51       * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
52       */
53      public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
54  
55      /**
56       * The name of the root Logger.
57       */
58      public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
59  
60      private static final Logger LOGGER = StatusLogger.getLogger();
61  
62      // for convenience
63      private static final String FQCN = LogManager.class.getName();
64  
65      private static volatile LoggerContextFactory factory;
66  
67      /**
68       * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
69       * extended to allow multiple implementations to be used.
70       */
71      static {
72          // Shortcut binding to force a specific logging implementation.
73          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
74          final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
75          if (factoryClassName != null) {
76              try {
77                  factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
78              } catch (final ClassNotFoundException cnfe) {
79                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
80              } catch (final Exception ex) {
81                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
82              }
83          }
84  
85          if (factory == null) {
86              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
87              // note that the following initial call to ProviderUtil may block until a Provider has been installed when
88              // running in an OSGi environment
89              if (ProviderUtil.hasProviders()) {
90                  for (final Provider provider : ProviderUtil.getProviders()) {
91                      final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
92                      if (factoryClass != null) {
93                          try {
94                              factories.put(provider.getPriority(), factoryClass.newInstance());
95                          } catch (final Exception e) {
96                              LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
97                                      .getUrl(), e);
98                          }
99                      }
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 }