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