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.Strings;
35  
36  /**
37   * The anchor point for the logging system. The most common usage of this class is to obtain a named
38   * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger
39   * based on the calling class name. This class also provides method for obtaining named Loggers that use
40   * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages.
41   * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods
42   * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not
43   * normally useful for typical usage of Log4j.
44   */
45  public class LogManager {
46  
47      private static volatile LoggerContextFactory factory;
48  
49      /**
50       * Log4j property to set to the fully qualified class name of a custom implementation of
51       * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
52       */
53      public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
54  
55      private static final Logger LOGGER = StatusLogger.getLogger();
56  
57      /**
58       * The name of the root Logger.
59       */
60      public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
61  
62      /**
63       * Scans the classpath to find all logging implementation. Currently, only one will
64       * be used but this could be extended to allow multiple implementations to be used.
65       */
66      static {
67          // Shortcut binding to force a specific logging implementation.
68          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
69          final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
70          final ClassLoader cl = LoaderUtil.getThreadContextClassLoader();
71          if (factoryClassName != null) {
72              try {
73                  final Class<?> clazz = cl.loadClass(factoryClassName);
74                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
75                      factory = (LoggerContextFactory) clazz.newInstance();
76                  }
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<Integer, LoggerContextFactory>();
86  
87              if (ProviderUtil.hasProviders()) {
88                  for (final Provider provider : ProviderUtil.getProviders()) {
89                      final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
90                      if (factoryClass != null) {
91                          try {
92                              factories.put(provider.getPriority(), factoryClass.newInstance());
93                          } catch (final Exception e) {
94                              LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
95                                  provider.getUrl().toString(), e);
96                          }
97                      }
98                  }
99  
100                 if (factories.isEmpty()) {
101                     LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
102                     factory = new SimpleLoggerContextFactory();
103                 } else {
104                     final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
105                     for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
106                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
107                         sb.append(", Weighting: ").append(entry.getKey()).append('\n');
108                     }
109                     factory = factories.get(factories.lastKey());
110                     sb.append("Using factory: ").append(factory.getClass().getName());
111                     LOGGER.warn(sb.toString());
112 
113                 }
114             } else {
115                 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...");
116                 factory = new SimpleLoggerContextFactory();
117             }
118         }
119     }
120 
121     /**
122      * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
123      *
124      * @param name
125      *            The Logger name to search for.
126      * @return true if the Logger exists, false otherwise.
127      * @see LoggerContext#hasLogger(String)
128      */
129     public static boolean exists(final String name) {
130         return getContext().hasLogger(name);
131     }
132 
133     /**
134      * Gets the class name of the caller in the current stack at the given {@code depth}.
135      *
136      * @param depth a 0-based index in the current stack.
137      * @return a class name
138      */
139     private static String getClassName(final int depth) {
140         return new Throwable().getStackTrace()[depth].getClassName();
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
147      * for the calling class.
148      * </p>
149      * @return  The current LoggerContext.
150      */
151     public static LoggerContext getContext() {
152         return factory.getContext(LogManager.class.getName(), null, null, true);
153     }
154 
155     /**
156      * Returns a LoggerContext.
157      *
158      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
159      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
160      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
161      * returned. If true then only a single LoggerContext will be returned.
162      * @return a LoggerContext.
163      */
164     public static LoggerContext getContext(final boolean currentContext) {
165         return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
166     }
167 
168     /**
169      * Returns a LoggerContext.
170      *
171      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
172      * ClassLoader.
173      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
174      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
175      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
176      * returned. If true then only a single LoggerContext will be returned.
177      * @return a LoggerContext.
178      */
179     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
180         return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
181     }
182 
183     /**
184      * Returns a LoggerContext.
185      *
186      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
187      * ClassLoader.
188      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
189      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
190      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
191      * returned. If true then only a single LoggerContext will be returned.
192      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
193      * @return a LoggerContext.
194      */
195     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
196                                            final Object externalContext) {
197         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
198     }
199 
200     /**
201      * Returns a LoggerContext.
202      *
203      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
204      * ClassLoader.
205      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
206      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
207      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
208      * returned. If true then only a single LoggerContext will be returned.
209      * @param configLocation The URI for the configuration to use.
210      * @return a LoggerContext.
211      */
212     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
213                                            final URI configLocation) {
214         return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
215     }
216 
217 
218     /**
219      * Returns a LoggerContext.
220      *
221      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
222      * ClassLoader.
223      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
224      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
225      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
226      * returned. If true then only a single LoggerContext will be returned.
227      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
228      * @param configLocation The URI for the configuration to use.
229      * @return a LoggerContext.
230      */
231     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
232                                            final Object externalContext, final URI configLocation) {
233         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
234             null);
235     }
236 
237 
238     /**
239      * Returns a LoggerContext.
240      *
241      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
242      * ClassLoader.
243      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
244      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
245      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
246      * returned. If true then only a single LoggerContext will be returned.
247      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
248      * @param configLocation The URI for the configuration to use.
249      * @param name The LoggerContext name.
250      * @return a LoggerContext.
251      */
252     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
253                                            final Object externalContext, final URI configLocation,
254                                            final String name) {
255         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
256             name);
257     }
258 
259     /**
260      * Returns a LoggerContext
261      * @param fqcn The fully qualified class name of the Class that this method is a member of.
262      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
263      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
264      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
265      * returned. If true then only a single LoggerContext will be returned.
266      * @return a LoggerContext.
267      */
268     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
269         return factory.getContext(fqcn, null, null, currentContext);
270     }
271 
272     /**
273      * Returns a LoggerContext
274      * @param fqcn The fully qualified class name of the Class that this method is a member of.
275      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
276      * ClassLoader.
277      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
278      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
279      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
280      * returned. If true then only a single LoggerContext will be returned.
281      * @return a LoggerContext.
282      */
283     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
284                                               final boolean currentContext) {
285         return factory.getContext(fqcn, loader, null, currentContext);
286     }
287 
288     /**
289      * Returns the current LoggerContextFactory.
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 Class as the Logger name.
316      * <p>
317      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
318      * </p>
319      * <p>
320      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
321      * </p>
322      *
323      * @param clazz
324      *            The Class whose name should be used as the Logger name.
325      * @return The Logger, created with a {@link StringFormatterMessageFactory}
326      * @see Logger#fatal(Marker, String, Object...)
327      * @see Logger#fatal(String, Object...)
328      * @see Logger#error(Marker, String, Object...)
329      * @see Logger#error(String, Object...)
330      * @see Logger#warn(Marker, String, Object...)
331      * @see Logger#warn(String, Object...)
332      * @see Logger#info(Marker, String, Object...)
333      * @see Logger#info(String, Object...)
334      * @see Logger#debug(Marker, String, Object...)
335      * @see Logger#debug(String, Object...)
336      * @see Logger#trace(Marker, String, Object...)
337      * @see Logger#trace(String, Object...)
338      * @see StringFormatterMessageFactory
339      */
340     public static Logger getFormatterLogger(final Class<?> clazz) {
341         return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
342     }
343 
344     /**
345      * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
346      * <p>
347      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
348      * </p>
349      * <p>
350      * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
351      * </p>
352      *
353      * @param value
354      *            The value's whose class name should be used as the Logger name.
355      * @return The Logger, created with a {@link StringFormatterMessageFactory}
356      * @see Logger#fatal(Marker, String, Object...)
357      * @see Logger#fatal(String, Object...)
358      * @see Logger#error(Marker, String, Object...)
359      * @see Logger#error(String, Object...)
360      * @see Logger#warn(Marker, String, Object...)
361      * @see Logger#warn(String, Object...)
362      * @see Logger#info(Marker, String, Object...)
363      * @see Logger#info(String, Object...)
364      * @see Logger#debug(Marker, String, Object...)
365      * @see Logger#debug(String, Object...)
366      * @see Logger#trace(Marker, String, Object...)
367      * @see Logger#trace(String, Object...)
368      * @see StringFormatterMessageFactory
369      */
370     public static Logger getFormatterLogger(final Object value) {
371         return getLogger(value != null ? value.getClass().getName() : getClassName(2),
372                 StringFormatterMessageFactory.INSTANCE);
373     }
374 
375     /**
376      * Returns a formatter Logger with the specified name.
377      * <p>
378      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
379      * </p>
380      * <p>
381      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
382      * </p>
383      *
384      * @param name The logger name. If null it will default to the name of the calling class.
385      * @return The Logger, created with a {@link StringFormatterMessageFactory}
386      * @see Logger#fatal(Marker, String, Object...)
387      * @see Logger#fatal(String, Object...)
388      * @see Logger#error(Marker, String, Object...)
389      * @see Logger#error(String, Object...)
390      * @see Logger#warn(Marker, String, Object...)
391      * @see Logger#warn(String, Object...)
392      * @see Logger#info(Marker, String, Object...)
393      * @see Logger#info(String, Object...)
394      * @see Logger#debug(Marker, String, Object...)
395      * @see Logger#debug(String, Object...)
396      * @see Logger#trace(Marker, String, Object...)
397      * @see Logger#trace(String, Object...)
398      * @see StringFormatterMessageFactory
399      */
400     public static Logger getFormatterLogger(final String name) {
401         return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
402     }
403 
404     /**
405      * Returns a Logger with the name of the calling class.
406      * @return The Logger for the calling class.
407      */
408     public static Logger getLogger() {
409         return getLogger(getClassName(2));
410     }
411 
412     /**
413      * Returns a Logger using the fully qualified name of the Class as the Logger name.
414      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
415      *              class.
416      * @return The Logger.
417      */
418     public static Logger getLogger(final Class<?> clazz) {
419         return getLogger(clazz != null ? clazz.getName() : getClassName(2));
420     }
421 
422     /**
423      * Returns a Logger using the fully qualified name of the Class as the Logger name.
424      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
425      *              class.
426      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
427      *                       the logger but will log a warning if mismatched.
428      * @return The Logger.
429      */
430     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
431         return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
432     }
433 
434     /**
435      * Returns a Logger with the name of the calling class.
436      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
437      *                       the logger but will log a warning if mismatched.
438      * @return The Logger for the calling class.
439      */
440     public static Logger getLogger(final MessageFactory messageFactory) {
441         return getLogger(getClassName(2), messageFactory);
442     }
443 
444     /**
445      * Returns a Logger using the fully qualified class name of the value as the Logger name.
446      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
447      *              class will be used as the logger name.
448      * @return The Logger.
449      */
450     public static Logger getLogger(final Object value) {
451         return getLogger(value != null ? value.getClass().getName() : getClassName(2));
452     }
453 
454     /**
455      * Returns a Logger using the fully qualified class name of the value as the Logger name.
456      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
457      *              class will be used as the logger name.
458      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
459      *                       the logger but will log a warning if mismatched.
460      * @return The Logger.
461      */
462     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
463         return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
464     }
465 
466     /**
467      * Returns a Logger with the specified name.
468      *
469      * @param name The logger name. If null the name of the calling class will be used.
470      * @return The Logger.
471      */
472     public static Logger getLogger(final String name) {
473         final String actualName = name != null ? name : getClassName(2);
474         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
475     }
476 
477     /**
478      * Returns a Logger with the specified name.
479      *
480      * @param name The logger name. If null the name of the calling class will be used.
481      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
482      *                       the logger but will log a warning if mismatched.
483      * @return The Logger.
484      */
485     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
486         final String actualName = name != null ? name : getClassName(2);
487         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
488     }
489 
490     /**
491      * Returns a Logger with the specified name.
492      *
493      * @param fqcn The fully qualified class name of the class that this method is a member of.
494      * @param name The logger name.
495      * @return The Logger.
496      */
497     protected static Logger getLogger(final String fqcn, final String name) {
498         return factory.getContext(fqcn, null, null, false).getLogger(name);
499     }
500 
501     /**
502      * Returns the root logger.
503      *
504      * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
505      */
506     public static Logger getRootLogger() {
507         return getLogger(ROOT_LOGGER_NAME);
508     }
509 
510     /**
511      * Prevents instantiation
512      */
513     protected LogManager() {
514     }
515 
516 }