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.util.Formatter;
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       * The name of the root Logger.
41       */
42      public static final String ROOT_LOGGER_NAME = "";
43  
44      private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
45  
46      private static LoggerContextFactory factory;
47  
48      private static final Logger LOGGER = StatusLogger.getLogger();
49  
50      /**
51       * Scans the classpath to find all logging implementation. Currently, only one will
52       * be used but this could be extended to allow multiple implementations to be used.
53       */
54      static {
55          // Shortcut binding to force a specific logging implementation.
56          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
57          final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
58          final ClassLoader cl = ProviderUtil.findClassLoader();
59          if (factoryClass != null) {
60              try {
61                  final Class<?> clazz = cl.loadClass(factoryClass);
62                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
63                      factory = (LoggerContextFactory) clazz.newInstance();
64                  }
65              } catch (final ClassNotFoundException cnfe) {
66                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
67              } catch (final Exception ex) {
68                  LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
69              }
70          }
71  
72          if (factory == null) {
73              final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
74  
75              if (ProviderUtil.hasProviders()) {
76                  final Iterator<Provider> providers = ProviderUtil.getProviders();
77                  while (providers.hasNext()) {
78                      final Provider provider = providers.next();
79                      final String className = provider.getClassName();
80                      if (className != null) {
81                          try {
82                              final Class<?> clazz = cl.loadClass(className);
83                              if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
84                                  factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance());
85                              } else {
86                                  LOGGER.error(className + " does not implement " + LoggerContextFactory.class.getName());
87                              }
88                          } catch (final ClassNotFoundException cnfe) {
89                              LOGGER.error("Unable to locate class " + className + " specified in " +
90                                  provider.getURL().toString(), cnfe);
91                          } catch (final IllegalAccessException iae) {
92                              LOGGER.error("Unable to create class " + className + " specified in " +
93                                  provider.getURL().toString(), iae);
94                          } catch (final Exception e) {
95                              LOGGER.error("Unable to create class " + className + " specified in " +
96                                  provider.getURL().toString(), e);
97                              e.printStackTrace();
98                          }
99                      }
100                 }
101 
102                 if (factories.size() == 0) {
103                     LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
104                     factory = new SimpleLoggerContextFactory();
105                 } else {
106                     final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
107                     for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
108                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
109                         sb.append(", Weighting: ").append(entry.getKey()).append("\n");
110                     }
111                     factory = factories.get(factories.lastKey());
112                     sb.append("Using factory: ").append(factory.getClass().getName());
113                     LOGGER.warn(sb.toString());
114 
115                 }
116             } else {
117                 LOGGER.error("Unable to locate a logging implementation, using SimpleLogger");
118                 factory = new SimpleLoggerContextFactory();
119             }
120         }
121     }
122 
123     /**
124      * Prevents instantiation
125      */
126     protected LogManager() {
127     }
128 
129     /**
130      * Returns the current LoggerContext.
131      * <p>
132      * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
133      * for the calling class.
134      * @return  The current LoggerContext.
135      */
136     public static LoggerContext getContext() {
137         return factory.getContext(LogManager.class.getName(), null, true);
138     }
139 
140     /**
141      * Returns a LoggerContext.
142      *
143      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
144      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
145      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
146      * returned. If true then only a single LoggerContext will be returned.
147      * @return a LoggerContext.
148      */
149     public static LoggerContext getContext(final boolean currentContext) {
150         return factory.getContext(LogManager.class.getName(), null, currentContext);
151     }
152 
153     /**
154      * Returns a LoggerContext.
155      *
156      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
157      * ClassLoader.
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 ClassLoader loader, final boolean currentContext) {
165         return factory.getContext(LogManager.class.getName(), loader, currentContext);
166     }
167 
168     /**
169      * Returns a LoggerContext
170      * @param fqcn The fully qualified class name of the Class that this method is a member of.
171      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
172      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
173      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
174      * returned. If true then only a single LoggerContext will be returned.
175      * @return a LoggerContext.
176      */
177     protected static LoggerContext getContext(final String fqcn, final boolean currentContext) {
178         return factory.getContext(fqcn, null, currentContext);
179     }
180 
181     /**
182      * Returns a LoggerContext
183      * @param fqcn The fully qualified class name of the Class that this method is a member of.
184      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
185      * ClassLoader.
186      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
187      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
188      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
189      * returned. If true then only a single LoggerContext will be returned.
190      * @return a LoggerContext.
191      */
192     protected static LoggerContext getContext(final String fqcn, final ClassLoader loader,
193                                               final boolean currentContext) {
194         return factory.getContext(fqcn, loader, currentContext);
195     }
196 
197     /**
198      * Returns the LoggerContextFactory.
199      * @return The LoggerContextFactory.
200      */
201     public static LoggerContextFactory getFactory() {
202         return factory;
203     }
204 
205     /**
206      * Returns a formatter Logger using the fully qualified name of the Class as the Logger name.
207      * <p>
208      * This logger let you use a {@link Formatter} string in the message to format parameters.
209      * </p>
210      * <p>
211      * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)}
212      * </p>
213      *
214      * @param clazz
215      *            The Class whose name should be used as the Logger name.
216      * @return The Logger, created with a {@link StringFormatterMessageFactory}
217      * @see Logger#fatal(Marker, String, Object...)
218      * @see Logger#fatal(String, Object...)
219      * @see Logger#error(Marker, String, Object...)
220      * @see Logger#error(String, Object...)
221      * @see Logger#warn(Marker, String, Object...)
222      * @see Logger#warn(String, Object...)
223      * @see Logger#info(Marker, String, Object...)
224      * @see Logger#info(String, Object...)
225      * @see Logger#debug(Marker, String, Object...)
226      * @see Logger#debug(String, Object...)
227      * @see Logger#trace(Marker, String, Object...)
228      * @see Logger#trace(String, Object...)
229      * @see StringFormatterMessageFactory
230      */
231     public static Logger getFormatterLogger(final Class<?> clazz) {
232         return getLogger(clazz, StringFormatterMessageFactory.INSTANCE);
233     }
234 
235     /**
236      * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name.
237      * <p>
238      * This logger let you use a {@link Formatter} string in the message to format parameters.
239      * </p>
240      * <p>
241      * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)}
242      * </p>
243      *
244      * @param value
245      *            The value's whose class name should be used as the Logger name.
246      * @return The Logger, created with a {@link StringFormatterMessageFactory}
247      * @see Logger#fatal(Marker, String, Object...)
248      * @see Logger#fatal(String, Object...)
249      * @see Logger#error(Marker, String, Object...)
250      * @see Logger#error(String, Object...)
251      * @see Logger#warn(Marker, String, Object...)
252      * @see Logger#warn(String, Object...)
253      * @see Logger#info(Marker, String, Object...)
254      * @see Logger#info(String, Object...)
255      * @see Logger#debug(Marker, String, Object...)
256      * @see Logger#debug(String, Object...)
257      * @see Logger#trace(Marker, String, Object...)
258      * @see Logger#trace(String, Object...)
259      * @see StringFormatterMessageFactory
260      */
261     public static Logger getFormatterLogger(final Object value) {
262         return getLogger(value, StringFormatterMessageFactory.INSTANCE);
263     }
264 
265     /**
266      * Returns a formatter Logger with the specified name.
267      * <p>
268      * This logger let you use a {@link Formatter} string in the message to format parameters.
269      * </p>
270      * <p>
271      * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)}
272      * </p>
273      *
274      * @param name
275      *            The logger name.
276      * @return The Logger, created with a {@link StringFormatterMessageFactory}
277      * @see Logger#fatal(Marker, String, Object...)
278      * @see Logger#fatal(String, Object...)
279      * @see Logger#error(Marker, String, Object...)
280      * @see Logger#error(String, Object...)
281      * @see Logger#warn(Marker, String, Object...)
282      * @see Logger#warn(String, Object...)
283      * @see Logger#info(Marker, String, Object...)
284      * @see Logger#info(String, Object...)
285      * @see Logger#debug(Marker, String, Object...)
286      * @see Logger#debug(String, Object...)
287      * @see Logger#trace(Marker, String, Object...)
288      * @see Logger#trace(String, Object...)
289      * @see StringFormatterMessageFactory
290      */
291     public static Logger getFormatterLogger(final String name) {
292         return getLogger(name, StringFormatterMessageFactory.INSTANCE);
293     }
294 
295     /**
296      * Returns a Logger using the fully qualified name of the Class as the Logger name.
297      * @param clazz The Class whose name should be used as the Logger name.
298      * @return The Logger.
299      */
300     public static Logger getLogger(final Class<?> clazz) {
301         return getLogger(clazz != null ? clazz.getName() : null);
302     }
303 
304     /**
305      * Returns a Logger using the fully qualified name of the Class as the Logger name.
306      * @param clazz The Class whose name should be used as the Logger name.
307      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
308      *                       the logger but will log a warning if mismatched.
309      * @return The Logger.
310      */
311     public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) {
312         return getLogger(clazz != null ? clazz.getName() : null, messageFactory);
313     }
314 
315     /**
316      * Returns a Logger using the fully qualified class name of the value as the Logger name.
317      * @param value The value whose class name should be used as the Logger name.
318      * @return The Logger.
319      */
320     public static Logger getLogger(final Object value) {
321         return getLogger(value != null ? value.getClass() : null);
322     }
323 
324     /**
325      * Returns a Logger using the fully qualified class name of the value as the Logger name.
326      * @param value The value whose class name should be used as the Logger name.
327      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
328      *                       the logger but will log a warning if mismatched.
329      * @return The Logger.
330      */
331     public static Logger getLogger(final Object value, final MessageFactory messageFactory) {
332         return getLogger(value != null ? value.getClass() : null, messageFactory);
333     }
334 
335     /**
336      * Returns a Logger with the specified name.
337      *
338      * @param name The logger name.
339      * @return The Logger.
340      */
341     public static Logger getLogger(final String name) {
342         return factory.getContext(LogManager.class.getName(), null, false).getLogger(name);
343     }
344 
345     /**
346      * Returns a Logger with the specified name.
347      *
348      * @param name The logger name.
349      * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
350      *                       the logger but will log a warning if mismatched.
351      * @return The Logger.
352      */
353     public static Logger getLogger(final String name, final MessageFactory messageFactory) {
354         return factory.getContext(LogManager.class.getName(), null, false).getLogger(name, messageFactory);
355     }
356 
357     /**
358      * Returns a Logger with the specified name.
359      *
360      * @param fqcn The fully qualified class name of the class that this method is a member of.
361      * @param name The logger name.
362      * @return The Logger.
363      */
364     protected static Logger getLogger(final String fqcn, final String name) {
365         return factory.getContext(fqcn, null, false).getLogger(name);
366     }
367 }