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.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 private static LoggerContextFactory factory; 41 42 private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 43 44 private static final Logger LOGGER = StatusLogger.getLogger(); 45 46 /** 47 * The name of the root Logger. 48 */ 49 public static final String ROOT_LOGGER_NAME = ""; 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 * Gets the class name of the caller in the current stack at the given {@code depth}. 126 * 127 * @param depth a 0-based index in the current stack. 128 * @return a class name 129 */ 130 private static String getClassName(final int depth) { 131 return new Throwable().getStackTrace()[depth].getClassName(); 132 } 133 134 /** 135 * Returns the current LoggerContext. 136 * <p> 137 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger 138 * for the calling class. 139 * @return The current LoggerContext. 140 */ 141 public static LoggerContext getContext() { 142 return factory.getContext(LogManager.class.getName(), null, null, true); 143 } 144 145 /** 146 * Returns a LoggerContext. 147 * 148 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 149 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 150 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 151 * returned. If true then only a single LoggerContext will be returned. 152 * @return a LoggerContext. 153 */ 154 public static LoggerContext getContext(final boolean currentContext) { 155 return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null); 156 } 157 158 /** 159 * Returns a LoggerContext. 160 * 161 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 162 * ClassLoader. 163 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 164 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 165 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 166 * returned. If true then only a single LoggerContext will be returned. 167 * @return a LoggerContext. 168 */ 169 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 170 return factory.getContext(LogManager.class.getName(), loader, null, currentContext); 171 } 172 173 /** 174 * Returns a LoggerContext. 175 * 176 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 177 * ClassLoader. 178 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 179 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 180 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 181 * returned. If true then only a single LoggerContext will be returned. 182 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 183 * @return a LoggerContext. 184 */ 185 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 186 final Object externalContext) { 187 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext); 188 } 189 190 /** 191 * Returns a LoggerContext. 192 * 193 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 194 * ClassLoader. 195 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 196 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 197 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 198 * returned. If true then only a single LoggerContext will be returned. 199 * @param configLocation The URI for the configuration to use. 200 * @return a LoggerContext. 201 */ 202 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 203 final URI configLocation) { 204 return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null); 205 } 206 207 208 /** 209 * Returns a LoggerContext. 210 * 211 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 212 * ClassLoader. 213 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 214 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 215 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 216 * returned. If true then only a single LoggerContext will be returned. 217 * @param configLocation The URI for the configuration to use. 218 * @return a LoggerContext. 219 */ 220 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 221 final Object externalContext, final URI configLocation) { 222 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 223 null); 224 } 225 226 227 /** 228 * Returns a LoggerContext. 229 * 230 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 231 * ClassLoader. 232 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 233 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 234 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 235 * returned. If true then only a single LoggerContext will be returned. 236 * @param configLocation The URI for the configuration to use. 237 * @param name The LoggerContext name. 238 * @return a LoggerContext. 239 */ 240 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 241 final Object externalContext, final URI configLocation, 242 final String name) { 243 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 244 name); 245 } 246 247 /** 248 * Returns a LoggerContext 249 * @param fqcn The fully qualified class name of the Class that this method is a member of. 250 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 251 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 252 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 253 * returned. If true then only a single LoggerContext will be returned. 254 * @return a LoggerContext. 255 */ 256 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 257 return factory.getContext(fqcn, null, null, currentContext); 258 } 259 260 /** 261 * Returns a LoggerContext 262 * @param fqcn The fully qualified class name of the Class that this method is a member of. 263 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 264 * ClassLoader. 265 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 266 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 267 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 268 * returned. If true then only a single LoggerContext will be returned. 269 * @return a LoggerContext. 270 */ 271 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, 272 final boolean currentContext) { 273 return factory.getContext(fqcn, loader, null, currentContext); 274 } 275 276 /** 277 * Returns the LoggerContextFactory. 278 * @return The LoggerContextFactory. 279 */ 280 public static LoggerContextFactory getFactory() { 281 return factory; 282 } 283 284 /** 285 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. 286 * <p> 287 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 288 * </p> 289 * <p> 290 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} 291 * </p> 292 * 293 * @param clazz 294 * The Class whose name should be used as the Logger name. 295 * @return The Logger, created with a {@link StringFormatterMessageFactory} 296 * @see Logger#fatal(Marker, String, Object...) 297 * @see Logger#fatal(String, Object...) 298 * @see Logger#error(Marker, String, Object...) 299 * @see Logger#error(String, Object...) 300 * @see Logger#warn(Marker, String, Object...) 301 * @see Logger#warn(String, Object...) 302 * @see Logger#info(Marker, String, Object...) 303 * @see Logger#info(String, Object...) 304 * @see Logger#debug(Marker, String, Object...) 305 * @see Logger#debug(String, Object...) 306 * @see Logger#trace(Marker, String, Object...) 307 * @see Logger#trace(String, Object...) 308 * @see StringFormatterMessageFactory 309 */ 310 public static Logger getFormatterLogger(final Class<?> clazz) { 311 return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE); 312 } 313 314 /** 315 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 316 * <p> 317 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 318 * </p> 319 * <p> 320 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 321 * </p> 322 * 323 * @param value 324 * The value's whose class name should be used as the Logger name. 325 * @return The Logger, created with a {@link StringFormatterMessageFactory} 326 * @see Logger#fatal(Marker, String, Object...) 327 * @see Logger#fatal(String, Object...) 328 * @see Logger#error(Marker, String, Object...) 329 * @see Logger#error(String, Object...) 330 * @see Logger#warn(Marker, String, Object...) 331 * @see Logger#warn(String, Object...) 332 * @see Logger#info(Marker, String, Object...) 333 * @see Logger#info(String, Object...) 334 * @see Logger#debug(Marker, String, Object...) 335 * @see Logger#debug(String, Object...) 336 * @see Logger#trace(Marker, String, Object...) 337 * @see Logger#trace(String, Object...) 338 * @see StringFormatterMessageFactory 339 */ 340 public static Logger getFormatterLogger(final Object value) { 341 return getLogger(value != null ? value.getClass().getName() : getClassName(2), 342 StringFormatterMessageFactory.INSTANCE); 343 } 344 345 /** 346 * Returns a formatter Logger with the specified name. 347 * <p> 348 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 349 * </p> 350 * <p> 351 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 352 * </p> 353 * 354 * @param name The logger name. If null it will default to the name of the calling class. 355 * @return The Logger, created with a {@link StringFormatterMessageFactory} 356 * @see Logger#fatal(Marker, String, Object...) 357 * @see Logger#fatal(String, Object...) 358 * @see Logger#error(Marker, String, Object...) 359 * @see Logger#error(String, Object...) 360 * @see Logger#warn(Marker, String, Object...) 361 * @see Logger#warn(String, Object...) 362 * @see Logger#info(Marker, String, Object...) 363 * @see Logger#info(String, Object...) 364 * @see Logger#debug(Marker, String, Object...) 365 * @see Logger#debug(String, Object...) 366 * @see Logger#trace(Marker, String, Object...) 367 * @see Logger#trace(String, Object...) 368 * @see StringFormatterMessageFactory 369 */ 370 public static Logger getFormatterLogger(final String name) { 371 return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE); 372 } 373 374 /** 375 * Returns a Logger with the name of the calling class. 376 * @return The Logger for the calling class. 377 */ 378 public static Logger getLogger() { 379 return getLogger(getClassName(2)); 380 } 381 382 /** 383 * Returns a Logger using the fully qualified name of the Class as the Logger name. 384 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 385 * class. 386 * @return The Logger. 387 */ 388 public static Logger getLogger(final Class<?> clazz) { 389 return getLogger(clazz != null ? clazz.getName() : getClassName(2)); 390 } 391 392 /** 393 * Returns a Logger using the fully qualified name of the Class as the Logger name. 394 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 395 * class. 396 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 397 * the logger but will log a warning if mismatched. 398 * @return The Logger. 399 */ 400 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 401 return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory); 402 } 403 404 /** 405 * Returns a Logger with the name of the calling class. 406 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 407 * the logger but will log a warning if mismatched. 408 * @return The Logger for the calling class. 409 */ 410 public static Logger getLogger(final MessageFactory messageFactory) { 411 return getLogger(getClassName(2), messageFactory); 412 } 413 414 /** 415 * Returns a Logger using the fully qualified class name of the value as the Logger name. 416 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 417 * class will be used as the logger name. 418 * @return The Logger. 419 */ 420 public static Logger getLogger(final Object value) { 421 return getLogger(value != null ? value.getClass().getName() : getClassName(2)); 422 } 423 424 /** 425 * Returns a Logger using the fully qualified class name of the value as the Logger name. 426 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 427 * class will be used as the logger name. 428 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 429 * the logger but will log a warning if mismatched. 430 * @return The Logger. 431 */ 432 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 433 return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory); 434 } 435 436 /** 437 * Returns a Logger with the specified name. 438 * 439 * @param name The logger name. If null the name of the calling class will be used. 440 * @return The Logger. 441 */ 442 public static Logger getLogger(final String name) { 443 final String actualName = name != null ? name : getClassName(2); 444 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName); 445 } 446 447 /** 448 * Returns a Logger with the specified name. 449 * 450 * @param name The logger name. If null the name of the calling class will be used. 451 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 452 * the logger but will log a warning if mismatched. 453 * @return The Logger. 454 */ 455 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 456 final String actualName = name != null ? name : getClassName(2); 457 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory); 458 } 459 460 /** 461 * Returns a Logger with the specified name. 462 * 463 * @param fqcn The fully qualified class name of the class that this method is a member of. 464 * @param name The logger name. 465 * @return The Logger. 466 */ 467 protected static Logger getLogger(final String fqcn, final String name) { 468 return factory.getContext(fqcn, null, null, false).getLogger(name); 469 } 470 471 /** 472 * Returns the root logger. 473 * 474 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 475 */ 476 public static Logger getRootLogger() { 477 return getLogger(ROOT_LOGGER_NAME); 478 } 479 480 /** 481 * Prevents instantiation 482 */ 483 protected LogManager() { 484 } 485 486 }