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