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.Map; 21 import java.util.SortedMap; 22 import java.util.TreeMap; 23 24 import org.apache.logging.log4j.message.MessageFactory; 25 import org.apache.logging.log4j.message.StringFormatterMessageFactory; 26 import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; 27 import org.apache.logging.log4j.spi.LoggerContext; 28 import org.apache.logging.log4j.spi.LoggerContextFactory; 29 import org.apache.logging.log4j.spi.Provider; 30 import org.apache.logging.log4j.status.StatusLogger; 31 import org.apache.logging.log4j.util.LoaderUtil; 32 import org.apache.logging.log4j.util.PropertiesUtil; 33 import org.apache.logging.log4j.util.ProviderUtil; 34 import org.apache.logging.log4j.util.Strings; 35 36 /** 37 * The anchor point for the logging system. The most common usage of this class is to obtain a named 38 * {@link Logger}. The method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger 39 * based on the calling class name. This class also provides method for obtaining named Loggers that use 40 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. 41 * These are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods 42 * are given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not 43 * normally useful for typical usage of Log4j. 44 */ 45 public class LogManager { 46 47 private static volatile LoggerContextFactory factory; 48 49 /** 50 * Log4j property to set to the fully qualified class name of a custom implementation of 51 * {@link org.apache.logging.log4j.spi.LoggerContextFactory}. 52 */ 53 public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 54 55 private static final Logger LOGGER = StatusLogger.getLogger(); 56 57 /** 58 * The name of the root Logger. 59 */ 60 public static final String ROOT_LOGGER_NAME = Strings.EMPTY; 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 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 69 final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); 70 final ClassLoader cl = LoaderUtil.getThreadContextClassLoader(); 71 if (factoryClassName != null) { 72 try { 73 final Class<?> clazz = cl.loadClass(factoryClassName); 74 if (LoggerContextFactory.class.isAssignableFrom(clazz)) { 75 factory = (LoggerContextFactory) clazz.newInstance(); 76 } 77 } catch (final ClassNotFoundException cnfe) { 78 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName); 79 } catch (final Exception ex) { 80 LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex); 81 } 82 } 83 84 if (factory == null) { 85 final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>(); 86 87 if (ProviderUtil.hasProviders()) { 88 for (final Provider provider : ProviderUtil.getProviders()) { 89 final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory(); 90 if (factoryClass != null) { 91 try { 92 factories.put(provider.getPriority(), factoryClass.newInstance()); 93 } catch (final Exception e) { 94 LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), 95 provider.getUrl().toString(), e); 96 } 97 } 98 } 99 100 if (factories.isEmpty()) { 101 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 102 factory = new SimpleLoggerContextFactory(); 103 } else { 104 final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); 105 for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { 106 sb.append("Factory: ").append(entry.getValue().getClass().getName()); 107 sb.append(", Weighting: ").append(entry.getKey()).append('\n'); 108 } 109 factory = factories.get(factories.lastKey()); 110 sb.append("Using factory: ").append(factory.getClass().getName()); 111 LOGGER.warn(sb.toString()); 112 113 } 114 } else { 115 LOGGER.error("Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 116 factory = new SimpleLoggerContextFactory(); 117 } 118 } 119 } 120 121 /** 122 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. 123 * 124 * @param name 125 * The Logger name to search for. 126 * @return true if the Logger exists, false otherwise. 127 * @see LoggerContext#hasLogger(String) 128 */ 129 public static boolean exists(final String name) { 130 return getContext().hasLogger(name); 131 } 132 133 /** 134 * Gets the class name of the caller in the current stack at the given {@code depth}. 135 * 136 * @param depth a 0-based index in the current stack. 137 * @return a class name 138 */ 139 private static String getClassName(final int depth) { 140 return new Throwable().getStackTrace()[depth].getClassName(); 141 } 142 143 /** 144 * Returns the current LoggerContext. 145 * <p> 146 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger 147 * for the calling class. 148 * </p> 149 * @return The current LoggerContext. 150 */ 151 public static LoggerContext getContext() { 152 return factory.getContext(LogManager.class.getName(), null, null, true); 153 } 154 155 /** 156 * Returns a LoggerContext. 157 * 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 boolean currentContext) { 165 return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null); 166 } 167 168 /** 169 * Returns a LoggerContext. 170 * 171 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 172 * ClassLoader. 173 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 174 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 175 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 176 * returned. If true then only a single LoggerContext will be returned. 177 * @return a LoggerContext. 178 */ 179 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 180 return factory.getContext(LogManager.class.getName(), loader, null, currentContext); 181 } 182 183 /** 184 * Returns a LoggerContext. 185 * 186 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 187 * ClassLoader. 188 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 189 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 190 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 191 * returned. If true then only a single LoggerContext will be returned. 192 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 193 * @return a LoggerContext. 194 */ 195 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 196 final Object externalContext) { 197 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext); 198 } 199 200 /** 201 * Returns a LoggerContext. 202 * 203 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 204 * ClassLoader. 205 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 206 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 207 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 208 * returned. If true then only a single LoggerContext will be returned. 209 * @param configLocation The URI for the configuration to use. 210 * @return a LoggerContext. 211 */ 212 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 213 final URI configLocation) { 214 return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null); 215 } 216 217 218 /** 219 * Returns a LoggerContext. 220 * 221 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 222 * ClassLoader. 223 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 224 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 225 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 226 * returned. If true then only a single LoggerContext will be returned. 227 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 228 * @param configLocation The URI for the configuration to use. 229 * @return a LoggerContext. 230 */ 231 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 232 final Object externalContext, final URI configLocation) { 233 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 234 null); 235 } 236 237 238 /** 239 * Returns a LoggerContext. 240 * 241 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 242 * ClassLoader. 243 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 244 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 245 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 246 * returned. If true then only a single LoggerContext will be returned. 247 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 248 * @param configLocation The URI for the configuration to use. 249 * @param name The LoggerContext name. 250 * @return a LoggerContext. 251 */ 252 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 253 final Object externalContext, final URI configLocation, 254 final String name) { 255 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 256 name); 257 } 258 259 /** 260 * Returns a LoggerContext 261 * @param fqcn The fully qualified class name of the Class that this method is a member of. 262 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 263 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 264 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 265 * returned. If true then only a single LoggerContext will be returned. 266 * @return a LoggerContext. 267 */ 268 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 269 return factory.getContext(fqcn, null, null, currentContext); 270 } 271 272 /** 273 * Returns a LoggerContext 274 * @param fqcn The fully qualified class name of the Class that this method is a member of. 275 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 276 * ClassLoader. 277 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 278 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 279 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 280 * returned. If true then only a single LoggerContext will be returned. 281 * @return a LoggerContext. 282 */ 283 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, 284 final boolean currentContext) { 285 return factory.getContext(fqcn, loader, null, currentContext); 286 } 287 288 /** 289 * Returns the current LoggerContextFactory. 290 * @return The LoggerContextFactory. 291 */ 292 public static LoggerContextFactory getFactory() { 293 return factory; 294 } 295 296 /** 297 * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at 298 * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. 299 * Thus, an alternative LoggerContextFactory can be set at runtime. 300 * 301 * <p> 302 * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be 303 * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! 304 * Generally, this method should be used only during startup before any code starts caching Logger objects. 305 * </p> 306 * 307 * @param factory the LoggerContextFactory to use. 308 */ 309 // FIXME: should we allow only one update of the factory? 310 public static void setFactory(final LoggerContextFactory factory) { 311 LogManager.factory = factory; 312 } 313 314 /** 315 * Returns a formatter Logger using the fully qualified name of the 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(clazz, StringFormatterMessageFactory.INSTANCE)} 321 * </p> 322 * 323 * @param clazz 324 * The Class whose 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 Class<?> clazz) { 341 return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE); 342 } 343 344 /** 345 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 346 * <p> 347 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 348 * </p> 349 * <p> 350 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 351 * </p> 352 * 353 * @param value 354 * The value's whose class name should be used as the Logger name. 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 Object value) { 371 return getLogger(value != null ? value.getClass().getName() : getClassName(2), 372 StringFormatterMessageFactory.INSTANCE); 373 } 374 375 /** 376 * Returns a formatter Logger with the specified name. 377 * <p> 378 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 379 * </p> 380 * <p> 381 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 382 * </p> 383 * 384 * @param name The logger name. If null it will default to the name of the calling class. 385 * @return The Logger, created with a {@link StringFormatterMessageFactory} 386 * @see Logger#fatal(Marker, String, Object...) 387 * @see Logger#fatal(String, Object...) 388 * @see Logger#error(Marker, String, Object...) 389 * @see Logger#error(String, Object...) 390 * @see Logger#warn(Marker, String, Object...) 391 * @see Logger#warn(String, Object...) 392 * @see Logger#info(Marker, String, Object...) 393 * @see Logger#info(String, Object...) 394 * @see Logger#debug(Marker, String, Object...) 395 * @see Logger#debug(String, Object...) 396 * @see Logger#trace(Marker, String, Object...) 397 * @see Logger#trace(String, Object...) 398 * @see StringFormatterMessageFactory 399 */ 400 public static Logger getFormatterLogger(final String name) { 401 return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE); 402 } 403 404 /** 405 * Returns a Logger with the name of the calling class. 406 * @return The Logger for the calling class. 407 */ 408 public static Logger getLogger() { 409 return getLogger(getClassName(2)); 410 } 411 412 /** 413 * Returns a Logger using the fully qualified name of the Class as the Logger name. 414 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 415 * class. 416 * @return The Logger. 417 */ 418 public static Logger getLogger(final Class<?> clazz) { 419 return getLogger(clazz != null ? clazz.getName() : getClassName(2)); 420 } 421 422 /** 423 * Returns a Logger using the fully qualified name of the Class as the Logger name. 424 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 425 * class. 426 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 427 * the logger but will log a warning if mismatched. 428 * @return The Logger. 429 */ 430 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 431 return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory); 432 } 433 434 /** 435 * Returns a Logger with the name of the calling class. 436 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 437 * the logger but will log a warning if mismatched. 438 * @return The Logger for the calling class. 439 */ 440 public static Logger getLogger(final MessageFactory messageFactory) { 441 return getLogger(getClassName(2), messageFactory); 442 } 443 444 /** 445 * Returns a Logger using the fully qualified class name of the value as the Logger name. 446 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 447 * class will be used as the logger name. 448 * @return The Logger. 449 */ 450 public static Logger getLogger(final Object value) { 451 return getLogger(value != null ? value.getClass().getName() : getClassName(2)); 452 } 453 454 /** 455 * Returns a Logger using the fully qualified class name of the value as the Logger name. 456 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 457 * class will be used as the logger name. 458 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 459 * the logger but will log a warning if mismatched. 460 * @return The Logger. 461 */ 462 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 463 return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory); 464 } 465 466 /** 467 * Returns a Logger with the specified name. 468 * 469 * @param name The logger name. If null the name of the calling class will be used. 470 * @return The Logger. 471 */ 472 public static Logger getLogger(final String name) { 473 final String actualName = name != null ? name : getClassName(2); 474 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName); 475 } 476 477 /** 478 * Returns a Logger with the specified name. 479 * 480 * @param name The logger name. If null the name of the calling class will be used. 481 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 482 * the logger but will log a warning if mismatched. 483 * @return The Logger. 484 */ 485 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 486 final String actualName = name != null ? name : getClassName(2); 487 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory); 488 } 489 490 /** 491 * Returns a Logger with the specified name. 492 * 493 * @param fqcn The fully qualified class name of the class that this method is a member of. 494 * @param name The logger name. 495 * @return The Logger. 496 */ 497 protected static Logger getLogger(final String fqcn, final String name) { 498 return factory.getContext(fqcn, null, null, false).getLogger(name); 499 } 500 501 /** 502 * Returns the root logger. 503 * 504 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 505 */ 506 public static Logger getRootLogger() { 507 return getLogger(ROOT_LOGGER_NAME); 508 } 509 510 /** 511 * Prevents instantiation 512 */ 513 protected LogManager() { 514 } 515 516 }