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