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.Iterator;
21  import java.util.Map;
22  import java.util.SortedMap;
23  import java.util.TreeMap;
24  
25  import org.apache.logging.log4j.message.MessageFactory;
26  import org.apache.logging.log4j.message.StringFormatterMessageFactory;
27  import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
28  import org.apache.logging.log4j.spi.LoggerContext;
29  import org.apache.logging.log4j.spi.LoggerContextFactory;
30  import org.apache.logging.log4j.spi.Provider;
31  import org.apache.logging.log4j.status.StatusLogger;
32  import org.apache.logging.log4j.util.PropertiesUtil;
33  import org.apache.logging.log4j.util.ProviderUtil;
34  
35  /**
36   * The anchor point for the logging system.
37   */
38  public class LogManager {
39  
40      private static LoggerContextFactory factory;
41  
42      private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
43  
44      private static final Logger LOGGER = StatusLogger.getLogger();
45  
46      /**
47       * The name of the root Logger.
48       */
49      public static final String ROOT_LOGGER_NAME = "";
50  
51      /**
52       * Scans the classpath to find all logging implementation. Currently, only one will
53       * be used but this could be extended to allow multiple implementations to be used.
54       */
55      static {
56          // Shortcut binding to force a specific logging implementation.
57          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
58          final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
59          final ClassLoader cl = ProviderUtil.findClassLoader();
60          if (factoryClass != null) {
61              try {
62                  final Class<?> clazz = cl.loadClass(factoryClass);
63                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
64                      factory = (LoggerContextFactory) clazz.newInstance();
65                  }
66              } catch (final ClassNotFoundException cnfe) {
67                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
68              } catch (final Exception ex) {
69                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
70              }
71          }
72  
73          if (factory == null) {
74              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
75  
76              if (ProviderUtil.hasProviders()) {
77                  final Iterator<Provider> providers = ProviderUtil.getProviders();
78                  while (providers.hasNext()) {
79                      final Provider provider = providers.next();
80                      final String className = provider.getClassName();
81                      if (className != null) {
82                          try {
83                              final Class<?> clazz = cl.loadClass(className);
84                              if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
85                                  factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
86                              } else {
87                                  LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName());
88                              }
89                          } catch (final ClassNotFoundException cnfe) {
90                              LOGGER.error("Unable to locate class " + className + " specified in " +
91                                  provider.getURL().toString(), cnfe);
92                          } catch (final IllegalAccessException iae) {
93                              LOGGER.error("Unable to create class " + className + " specified in " +
94                                  provider.getURL().toString(), iae);
95                          } catch (final Exception e) {
96                              LOGGER.error("Unable to create class " + className + " specified in " +
97                                  provider.getURL().toString(), e);
98                              e.printStackTrace();
99                          }
100                     }
101                 }
102 
103                 if (factories.size() == 0) {
104                     LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
105                     factory = new SimpleLoggerContextFactory();
106                 } else {
107                     final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
108                     for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
109                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
110                         sb.append(", Weighting: ").append(entry.getKey()).append("\n");
111                     }
112                     factory = factories.get(factories.lastKey());
113                     sb.append("Using factory: ").append(factory.getClass().getName());
114                     LOGGER.warn(sb.toString());
115 
116                 }
117             } else {
118                 LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
119                 factory = new SimpleLoggerContextFactory();
120             }
121         }
122     }
123 
124     /**
125      * Gets the class name of the caller in the current stack at the given {@code depth}.
126      *
127      * @param depth a 0-based index in the current stack.
128      * @return a class name
129      */
130     private static String getClassName(final int depth) {
131         return new Throwable().getStackTrace()[depth].getClassName();
132     }
133 
134     /**
135      * Returns the current LoggerContext.
136      * <p>
137      * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
138      * for the calling class.
139      * @return  The current LoggerContext.
140      */
141     public static LoggerContext getContext() {
142         return factory.getContext(LogManager.class.getName(), null, null, true);
143     }
144 
145     /**
146      * Returns a LoggerContext.
147      *
148      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
149      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
150      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
151      * returned. If true then only a single LoggerContext will be returned.
152      * @return a LoggerContext.
153      */
154     public static LoggerContext getContext(final boolean currentContext) {
155         return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
156     }
157 
158     /**
159      * Returns a LoggerContext.
160      *
161      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
162      * ClassLoader.
163      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
164      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
165      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
166      * returned. If true then only a single LoggerContext will be returned.
167      * @return a LoggerContext.
168      */
169     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
170         return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
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 be
181      * returned. If true then only a single LoggerContext will be returned.
182      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
183      * @return a LoggerContext.
184      */
185     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
186                                            final Object externalContext) {
187         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
188     }
189 
190     /**
191      * Returns a LoggerContext.
192      *
193      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
194      * ClassLoader.
195      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
196      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
197      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
198      * returned. If true then only a single LoggerContext will be returned.
199      * @param configLocation The URI for the configuration to use.
200      * @return a LoggerContext.
201      */
202     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
203                                            final URI configLocation) {
204         return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
205     }
206 
207 
208     /**
209      * Returns a LoggerContext.
210      *
211      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
212      * ClassLoader.
213      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
214      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
215      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
216      * returned. If true then only a single LoggerContext will be returned.
217      * @param configLocation The URI for the configuration to use.
218      * @return a LoggerContext.
219      */
220     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
221                                            final Object externalContext, final URI configLocation) {
222         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
223             null);
224     }
225 
226 
227     /**
228      * Returns a LoggerContext.
229      *
230      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
231      * ClassLoader.
232      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
233      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
234      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
235      * returned. If true then only a single LoggerContext will be returned.
236      * @param configLocation The URI for the configuration to use.
237      * @param name The LoggerContext name.
238      * @return a LoggerContext.
239      */
240     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
241                                            final Object externalContext, final URI configLocation,
242                                            final String name) {
243         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
244             name);
245     }
246 
247     /**
248      * Returns a LoggerContext
249      * @param fqcn The fully qualified class name of the Class that this method is a member of.
250      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
251      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
252      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
253      * returned. If true then only a single LoggerContext will be returned.
254      * @return a LoggerContext.
255      */
256     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
257         return factory.getContext(fqcn, null, null, currentContext);
258     }
259 
260     /**
261      * Returns a LoggerContext
262      * @param fqcn The fully qualified class name of the Class that this method is a member of.
263      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
264      * ClassLoader.
265      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
266      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
267      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
268      * returned. If true then only a single LoggerContext will be returned.
269      * @return a LoggerContext.
270      */
271     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
272                                               final boolean currentContext) {
273         return factory.getContext(fqcn, loader, null, currentContext);
274     }
275 
276     /**
277      * Returns the LoggerContextFactory.
278      * @return The LoggerContextFactory.
279      */
280     public static LoggerContextFactory getFactory() {
281         return factory;
282     }
283 
284     /**
285      * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
286      * <p>
287      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
288      * </p>
289      * <p>
290      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
291      * </p>
292      *
293      * @param clazz
294      *            The Class whose name should be used as the Logger name.
295      * @return The Logger, created with a {@link StringFormatterMessageFactory}
296      * @see Logger#fatal(Marker, String, Object...)
297      * @see Logger#fatal(String, Object...)
298      * @see Logger#error(Marker, String, Object...)
299      * @see Logger#error(String, Object...)
300      * @see Logger#warn(Marker, String, Object...)
301      * @see Logger#warn(String, Object...)
302      * @see Logger#info(Marker, String, Object...)
303      * @see Logger#info(String, Object...)
304      * @see Logger#debug(Marker, String, Object...)
305      * @see Logger#debug(String, Object...)
306      * @see Logger#trace(Marker, String, Object...)
307      * @see Logger#trace(String, Object...)
308      * @see StringFormatterMessageFactory
309      */
310     public static Logger getFormatterLogger(final Class<?> clazz) {
311         return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
312     }
313 
314     /**
315      * Returns a formatter Logger using the fully qualified name of the value's 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(value, StringFormatterMessageFactory.INSTANCE)}
321      * </p>
322      *
323      * @param value
324      *            The value's whose class 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 Object value) {
341         return getLogger(value != null ? value.getClass().getName() : getClassName(2),
342                 StringFormatterMessageFactory.INSTANCE);
343     }
344 
345     /**
346      * Returns a formatter Logger with the specified name.
347      * <p>
348      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
349      * </p>
350      * <p>
351      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
352      * </p>
353      *
354      * @param name The logger name. If null it will default to the name of the calling class.
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 String name) {
371         return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
372     }
373 
374     /**
375      * Returns a Logger with the name of the calling class.
376      * @return The Logger for the calling class.
377      */
378     public static Logger getLogger() {
379         return getLogger(getClassName(2));
380     }
381 
382     /**
383      * Returns a Logger using the fully qualified name of the Class as the Logger name.
384      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
385      *              class.
386      * @return The Logger.
387      */
388     public static Logger getLogger(final Class<?> clazz) {
389         return getLogger(clazz != null ? clazz.getName() : getClassName(2));
390     }
391 
392     /**
393      * Returns a Logger using the fully qualified name of the Class as the Logger name.
394      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
395      *              class.
396      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
397      *                       the logger but will log a warning if mismatched.
398      * @return The Logger.
399      */
400     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
401         return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
402     }
403 
404     /**
405      * Returns a Logger with the name of the calling class.
406      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
407      *                       the logger but will log a warning if mismatched.
408      * @return The Logger for the calling class.
409      */
410     public static Logger getLogger(final MessageFactory messageFactory) {
411         return getLogger(getClassName(2), messageFactory);
412     }
413 
414     /**
415      * Returns a Logger using the fully qualified class name of the value as the Logger name.
416      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
417      *              class will be used as the logger name.
418      * @return The Logger.
419      */
420     public static Logger getLogger(final Object value) {
421         return getLogger(value != null ? value.getClass().getName() : getClassName(2));
422     }
423 
424     /**
425      * Returns a Logger using the fully qualified class name of the value as the Logger name.
426      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
427      *              class will be used as the logger name.
428      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
429      *                       the logger but will log a warning if mismatched.
430      * @return The Logger.
431      */
432     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
433         return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
434     }
435 
436     /**
437      * Returns a Logger with the specified name.
438      *
439      * @param name The logger name. If null the name of the calling class will be used.
440      * @return The Logger.
441      */
442     public static Logger getLogger(final String name) {
443         final String actualName = name != null ? name : getClassName(2);
444         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
445     }
446 
447     /**
448      * Returns a Logger with the specified name.
449      *
450      * @param name The logger name. If null the name of the calling class will be used.
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.
454      */
455     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
456         final String actualName = name != null ? name : getClassName(2);
457         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
458     }
459 
460     /**
461      * Returns a Logger with the specified name.
462      *
463      * @param fqcn The fully qualified class name of the class that this method is a member of.
464      * @param name The logger name.
465      * @return The Logger.
466      */
467     protected static Logger getLogger(final String fqcn, final String name) {
468         return factory.getContext(fqcn, null, null, false).getLogger(name);
469     }
470 
471     /**
472      * Returns the root logger.
473      *
474      * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
475      */
476     public static Logger getRootLogger() {
477         return getLogger(ROOT_LOGGER_NAME);
478     }
479 
480     /**
481      * Prevents instantiation
482      */
483     protected LogManager() {
484     }
485 
486 }