001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j; 018 019 import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; 020 import org.apache.logging.log4j.status.StatusLogger; 021 import org.apache.logging.log4j.spi.LoggerContext; 022 import org.apache.logging.log4j.spi.LoggerContextFactory; 023 import org.apache.logging.log4j.util.PropsUtil; 024 025 import java.io.IOException; 026 import java.net.URL; 027 import java.util.Enumeration; 028 import java.util.Map; 029 import java.util.Properties; 030 import java.util.SortedMap; 031 import java.util.TreeMap; 032 033 /** 034 * The anchor point for the logging system. 035 */ 036 public class LogManager { 037 /** 038 * The name of the root Logger. 039 */ 040 public static final String ROOT_LOGGER_NAME = ""; 041 042 private static final String LOGGER_RESOURCE = "META-INF/log4j-provider.properties"; 043 private static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory"; 044 private static final String API_VERSION = "Log4jAPIVersion"; 045 private static final String FACTORY_PRIORITY = "FactoryPriority"; 046 private static final String[] COMPATIBLE_API_VERSIONS = { 047 "2.0.0" 048 }; 049 050 private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 051 052 private static LoggerContextFactory factory; 053 054 private static final Logger logger = StatusLogger.getLogger(); 055 056 /** 057 * Prevents instantiation 058 */ 059 protected LogManager() { 060 } 061 062 /** 063 * Scans the classpath to find all logging implementation. Currently, only one will 064 * be used but this could be extended to allow multiple implementations to be used. 065 */ 066 static { 067 // Shortcut binding to force a specific logging implementation. 068 PropsUtil managerProps = new PropsUtil("log4j2.LogManager.properties"); 069 String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); 070 ClassLoader cl = findClassLoader(); 071 if (factoryClass != null) { 072 try { 073 Class<?> clazz = cl.loadClass(factoryClass); 074 if (LoggerContextFactory.class.isAssignableFrom(clazz)) { 075 factory = (LoggerContextFactory) clazz.newInstance(); 076 } 077 } catch (ClassNotFoundException cnfe) { 078 logger.error("Unable to locate configured LoggerContextFactory {}", factoryClass); 079 } catch (Exception ex) { 080 logger.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex); 081 } 082 } 083 084 if (factory == null) { 085 SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>(); 086 087 Enumeration<URL> enumResources = null; 088 try { 089 enumResources = cl.getResources(LOGGER_RESOURCE); 090 } catch (IOException e) { 091 logger.fatal("Unable to locate " + LOGGER_RESOURCE, e); 092 } 093 094 if (enumResources != null) { 095 while (enumResources.hasMoreElements()) { 096 Properties props = new Properties(); 097 URL url = enumResources.nextElement(); 098 try { 099 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 }