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