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.PropertiesUtil;
32  import org.apache.logging.log4j.util.ProviderUtil;
33  import org.apache.logging.log4j.util.Strings;
34  
35  /**
36   * The anchor point for the logging system.
37   */
38  public class LogManager {
39  
40      private static volatile 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 = Strings.EMPTY;
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                  for (final Provider provider : ProviderUtil.getProviders()) {
78                      final String className = provider.getClassName();
79                      if (className != null) {
80                          try {
81                              final Class<?> clazz = cl.loadClass(className);
82                              if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
83                                  factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
84                              } else {
85                                  LOGGER.error("{} does not implement {}", className, LoggerContextFactory.class.getName());
86                              }
87                          } catch (final ClassNotFoundException cnfe) {
88                              LOGGER.error("Unable to locate class {} specified in {}", className,
89                                  provider.getUrl().toString(), cnfe);
90                          } catch (final IllegalAccessException iae) {
91                              LOGGER.error("Unable to create class {} specified in {}", className,
92                                  provider.getUrl().toString(), iae);
93                          } catch (final Exception e) {
94                              LOGGER.error("Unable to create class {} specified in {}", className,
95                                  provider.getUrl().toString(), e);
96                          }
97                      }
98                  }
99  
100                 if (factories.size() == 0) {
101                     LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
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("Unable to locate a logging implementation, using SimpleLogger");
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      * @return  The current LoggerContext.
149      */
150     public static LoggerContext getContext() {
151         return factory.getContext(LogManager.class.getName(), null, null, true);
152     }
153 
154     /**
155      * Returns a LoggerContext.
156      *
157      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
158      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
159      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
160      * returned. If true then only a single LoggerContext will be returned.
161      * @return a LoggerContext.
162      */
163     public static LoggerContext getContext(final boolean currentContext) {
164         return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
165     }
166 
167     /**
168      * Returns a LoggerContext.
169      *
170      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
171      * ClassLoader.
172      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
173      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
174      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
175      * returned. If true then only a single LoggerContext will be returned.
176      * @return a LoggerContext.
177      */
178     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
179         return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
180     }
181 
182     /**
183      * Returns a LoggerContext.
184      *
185      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
186      * ClassLoader.
187      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
188      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
189      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
190      * returned. If true then only a single LoggerContext will be returned.
191      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
192      * @return a LoggerContext.
193      */
194     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
195                                            final Object externalContext) {
196         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
197     }
198 
199     /**
200      * Returns a LoggerContext.
201      *
202      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
203      * ClassLoader.
204      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
205      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
206      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
207      * returned. If true then only a single LoggerContext will be returned.
208      * @param configLocation The URI for the configuration to use.
209      * @return a LoggerContext.
210      */
211     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
212                                            final URI configLocation) {
213         return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
214     }
215 
216 
217     /**
218      * Returns a LoggerContext.
219      *
220      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
221      * ClassLoader.
222      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
223      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
224      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
225      * returned. If true then only a single LoggerContext will be returned.
226      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
227      * @param configLocation The URI for the configuration to use.
228      * @return a LoggerContext.
229      */
230     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
231                                            final Object externalContext, final URI configLocation) {
232         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
233             null);
234     }
235 
236 
237     /**
238      * Returns a LoggerContext.
239      *
240      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
241      * ClassLoader.
242      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
243      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
244      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
245      * returned. If true then only a single LoggerContext will be returned.
246      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
247      * @param configLocation The URI for the configuration to use.
248      * @param name The LoggerContext name.
249      * @return a LoggerContext.
250      */
251     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
252                                            final Object externalContext, final URI configLocation,
253                                            final String name) {
254         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
255             name);
256     }
257 
258     /**
259      * Returns a LoggerContext
260      * @param fqcn The fully qualified class name of the Class that this method is a member of.
261      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
262      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
263      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
264      * returned. If true then only a single LoggerContext will be returned.
265      * @return a LoggerContext.
266      */
267     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
268         return factory.getContext(fqcn, null, null, currentContext);
269     }
270 
271     /**
272      * Returns a LoggerContext
273      * @param fqcn The fully qualified class name of the Class that this method is a member of.
274      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
275      * ClassLoader.
276      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
277      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
278      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
279      * returned. If true then only a single LoggerContext will be returned.
280      * @return a LoggerContext.
281      */
282     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
283                                               final boolean currentContext) {
284         return factory.getContext(fqcn, loader, null, currentContext);
285     }
286 
287     /**
288      * Returns the current LoggerContextFactory.
289      * @return The LoggerContextFactory.
290      */
291     public static LoggerContextFactory getFactory() {
292         return factory;
293     }
294 
295     /**
296      * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
297      * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
298      * Thus, an alternative LoggerContextFactory can be set at runtime.
299      *
300      * <p>
301      * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
302      * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
303      * Generally, this method should be used only during startup before any code starts caching Logger objects.
304      * </p>
305      *
306      * @param factory the LoggerContextFactory to use.
307      */
308     // FIXME: should we allow only one update of the factory?
309     public static void setFactory(final LoggerContextFactory factory) {
310         LogManager.factory = factory;
311     }
312 
313     /**
314      * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
315      * <p>
316      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
317      * </p>
318      * <p>
319      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
320      * </p>
321      *
322      * @param clazz
323      *            The Class whose name should be used as the Logger name.
324      * @return The Logger, created with a {@link StringFormatterMessageFactory}
325      * @see Logger#fatal(Marker, String, Object...)
326      * @see Logger#fatal(String, Object...)
327      * @see Logger#error(Marker, String, Object...)
328      * @see Logger#error(String, Object...)
329      * @see Logger#warn(Marker, String, Object...)
330      * @see Logger#warn(String, Object...)
331      * @see Logger#info(Marker, String, Object...)
332      * @see Logger#info(String, Object...)
333      * @see Logger#debug(Marker, String, Object...)
334      * @see Logger#debug(String, Object...)
335      * @see Logger#trace(Marker, String, Object...)
336      * @see Logger#trace(String, Object...)
337      * @see StringFormatterMessageFactory
338      */
339     public static Logger getFormatterLogger(final Class<?> clazz) {
340         return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
341     }
342 
343     /**
344      * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
345      * <p>
346      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
347      * </p>
348      * <p>
349      * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
350      * </p>
351      *
352      * @param value
353      *            The value's whose class name should be used as the Logger name.
354      * @return The Logger, created with a {@link StringFormatterMessageFactory}
355      * @see Logger#fatal(Marker, String, Object...)
356      * @see Logger#fatal(String, Object...)
357      * @see Logger#error(Marker, String, Object...)
358      * @see Logger#error(String, Object...)
359      * @see Logger#warn(Marker, String, Object...)
360      * @see Logger#warn(String, Object...)
361      * @see Logger#info(Marker, String, Object...)
362      * @see Logger#info(String, Object...)
363      * @see Logger#debug(Marker, String, Object...)
364      * @see Logger#debug(String, Object...)
365      * @see Logger#trace(Marker, String, Object...)
366      * @see Logger#trace(String, Object...)
367      * @see StringFormatterMessageFactory
368      */
369     public static Logger getFormatterLogger(final Object value) {
370         return getLogger(value != null ? value.getClass().getName() : getClassName(2),
371                 StringFormatterMessageFactory.INSTANCE);
372     }
373 
374     /**
375      * Returns a formatter Logger with the specified name.
376      * <p>
377      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
378      * </p>
379      * <p>
380      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
381      * </p>
382      *
383      * @param name The logger name. If null it will default to the name of the calling class.
384      * @return The Logger, created with a {@link StringFormatterMessageFactory}
385      * @see Logger#fatal(Marker, String, Object...)
386      * @see Logger#fatal(String, Object...)
387      * @see Logger#error(Marker, String, Object...)
388      * @see Logger#error(String, Object...)
389      * @see Logger#warn(Marker, String, Object...)
390      * @see Logger#warn(String, Object...)
391      * @see Logger#info(Marker, String, Object...)
392      * @see Logger#info(String, Object...)
393      * @see Logger#debug(Marker, String, Object...)
394      * @see Logger#debug(String, Object...)
395      * @see Logger#trace(Marker, String, Object...)
396      * @see Logger#trace(String, Object...)
397      * @see StringFormatterMessageFactory
398      */
399     public static Logger getFormatterLogger(final String name) {
400         return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
401     }
402 
403     /**
404      * Returns a Logger with the name of the calling class.
405      * @return The Logger for the calling class.
406      */
407     public static Logger getLogger() {
408         return getLogger(getClassName(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      */
417     public static Logger getLogger(final Class<?> clazz) {
418         return getLogger(clazz != null ? clazz.getName() : getClassName(2));
419     }
420 
421     /**
422      * Returns a Logger using the fully qualified name of the Class as the Logger name.
423      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
424      *              class.
425      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
426      *                       the logger but will log a warning if mismatched.
427      * @return The Logger.
428      */
429     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
430         return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
431     }
432 
433     /**
434      * Returns a Logger with the name of the calling class.
435      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
436      *                       the logger but will log a warning if mismatched.
437      * @return The Logger for the calling class.
438      */
439     public static Logger getLogger(final MessageFactory messageFactory) {
440         return getLogger(getClassName(2), messageFactory);
441     }
442 
443     /**
444      * Returns a Logger using the fully qualified class name of the value as the Logger name.
445      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
446      *              class will be used as the logger name.
447      * @return The Logger.
448      */
449     public static Logger getLogger(final Object value) {
450         return getLogger(value != null ? value.getClass().getName() : getClassName(2));
451     }
452 
453     /**
454      * Returns a Logger using the fully qualified class name of the value as the Logger name.
455      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
456      *              class will be used as the logger name.
457      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
458      *                       the logger but will log a warning if mismatched.
459      * @return The Logger.
460      */
461     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
462         return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
463     }
464 
465     /**
466      * Returns a Logger with the specified name.
467      *
468      * @param name The logger name. If null the name of the calling class will be used.
469      * @return The Logger.
470      */
471     public static Logger getLogger(final String name) {
472         final String actualName = name != null ? name : getClassName(2);
473         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
474     }
475 
476     /**
477      * Returns a Logger with the specified name.
478      *
479      * @param name The logger name. If null the name of the calling class will be used.
480      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
481      *                       the logger but will log a warning if mismatched.
482      * @return The Logger.
483      */
484     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
485         final String actualName = name != null ? name : getClassName(2);
486         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
487     }
488 
489     /**
490      * Returns a Logger with the specified name.
491      *
492      * @param fqcn The fully qualified class name of the class that this method is a member of.
493      * @param name The logger name.
494      * @return The Logger.
495      */
496     protected static Logger getLogger(final String fqcn, final String name) {
497         return factory.getContext(fqcn, null, null, false).getLogger(name);
498     }
499 
500     /**
501      * Returns the root logger.
502      *
503      * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
504      */
505     public static Logger getRootLogger() {
506         return getLogger(ROOT_LOGGER_NAME);
507     }
508 
509     /**
510      * Prevents instantiation
511      */
512     protected LogManager() {
513     }
514 
515 }