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.internal.LogManagerStatus;
25  import org.apache.logging.log4j.message.MessageFactory;
26  import org.apache.logging.log4j.message.StringFormatterMessageFactory;
27  import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
28  import org.apache.logging.log4j.spi.LoggerContext;
29  import org.apache.logging.log4j.spi.LoggerContextFactory;
30  import org.apache.logging.log4j.spi.Provider;
31  import org.apache.logging.log4j.spi.Terminable;
32  import org.apache.logging.log4j.status.StatusLogger;
33  import org.apache.logging.log4j.util.LoaderUtil;
34  import org.apache.logging.log4j.util.PropertiesUtil;
35  import org.apache.logging.log4j.util.ProviderUtil;
36  import org.apache.logging.log4j.util.StackLocatorUtil;
37  import org.apache.logging.log4j.util.Strings;
38  
39  /**
40   * The anchor point for the Log4j logging system. The most common usage of this class is to obtain a named
41   * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based
42   * on the calling class name. This class also provides method for obtaining named Loggers that use
43   * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. These
44   * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are
45   * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally
46   * useful for typical usage of Log4j.
47   */
48  public class LogManager {
49  
50      /**
51       * Log4j property to set to the fully qualified class name of a custom implementation of
52       * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
53       */
54      public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
55  
56      /**
57       * The name of the root Logger.
58       */
59      public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
60  
61      private static final Logger LOGGER = StatusLogger.getLogger();
62  
63      // for convenience
64      private static final String FQCN = LogManager.class.getName();
65  
66      private static volatile LoggerContextFactory factory;
67  
68      /**
69       * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be
70       * extended to allow multiple implementations to be used.
71       */
72      static {
73          // Shortcut binding to force a specific logging implementation.
74          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
75          final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
76          if (factoryClassName != null) {
77              try {
78                  factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class);
79              } catch (final ClassNotFoundException cnfe) {
80                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
81              } catch (final Exception ex) {
82                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
83              }
84          }
85  
86          if (factory == null) {
87              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>();
88              // note that the following initial call to ProviderUtil may block until a Provider has been installed when
89              // running in an OSGi environment
90              if (ProviderUtil.hasProviders()) {
91                  for (final Provider provider : ProviderUtil.getProviders()) {
92                      final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
93                      if (factoryClass != null) {
94                          try {
95                              factories.put(provider.getPriority(), factoryClass.newInstance());
96                          } catch (final Exception e) {
97                              LOGGER.error("Unable to create class {} specified in provider URL {}", factoryClass.getName(), provider
98                                      .getUrl(), e);
99                          }
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 }