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 org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
20  import org.apache.logging.log4j.status.StatusLogger;
21  import org.apache.logging.log4j.spi.LoggerContext;
22  import org.apache.logging.log4j.spi.LoggerContextFactory;
23  import org.apache.logging.log4j.util.PropsUtil;
24  
25  import java.io.IOException;
26  import java.net.URL;
27  import java.util.Enumeration;
28  import java.util.Map;
29  import java.util.Properties;
30  import java.util.SortedMap;
31  import java.util.TreeMap;
32  
33  /**
34   * The anchor point for the logging system.
35   */
36  public class LogManager {
37      /**
38       * The name of the root Logger.
39       */
40      public static final String ROOT_LOGGER_NAME = "";
41  
42      private static final String LOGGER_RESOURCE = "META-INF/log4j-provider.properties";
43      private static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory";
44      private static final String API_VERSION = "Log4jAPIVersion";
45      private static final String FACTORY_PRIORITY = "FactoryPriority";
46      private static final String[] COMPATIBLE_API_VERSIONS = {
47          "2.0.0"
48      };
49  
50      private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory";
51  
52      private static LoggerContextFactory factory;
53  
54      private static final Logger logger = StatusLogger.getLogger();
55  
56      /**
57       * Prevents instantiation
58       */
59      protected LogManager() {
60      }
61  
62      /**
63       * Scans the classpath to find all logging implementation. Currently, only one will
64       * be used but this could be extended to allow multiple implementations to be used.
65       */
66      static {
67          // Shortcut binding to force a specific logging implementation.
68          PropsUtil managerProps = new PropsUtil("log4j2.LogManager.properties");
69          String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME);
70          ClassLoader cl = findClassLoader();
71          if (factoryClass != null) {
72              try {
73                  Class<?> clazz = cl.loadClass(factoryClass);
74                  if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
75                      factory = (LoggerContextFactory) clazz.newInstance();
76                  }
77              } catch (ClassNotFoundException cnfe) {
78                  logger.error("Unable to locate configured LoggerContextFactory {}", factoryClass);
79              } catch (Exception ex) {
80                  logger.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex);
81              }
82          }
83  
84          if (factory == null) {
85              SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>();
86  
87              Enumeration<URL> enumResources = null;
88              try {
89                  enumResources = cl.getResources(LOGGER_RESOURCE);
90              } catch (IOException e) {
91                  logger.fatal("Unable to locate " + LOGGER_RESOURCE, e);
92              }
93  
94              if (enumResources != null) {
95                  while (enumResources.hasMoreElements()) {
96                      Properties props = new Properties();
97                      URL url = enumResources.nextElement();
98                      try {
99                          props.load(url.openStream());
100                     } catch (IOException ioe) {
101                         logger.error("Unable to read " + url.toString(), ioe);
102                     }
103                     if (!validVersion(props.getProperty(API_VERSION))) {
104                         continue;
105                     }
106                     String weight = props.getProperty(FACTORY_PRIORITY);
107                     Integer priority = weight == null ? -1 : Integer.valueOf(weight);
108                     String className = props.getProperty(LOGGER_CONTEXT_FACTORY);
109                     if (className != null) {
110                         try {
111                             Class<?> clazz = cl.loadClass(className);
112                             if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
113                                 factories.put(priority, (LoggerContextFactory) clazz.newInstance());
114                             } else {
115                                 logger.error(className + " does not implement " + LoggerContextFactory.class.getName());
116                             }
117                         } catch (ClassNotFoundException cnfe) {
118                             logger.error("Unable to locate class " + className + " specified in " + url.toString(),
119                                 cnfe);
120                         } catch (IllegalAccessException iae) {
121                             logger.error("Unable to create class " + className + " specified in " + url.toString(),
122                                 iae);
123                         } catch (Exception e) {
124                             logger.error("Unable to create class " + className + " specified in " + url.toString(), e);
125                             e.printStackTrace();
126                         }
127                     }
128                 }
129                 if (factories.size() == 0) {
130                     logger.error("Unable to locate a logging implementation, using SimpleLogger");
131                     factory = new SimpleLoggerContextFactory();
132                 } else {
133                     StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n");
134                     for (Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) {
135                         sb.append("Factory: ").append(entry.getValue().getClass().getName());
136                         sb.append(", Weighting: ").append(entry.getKey()).append("\n");
137                     }
138                     factory = factories.get(factories.lastKey());
139                     sb.append("Using factory: ").append(factory.getClass().getName());
140                     logger.warn(sb.toString());
141 
142                 }
143             } else {
144                 logger.error("Unable to locate a logging implementation, using SimpleLogger");
145                 factory = new SimpleLoggerContextFactory();
146             }
147         }
148     }
149 
150     /**
151      * Returns the LoggerContextFactory.
152      * @return The LoggerContextFactory.
153      */
154     public static LoggerContextFactory getFactory() {
155         return factory;
156     }
157 
158     /**
159      * Returns a Logger with the specified name.
160      *
161      * @param name The logger name.
162      * @return The Logger.
163      */
164     public static Logger getLogger(String name) {
165         return factory.getContext(LogManager.class.getName(), null, false).getLogger(name);
166     }
167 
168     /**
169      * Returns a Logger using the fully qualified name of the Class as the Logger name.
170      * @param clazz The Class whose name should be used as the Logger name.
171      * @return The Logger.
172      */
173     public static Logger getLogger(Class<?> clazz) {
174         return getLogger(clazz != null ? clazz.getName() : null);
175     }
176 
177     /**
178      * Returns a Logger using the fully qualified class name of the value as the Logger name.
179      * @param value The value whose class name should be used as the Logger name.
180      * @return The Logger.
181      */
182     public static Logger getLogger(Object value) {
183         return getLogger(value != null ? value.getClass() : null);
184     }
185 
186     /**
187      * Returns a Logger with the specified name.
188      *
189      * @param fqcn The fully qualified class name of the class that this method is a member of.
190      * @param name The logger name.
191      * @return The Logger.
192      */
193     protected static Logger getLogger(String fqcn, String name) {
194         return factory.getContext(fqcn, null, false).getLogger(name);
195     }
196 
197     /**
198      * Returns the current LoggerContext.
199      * <p>
200      * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger
201      * for the calling class.
202      * @return  The current LoggerContext.
203      */
204     public static LoggerContext getContext() {
205         return factory.getContext(LogManager.class.getName(), null, true);
206     }
207 
208     /**
209      * Returns a LoggerContext.
210      *
211      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
212      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
213      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
214      * returned. If true then only a single LoggerContext will be returned.
215      * @return a LoggerContext.
216      */
217     public static LoggerContext getContext(boolean currentContext) {
218         return factory.getContext(LogManager.class.getName(), null, currentContext);
219     }
220 
221     /**
222      * Returns a LoggerContext.
223      *
224      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
225      * ClassLoader.
226      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
227      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
228      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
229      * returned. If true then only a single LoggerContext will be returned.
230      * @return a LoggerContext.
231      */
232     public static LoggerContext getContext(ClassLoader loader, boolean currentContext) {
233         return factory.getContext(LogManager.class.getName(), loader, currentContext);
234     }
235 
236 
237     /**
238      * Returns a LoggerContext
239      * @param fqcn The fully qualified class name of the Class that this method is a member of.
240      * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For
241      * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be
242      * returned and if the caller is a class in the container's classpath then a different LoggerContext may be
243      * returned. If true then only a single LoggerContext will be returned.
244      * @return a LoggerContext.
245      */
246     protected static LoggerContext getContext(String fqcn, boolean currentContext) {
247         return factory.getContext(fqcn, null, currentContext);
248     }
249 
250 
251     /**
252      * Returns a LoggerContext
253      * @param fqcn The fully qualified class name of the Class that this method is a member of.
254      * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate
255      * ClassLoader.
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(String fqcn, ClassLoader loader, boolean currentContext) {
263         return factory.getContext(fqcn, loader, currentContext);
264     }
265 
266     private static ClassLoader findClassLoader() {
267         ClassLoader cl;
268         if (System.getSecurityManager() == null) {
269             cl = Thread.currentThread().getContextClassLoader();
270         } else {
271             cl = java.security.AccessController.doPrivileged(
272                 new java.security.PrivilegedAction<ClassLoader>() {
273                     public ClassLoader run() {
274                         return Thread.currentThread().getContextClassLoader();
275                     }
276                 }
277             );
278         }
279         if (cl == null) {
280             cl = LogManager.class.getClassLoader();
281         }
282 
283         return cl;
284     }
285 
286     private static boolean validVersion(String version) {
287         for (String v : COMPATIBLE_API_VERSIONS) {
288             if (version.startsWith(v)) {
289                 return true;
290             }
291         }
292         return false;
293     }
294 
295 }