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.ReflectionUtil; 35 import org.apache.logging.log4j.util.Strings; 36 37 /** 38 * The anchor point for the logging system. The most common usage of this class is to obtain a named {@link Logger}. The 39 * method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based on the calling 40 * class name. This class also provides method for obtaining named Loggers that use 41 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. These 42 * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are 43 * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally 44 * useful for typical usage of Log4j. 45 */ 46 public class LogManager { 47 48 /** 49 * Log4j property to set to the fully qualified class name of a custom implementation of 50 * {@link org.apache.logging.log4j.spi.LoggerContextFactory}. 51 */ 52 public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 53 54 /** 55 * The name of the root Logger. 56 */ 57 public static final String ROOT_LOGGER_NAME = Strings.EMPTY; 58 59 private static final Logger LOGGER = StatusLogger.getLogger(); 60 61 // for convenience 62 private static final String FQCN = LogManager.class.getName(); 63 64 private static volatile LoggerContextFactory factory; 65 66 /** 67 * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be 68 * extended to allow multiple implementations to be used. 69 */ 70 static { 71 // Shortcut binding to force a specific logging implementation. 72 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 73 final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); 74 if (factoryClassName != null) { 75 try { 76 factory = LoaderUtil.newCheckedInstanceOf(factoryClassName, LoggerContextFactory.class); 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<>(); 86 // note that the following initial call to ProviderUtil may block until a Provider has been installed when 87 // running in an OSGi environment 88 if (ProviderUtil.hasProviders()) { 89 for (final Provider provider : ProviderUtil.getProviders()) { 90 final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory(); 91 if (factoryClass != null) { 92 try { 93 factories.put(provider.getPriority(), factoryClass.newInstance()); 94 } catch (final Exception e) { 95 LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), provider 96 .getUrl().toString(), e); 97 } 98 } 99 } 100 101 if (factories.isEmpty()) { 102 LOGGER.error("Log4j2 could not find a logging implementation. " 103 + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 104 factory = new SimpleLoggerContextFactory(); 105 } else if (factories.size() == 1) { 106 factory = factories.get(factories.lastKey()); 107 } else { 108 final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); 109 for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { 110 sb.append("Factory: ").append(entry.getValue().getClass().getName()); 111 sb.append(", Weighting: ").append(entry.getKey()).append('\n'); 112 } 113 factory = factories.get(factories.lastKey()); 114 sb.append("Using factory: ").append(factory.getClass().getName()); 115 LOGGER.warn(sb.toString()); 116 117 } 118 } else { 119 LOGGER.error("Log4j2 could not find a logging implementation. " 120 + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 121 factory = new SimpleLoggerContextFactory(); 122 } 123 } 124 } 125 126 /** 127 * Prevents instantiation 128 */ 129 protected LogManager() { 130 } 131 132 /** 133 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. 134 * 135 * @param name The Logger name to search for. 136 * @return true if the Logger exists, false otherwise. 137 * @see LoggerContext#hasLogger(String) 138 */ 139 public static boolean exists(final String name) { 140 return getContext().hasLogger(name); 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 for the 147 * calling class. 148 * </p> 149 * 150 * @return The current LoggerContext. 151 */ 152 public static LoggerContext getContext() { 153 return factory.getContext(FQCN, null, null, true); 154 } 155 156 /** 157 * Returns a LoggerContext. 158 * 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 162 * be returned. If true then only a single LoggerContext will be returned. 163 * @return a LoggerContext. 164 */ 165 public static LoggerContext getContext(final boolean currentContext) { 166 // TODO: would it be a terrible idea to try and find the caller ClassLoader here? 167 return factory.getContext(FQCN, null, null, currentContext, null, null); 168 } 169 170 /** 171 * Returns a LoggerContext. 172 * 173 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 174 * ClassLoader. 175 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 176 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 177 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 178 * be returned. If true then only a single LoggerContext will be returned. 179 * @return a LoggerContext. 180 */ 181 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 182 return factory.getContext(FQCN, loader, null, currentContext); 183 } 184 185 /** 186 * Returns a LoggerContext. 187 * 188 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 189 * ClassLoader. 190 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 191 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 192 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 193 * be returned. If true then only a single LoggerContext will be returned. 194 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 195 * @return a LoggerContext. 196 */ 197 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 198 final Object externalContext) { 199 return factory.getContext(FQCN, loader, externalContext, currentContext); 200 } 201 202 /** 203 * Returns a LoggerContext. 204 * 205 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 206 * ClassLoader. 207 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 208 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 209 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 210 * be returned. If true then only a single LoggerContext will be returned. 211 * @param configLocation The URI for the configuration to use. 212 * @return a LoggerContext. 213 */ 214 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 215 final URI configLocation) { 216 return factory.getContext(FQCN, loader, null, currentContext, configLocation, null); 217 } 218 219 /** 220 * Returns a LoggerContext. 221 * 222 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 223 * ClassLoader. 224 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 225 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 226 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 227 * be returned. If true then only a single LoggerContext will be returned. 228 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 229 * @param configLocation The URI for the configuration to use. 230 * @return a LoggerContext. 231 */ 232 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 233 final Object externalContext, final URI configLocation) { 234 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null); 235 } 236 237 /** 238 * Returns a LoggerContext. 239 * 240 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 241 * ClassLoader. 242 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 243 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 244 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 245 * be returned. If true then only a single LoggerContext will be returned. 246 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 247 * @param configLocation The URI for the configuration to use. 248 * @param name The LoggerContext name. 249 * @return a LoggerContext. 250 */ 251 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 252 final Object externalContext, final URI configLocation, final String name) { 253 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name); 254 } 255 256 /** 257 * Returns a LoggerContext 258 * 259 * @param fqcn The fully qualified class name of the Class that this method is a member of. 260 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 261 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 262 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 263 * be returned. If true then only a single LoggerContext will be returned. 264 * @return a LoggerContext. 265 */ 266 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 267 return factory.getContext(fqcn, null, null, currentContext); 268 } 269 270 /** 271 * Returns a LoggerContext 272 * 273 * @param fqcn The fully qualified class name of the Class that this method is a member of. 274 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 275 * ClassLoader. 276 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 277 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 278 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 279 * be returned. If true then only a single LoggerContext will be returned. 280 * @return a LoggerContext. 281 */ 282 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, 283 final boolean currentContext) { 284 return factory.getContext(fqcn, loader, null, currentContext); 285 } 286 287 /** 288 * Returns the current LoggerContextFactory. 289 * 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 calling Class as the Logger name. 316 * <p> 317 * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters. 318 * </p> 319 * 320 * @return The Logger for the calling class. 321 * @throws UnsupportedOperationException if the calling class cannot be determined. 322 * @since 2.4 323 */ 324 public static Logger getFormatterLogger() { 325 return getFormatterLogger(ReflectionUtil.getCallerClass(2)); 326 } 327 328 /** 329 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. 330 * <p> 331 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 332 * </p> 333 * <p> 334 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} 335 * </p> 336 * 337 * @param clazz The Class whose name should be used as the Logger name. 338 * @return The Logger, created with a {@link StringFormatterMessageFactory} 339 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 340 * determined. 341 * @see Logger#fatal(Marker, String, Object...) 342 * @see Logger#fatal(String, Object...) 343 * @see Logger#error(Marker, String, Object...) 344 * @see Logger#error(String, Object...) 345 * @see Logger#warn(Marker, String, Object...) 346 * @see Logger#warn(String, Object...) 347 * @see Logger#info(Marker, String, Object...) 348 * @see Logger#info(String, Object...) 349 * @see Logger#debug(Marker, String, Object...) 350 * @see Logger#debug(String, Object...) 351 * @see Logger#trace(Marker, String, Object...) 352 * @see Logger#trace(String, Object...) 353 * @see StringFormatterMessageFactory 354 */ 355 public static Logger getFormatterLogger(final Class<?> clazz) { 356 return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2), 357 StringFormatterMessageFactory.INSTANCE); 358 } 359 360 /** 361 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 362 * <p> 363 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 364 * </p> 365 * <p> 366 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 367 * </p> 368 * 369 * @param value The value's whose class name should be used as the Logger name. 370 * @return The Logger, created with a {@link StringFormatterMessageFactory} 371 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 372 * determined. 373 * @see Logger#fatal(Marker, String, Object...) 374 * @see Logger#fatal(String, Object...) 375 * @see Logger#error(Marker, String, Object...) 376 * @see Logger#error(String, Object...) 377 * @see Logger#warn(Marker, String, Object...) 378 * @see Logger#warn(String, Object...) 379 * @see Logger#info(Marker, String, Object...) 380 * @see Logger#info(String, Object...) 381 * @see Logger#debug(Marker, String, Object...) 382 * @see Logger#debug(String, Object...) 383 * @see Logger#trace(Marker, String, Object...) 384 * @see Logger#trace(String, Object...) 385 * @see StringFormatterMessageFactory 386 */ 387 public static Logger getFormatterLogger(final Object value) { 388 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), 389 StringFormatterMessageFactory.INSTANCE); 390 } 391 392 /** 393 * Returns a formatter Logger with the specified name. 394 * <p> 395 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 396 * </p> 397 * <p> 398 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 399 * </p> 400 * 401 * @param name The logger name. If null it will default to the name of the calling class. 402 * @return The Logger, created with a {@link StringFormatterMessageFactory} 403 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 404 * @see Logger#fatal(Marker, String, Object...) 405 * @see Logger#fatal(String, Object...) 406 * @see Logger#error(Marker, String, Object...) 407 * @see Logger#error(String, Object...) 408 * @see Logger#warn(Marker, String, Object...) 409 * @see Logger#warn(String, Object...) 410 * @see Logger#info(Marker, String, Object...) 411 * @see Logger#info(String, Object...) 412 * @see Logger#debug(Marker, String, Object...) 413 * @see Logger#debug(String, Object...) 414 * @see Logger#trace(Marker, String, Object...) 415 * @see Logger#trace(String, Object...) 416 * @see StringFormatterMessageFactory 417 */ 418 public static Logger getFormatterLogger(final String name) { 419 return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name, 420 StringFormatterMessageFactory.INSTANCE); 421 } 422 423 private static Class<?> callerClass(final Class<?> clazz) { 424 if (clazz != null) { 425 return clazz; 426 } 427 final Class<?> candidate = ReflectionUtil.getCallerClass(3); 428 if (candidate == null) { 429 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); 430 } 431 return candidate; 432 } 433 434 /** 435 * Returns a Logger with the name of the calling class. 436 * 437 * @return The Logger for the calling class. 438 * @throws UnsupportedOperationException if the calling class cannot be determined. 439 */ 440 public static Logger getLogger() { 441 return getLogger(ReflectionUtil.getCallerClass(2)); 442 } 443 444 /** 445 * Returns a Logger using the fully qualified name of the Class as the Logger name. 446 * 447 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 448 * class. 449 * @return The Logger. 450 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 451 * determined. 452 */ 453 public static Logger getLogger(final Class<?> clazz) { 454 final Class<?> cls = callerClass(clazz); 455 return getContext(cls.getClassLoader(), false).getLogger(cls.getName()); 456 } 457 458 /** 459 * Returns a Logger using the fully qualified name of the Class as the Logger name. 460 * 461 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 462 * class. 463 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 464 * logger but will log a warning if mismatched. 465 * @return The Logger. 466 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 467 * determined. 468 */ 469 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 470 final Class<?> cls = callerClass(clazz); 471 return getContext(cls.getClassLoader(), false).getLogger(cls.getName(), messageFactory); 472 } 473 474 /** 475 * Returns a Logger with the name of the calling class. 476 * 477 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 478 * logger but will log a warning if mismatched. 479 * @return The Logger for the calling class. 480 * @throws UnsupportedOperationException if the calling class cannot be determined. 481 */ 482 public static Logger getLogger(final MessageFactory messageFactory) { 483 return getLogger(ReflectionUtil.getCallerClass(2), messageFactory); 484 } 485 486 /** 487 * Returns a Logger using the fully qualified class name of the value as the Logger name. 488 * 489 * @param value The value whose class name should be used as the Logger name. If null the name of the calling class 490 * will be used as the logger name. 491 * @return The Logger. 492 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 493 * determined. 494 */ 495 public static Logger getLogger(final Object value) { 496 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2)); 497 } 498 499 /** 500 * Returns a Logger using the fully qualified class name of the value as the Logger name. 501 * 502 * @param value The value whose class name should be used as the Logger name. If null the name of the calling class 503 * will be used as the logger name. 504 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 505 * logger but will log a warning if mismatched. 506 * @return The Logger. 507 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 508 * determined. 509 */ 510 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 511 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory); 512 } 513 514 /** 515 * Returns a Logger with the specified name. 516 * 517 * @param name The logger name. If null the name of the calling class will be used. 518 * @return The Logger. 519 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 520 */ 521 public static Logger getLogger(final String name) { 522 return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2)); 523 } 524 525 /** 526 * Returns a Logger with the specified name. 527 * 528 * @param name The logger name. If null the name of the calling class will be used. 529 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 530 * logger but will log a warning if mismatched. 531 * @return The Logger. 532 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 533 */ 534 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 535 return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger( 536 ReflectionUtil.getCallerClass(2), messageFactory); 537 } 538 539 /** 540 * Returns a Logger with the specified name. 541 * 542 * @param fqcn The fully qualified class name of the class that this method is a member of. 543 * @param name The logger name. 544 * @return The Logger. 545 */ 546 protected static Logger getLogger(final String fqcn, final String name) { 547 return factory.getContext(fqcn, null, null, false).getLogger(name); 548 } 549 550 /** 551 * Returns the root logger. 552 * 553 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 554 */ 555 public static Logger getRootLogger() { 556 return getLogger(ROOT_LOGGER_NAME); 557 } 558 }