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