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.
38   */
39  public class LogManager {
40  
41      private static volatile LoggerContextFactory factory;
42  
43      /**
44       * Log4j property to set to the fully qualified class name of a custom implementation of
45       * {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
46       */
47      public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
48  
49      private static final Logger LOGGER = StatusLogger.getLogger();
50  
51      /**
52       * The name of the root Logger.
53       */
54      public static final String ROOT_LOGGER_NAME = Strings.EMPTY;
55  
56      /**
57       * Scans the classpath to find all logging implementation. Currently, only one will
58       * be used but this could be extended to allow multiple implementations to be used.
59       */
60      static {
61          // Shortcut binding to force a specific logging implementation.
62          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
63          final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
64          final ClassLoader cl = LoaderUtil.getThreadContextClassLoader();
65          if (factoryClassName != null) {
66              try {
67                  final Class<?> clazz = cl.loadClass(factoryClassName);
68                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
69                      factory = (LoggerContextFactory) clazz.newInstance();
70                  }
71              } catch (final ClassNotFoundException cnfe) {
72                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName);
73              } catch (final Exception ex) {
74                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex);
75              }
76          }
77  
78          if (factory == null) {
79              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
80  
81              if (ProviderUtil.hasProviders()) {
82                  for (final Provider provider : ProviderUtil.getProviders()) {
83                      final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory();
84                      if (factoryClass != null) {
85                          try {
86                              factories.put(provider.getPriority(), factoryClass.newInstance());
87                          } catch (final Exception e) {
88                              LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(),
89                                  provider.getUrl().toString(), e);
90                          }
91                      }
92                  }
93  
94                  if (factories.isEmpty()) {
95                      LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
96                      factory = new SimpleLoggerContextFactory();
97                  } else {
98                      final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
99                      for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
100                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
101                         sb.append(", Weighting: ").append(entry.getKey()).append('\n');
102                     }
103                     factory = factories.get(factories.lastKey());
104                     sb.append("Using factory: ").append(factory.getClass().getName());
105                     LOGGER.warn(sb.toString());
106 
107                 }
108             } else {
109                 LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
110                 factory = new SimpleLoggerContextFactory();
111             }
112         }
113     }
114 
115     /**
116      * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1.
117      *
118      * @param name
119      *            The Logger name to search for.
120      * @return true if the Logger exists, false otherwise.
121      * @see LoggerContext#hasLogger(String)
122      */
123     public static boolean exists(final String name) {
124         return getContext().hasLogger(name);
125     }
126 
127     /**
128      * Gets the class name of the caller in the current stack at the given {@code depth}.
129      *
130      * @param depth a 0-based index in the current stack.
131      * @return a class name
132      */
133     private static String getClassName(final int depth) {
134         return new Throwable().getStackTrace()[depth].getClassName();
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(LogManager.class.getName(), 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         return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null);
160     }
161 
162     /**
163      * Returns a LoggerContext.
164      *
165      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
166      * ClassLoader.
167      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
168      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
169      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
170      * returned. If true then only a single LoggerContext will be returned.
171      * @return a LoggerContext.
172      */
173     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
174         return factory.getContext(LogManager.class.getName(), loader, null, currentContext);
175     }
176 
177     /**
178      * Returns a LoggerContext.
179      *
180      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
181      * ClassLoader.
182      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
183      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
184      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
185      * returned. If true then only a single LoggerContext will be returned.
186      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
187      * @return a LoggerContext.
188      */
189     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
190                                            final Object externalContext) {
191         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext);
192     }
193 
194     /**
195      * Returns a LoggerContext.
196      *
197      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
198      * ClassLoader.
199      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
200      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
201      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
202      * returned. If true then only a single LoggerContext will be returned.
203      * @param configLocation The URI for the configuration to use.
204      * @return a LoggerContext.
205      */
206     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
207                                            final URI configLocation) {
208         return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null);
209     }
210 
211 
212     /**
213      * Returns a LoggerContext.
214      *
215      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
216      * ClassLoader.
217      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
218      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
219      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
220      * returned. If true then only a single LoggerContext will be returned.
221      * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext.
222      * @param configLocation The URI for the configuration to use.
223      * @return a LoggerContext.
224      */
225     public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext,
226                                            final Object externalContext, final URI configLocation) {
227         return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
228             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(LogManager.class.getName(), loader, externalContext, currentContext, configLocation,
250             name);
251     }
252 
253     /**
254      * Returns a LoggerContext
255      * @param fqcn The fully qualified class name of the Class that this method is a member of.
256      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
257      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
258      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
259      * returned. If true then only a single LoggerContext will be returned.
260      * @return a LoggerContext.
261      */
262     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
263         return factory.getContext(fqcn, null, null, currentContext);
264     }
265 
266     /**
267      * Returns a LoggerContext
268      * @param fqcn The fully qualified class name of the Class that this method is a member of.
269      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
270      * ClassLoader.
271      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
272      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
273      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
274      * returned. If true then only a single LoggerContext will be returned.
275      * @return a LoggerContext.
276      */
277     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
278                                               final boolean currentContext) {
279         return factory.getContext(fqcn, loader, null, currentContext);
280     }
281 
282     /**
283      * Returns the current LoggerContextFactory.
284      * @return The LoggerContextFactory.
285      */
286     public static LoggerContextFactory getFactory() {
287         return factory;
288     }
289 
290     /**
291      * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at
292      * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point.
293      * Thus, an alternative LoggerContextFactory can be set at runtime.
294      *
295      * <p>
296      * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be
297      * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>!
298      * Generally, this method should be used only during startup before any code starts caching Logger objects.
299      * </p>
300      *
301      * @param factory the LoggerContextFactory to use.
302      */
303     // FIXME: should we allow only one update of the factory?
304     public static void setFactory(final LoggerContextFactory factory) {
305         LogManager.factory = factory;
306     }
307 
308     /**
309      * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
310      * <p>
311      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
312      * </p>
313      * <p>
314      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
315      * </p>
316      *
317      * @param clazz
318      *            The Class whose name should be used as the Logger name.
319      * @return The Logger, created with a {@link StringFormatterMessageFactory}
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.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE);
336     }
337 
338     /**
339      * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
340      * <p>
341      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
342      * </p>
343      * <p>
344      * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
345      * </p>
346      *
347      * @param value
348      *            The value's whose class name should be used as the Logger name.
349      * @return The Logger, created with a {@link StringFormatterMessageFactory}
350      * @see Logger#fatal(Marker, String, Object...)
351      * @see Logger#fatal(String, Object...)
352      * @see Logger#error(Marker, String, Object...)
353      * @see Logger#error(String, Object...)
354      * @see Logger#warn(Marker, String, Object...)
355      * @see Logger#warn(String, Object...)
356      * @see Logger#info(Marker, String, Object...)
357      * @see Logger#info(String, Object...)
358      * @see Logger#debug(Marker, String, Object...)
359      * @see Logger#debug(String, Object...)
360      * @see Logger#trace(Marker, String, Object...)
361      * @see Logger#trace(String, Object...)
362      * @see StringFormatterMessageFactory
363      */
364     public static Logger getFormatterLogger(final Object value) {
365         return getLogger(value != null ? value.getClass().getName() : getClassName(2),
366                 StringFormatterMessageFactory.INSTANCE);
367     }
368 
369     /**
370      * Returns a formatter Logger with the specified name.
371      * <p>
372      * This logger let you use a {@link java.util.Formatter} string in the message to format parameters.
373      * </p>
374      * <p>
375      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
376      * </p>
377      *
378      * @param name The logger name. If null it will default to the name of the calling class.
379      * @return The Logger, created with a {@link StringFormatterMessageFactory}
380      * @see Logger#fatal(Marker, String, Object...)
381      * @see Logger#fatal(String, Object...)
382      * @see Logger#error(Marker, String, Object...)
383      * @see Logger#error(String, Object...)
384      * @see Logger#warn(Marker, String, Object...)
385      * @see Logger#warn(String, Object...)
386      * @see Logger#info(Marker, String, Object...)
387      * @see Logger#info(String, Object...)
388      * @see Logger#debug(Marker, String, Object...)
389      * @see Logger#debug(String, Object...)
390      * @see Logger#trace(Marker, String, Object...)
391      * @see Logger#trace(String, Object...)
392      * @see StringFormatterMessageFactory
393      */
394     public static Logger getFormatterLogger(final String name) {
395         return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE);
396     }
397 
398     /**
399      * Returns a Logger with the name of the calling class.
400      * @return The Logger for the calling class.
401      */
402     public static Logger getLogger() {
403         return getLogger(getClassName(2));
404     }
405 
406     /**
407      * Returns a Logger using the fully qualified name of the Class as the Logger name.
408      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
409      *              class.
410      * @return The Logger.
411      */
412     public static Logger getLogger(final Class<?> clazz) {
413         return getLogger(clazz != null ? clazz.getName() : getClassName(2));
414     }
415 
416     /**
417      * Returns a Logger using the fully qualified name of the Class as the Logger name.
418      * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling
419      *              class.
420      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
421      *                       the logger but will log a warning if mismatched.
422      * @return The Logger.
423      */
424     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
425         return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory);
426     }
427 
428     /**
429      * Returns a Logger with the name of the calling class.
430      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
431      *                       the logger but will log a warning if mismatched.
432      * @return The Logger for the calling class.
433      */
434     public static Logger getLogger(final MessageFactory messageFactory) {
435         return getLogger(getClassName(2), messageFactory);
436     }
437 
438     /**
439      * Returns a Logger using the fully qualified class name of the value as the Logger name.
440      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
441      *              class will be used as the logger name.
442      * @return The Logger.
443      */
444     public static Logger getLogger(final Object value) {
445         return getLogger(value != null ? value.getClass().getName() : getClassName(2));
446     }
447 
448     /**
449      * Returns a Logger using the fully qualified class name of the value as the Logger name.
450      * @param value The value whose class name should be used as the Logger name. If null the name of the calling
451      *              class will be used as the logger name.
452      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
453      *                       the logger but will log a warning if mismatched.
454      * @return The Logger.
455      */
456     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
457         return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory);
458     }
459 
460     /**
461      * Returns a Logger with the specified name.
462      *
463      * @param name The logger name. If null the name of the calling class will be used.
464      * @return The Logger.
465      */
466     public static Logger getLogger(final String name) {
467         final String actualName = name != null ? name : getClassName(2);
468         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName);
469     }
470 
471     /**
472      * Returns a Logger with the specified name.
473      *
474      * @param name The logger name. If null the name of the calling class will be used.
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      */
479     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
480         final String actualName = name != null ? name : getClassName(2);
481         return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory);
482     }
483 
484     /**
485      * Returns a Logger with the specified name.
486      *
487      * @param fqcn The fully qualified class name of the class that this method is a member of.
488      * @param name The logger name.
489      * @return The Logger.
490      */
491     protected static Logger getLogger(final String fqcn, final String name) {
492         return factory.getContext(fqcn, null, null, false).getLogger(name);
493     }
494 
495     /**
496      * Returns the root logger.
497      *
498      * @return the root logger, named {@link #ROOT_LOGGER_NAME}.
499      */
500     public static Logger getRootLogger() {
501         return getLogger(ROOT_LOGGER_NAME);
502     }
503 
504     /**
505      * Prevents instantiation
506      */
507     protected LogManager() {
508     }
509 
510 }