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