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<Integer, LoggerContextFactory>(); 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 { 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. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 120 factory = new SimpleLoggerContextFactory(); 121 } 122 } 123 } 124 125 /** 126 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. 127 * 128 * @param name 129 * The Logger name to search for. 130 * @return true if the Logger exists, false otherwise. 131 * @see LoggerContext#hasLogger(String) 132 */ 133 public static boolean exists(final String name) { 134 return getContext().hasLogger(name); 135 } 136 137 /** 138 * Returns the current LoggerContext. 139 * <p> 140 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger 141 * for the calling class. 142 * </p> 143 * @return The current LoggerContext. 144 */ 145 public static LoggerContext getContext() { 146 return factory.getContext(FQCN, null, null, true); 147 } 148 149 /** 150 * Returns a LoggerContext. 151 * 152 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 153 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 154 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 155 * returned. If true then only a single LoggerContext will be returned. 156 * @return a LoggerContext. 157 */ 158 public static LoggerContext getContext(final boolean currentContext) { 159 // TODO: would it be a terrible idea to try and find the caller ClassLoader here? 160 return factory.getContext(FQCN, null, null, currentContext, null, null); 161 } 162 163 /** 164 * Returns a LoggerContext. 165 * 166 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 167 * ClassLoader. 168 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 169 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 170 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 171 * returned. If true then only a single LoggerContext will be returned. 172 * @return a LoggerContext. 173 */ 174 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 175 return factory.getContext(FQCN, loader, null, currentContext); 176 } 177 178 /** 179 * Returns a LoggerContext. 180 * 181 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 182 * ClassLoader. 183 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 184 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 185 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 186 * returned. If true then only a single LoggerContext will be returned. 187 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 188 * @return a LoggerContext. 189 */ 190 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 191 final Object externalContext) { 192 return factory.getContext(FQCN, loader, externalContext, currentContext); 193 } 194 195 /** 196 * Returns a LoggerContext. 197 * 198 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 199 * ClassLoader. 200 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 201 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 202 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 203 * returned. If true then only a single LoggerContext will be returned. 204 * @param configLocation The URI for the configuration to use. 205 * @return a LoggerContext. 206 */ 207 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 208 final URI configLocation) { 209 return factory.getContext(FQCN, loader, null, currentContext, configLocation, null); 210 } 211 212 213 /** 214 * Returns a LoggerContext. 215 * 216 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 217 * ClassLoader. 218 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 219 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 220 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 221 * returned. If true then only a single LoggerContext will be returned. 222 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 223 * @param configLocation The URI for the configuration to use. 224 * @return a LoggerContext. 225 */ 226 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 227 final Object externalContext, final URI configLocation) { 228 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null); 229 } 230 231 232 /** 233 * Returns a LoggerContext. 234 * 235 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 236 * ClassLoader. 237 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 238 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 239 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 240 * returned. If true then only a single LoggerContext will be returned. 241 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 242 * @param configLocation The URI for the configuration to use. 243 * @param name The LoggerContext name. 244 * @return a LoggerContext. 245 */ 246 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 247 final Object externalContext, final URI configLocation, 248 final String name) { 249 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name); 250 } 251 252 /** 253 * Returns a LoggerContext 254 * @param fqcn The fully qualified class name of the Class that this method is a member of. 255 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 256 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 257 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 258 * returned. If true then only a single LoggerContext will be returned. 259 * @return a LoggerContext. 260 */ 261 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 262 return factory.getContext(fqcn, null, null, currentContext); 263 } 264 265 /** 266 * Returns a LoggerContext 267 * @param fqcn The fully qualified class name of the Class that this method is a member of. 268 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 269 * ClassLoader. 270 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 271 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 272 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 273 * returned. If true then only a single LoggerContext will be returned. 274 * @return a LoggerContext. 275 */ 276 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, 277 final boolean currentContext) { 278 return factory.getContext(fqcn, loader, null, currentContext); 279 } 280 281 /** 282 * Returns the current LoggerContextFactory. 283 * @return The LoggerContextFactory. 284 */ 285 public static LoggerContextFactory getFactory() { 286 return factory; 287 } 288 289 /** 290 * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at 291 * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. 292 * Thus, an alternative LoggerContextFactory can be set at runtime. 293 * 294 * <p> 295 * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be 296 * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! 297 * Generally, this method should be used only during startup before any code starts caching Logger objects. 298 * </p> 299 * 300 * @param factory the LoggerContextFactory to use. 301 */ 302 // FIXME: should we allow only one update of the factory? 303 public static void setFactory(final LoggerContextFactory factory) { 304 LogManager.factory = factory; 305 } 306 307 /** 308 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. 309 * <p> 310 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 311 * </p> 312 * <p> 313 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} 314 * </p> 315 * 316 * @param clazz 317 * The Class whose name should be used as the Logger name. 318 * @return The Logger, created with a {@link StringFormatterMessageFactory} 319 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. 320 * @see Logger#fatal(Marker, String, Object...) 321 * @see Logger#fatal(String, Object...) 322 * @see Logger#error(Marker, String, Object...) 323 * @see Logger#error(String, Object...) 324 * @see Logger#warn(Marker, String, Object...) 325 * @see Logger#warn(String, Object...) 326 * @see Logger#info(Marker, String, Object...) 327 * @see Logger#info(String, Object...) 328 * @see Logger#debug(Marker, String, Object...) 329 * @see Logger#debug(String, Object...) 330 * @see Logger#trace(Marker, String, Object...) 331 * @see Logger#trace(String, Object...) 332 * @see StringFormatterMessageFactory 333 */ 334 public static Logger getFormatterLogger(final Class<?> clazz) { 335 return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2), 336 StringFormatterMessageFactory.INSTANCE); 337 } 338 339 /** 340 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 341 * <p> 342 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 343 * </p> 344 * <p> 345 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 346 * </p> 347 * 348 * @param value 349 * The value's whose class name should be used as the Logger name. 350 * @return The Logger, created with a {@link StringFormatterMessageFactory} 351 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. 352 * @see Logger#fatal(Marker, String, Object...) 353 * @see Logger#fatal(String, Object...) 354 * @see Logger#error(Marker, String, Object...) 355 * @see Logger#error(String, Object...) 356 * @see Logger#warn(Marker, String, Object...) 357 * @see Logger#warn(String, Object...) 358 * @see Logger#info(Marker, String, Object...) 359 * @see Logger#info(String, Object...) 360 * @see Logger#debug(Marker, String, Object...) 361 * @see Logger#debug(String, Object...) 362 * @see Logger#trace(Marker, String, Object...) 363 * @see Logger#trace(String, Object...) 364 * @see StringFormatterMessageFactory 365 */ 366 public static Logger getFormatterLogger(final Object value) { 367 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), 368 StringFormatterMessageFactory.INSTANCE); 369 } 370 371 /** 372 * Returns a formatter Logger with the specified name. 373 * <p> 374 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 375 * </p> 376 * <p> 377 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 378 * </p> 379 * 380 * @param name The logger name. If null it will default to the name of the calling class. 381 * @return The Logger, created with a {@link StringFormatterMessageFactory} 382 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 383 * @see Logger#fatal(Marker, String, Object...) 384 * @see Logger#fatal(String, Object...) 385 * @see Logger#error(Marker, String, Object...) 386 * @see Logger#error(String, Object...) 387 * @see Logger#warn(Marker, String, Object...) 388 * @see Logger#warn(String, Object...) 389 * @see Logger#info(Marker, String, Object...) 390 * @see Logger#info(String, Object...) 391 * @see Logger#debug(Marker, String, Object...) 392 * @see Logger#debug(String, Object...) 393 * @see Logger#trace(Marker, String, Object...) 394 * @see Logger#trace(String, Object...) 395 * @see StringFormatterMessageFactory 396 */ 397 public static Logger getFormatterLogger(final String name) { 398 return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name, 399 StringFormatterMessageFactory.INSTANCE); 400 } 401 402 /** 403 * Returns a Logger with the name of the calling class. 404 * @return The Logger for the calling class. 405 * @throws UnsupportedOperationException if the calling class cannot be determined. 406 */ 407 public static Logger getLogger() { 408 return getLogger(ReflectionUtil.getCallerClass(2)); 409 } 410 411 /** 412 * Returns a Logger using the fully qualified name of the Class as the Logger name. 413 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 414 * class. 415 * @return The Logger. 416 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. 417 */ 418 public static Logger getLogger(final Class<?> clazz) { 419 if (clazz == null) { 420 final Class<?> candidate = ReflectionUtil.getCallerClass(2); 421 if (candidate == null) { 422 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); 423 } 424 return getLogger(candidate); 425 } 426 return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName()); 427 } 428 429 /** 430 * Returns a Logger using the fully qualified name of the Class as the Logger name. 431 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 432 * class. 433 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 434 * the logger but will log a warning if mismatched. 435 * @return The Logger. 436 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be determined. 437 */ 438 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 439 if (clazz == null) { 440 final Class<?> candidate = ReflectionUtil.getCallerClass(2); 441 if (candidate == null) { 442 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); 443 } 444 return getLogger(candidate, messageFactory); 445 } 446 return getContext(clazz.getClassLoader(), false).getLogger(clazz.getName(), messageFactory); 447 } 448 449 /** 450 * Returns a Logger with the name of the calling class. 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 for the calling class. 454 * @throws UnsupportedOperationException if the calling class cannot be determined. 455 */ 456 public static Logger getLogger(final MessageFactory messageFactory) { 457 return getLogger(ReflectionUtil.getCallerClass(2), messageFactory); 458 } 459 460 /** 461 * Returns a Logger using the fully qualified class name of the value as the Logger name. 462 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 463 * class will be used as the logger name. 464 * @return The Logger. 465 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. 466 */ 467 public static Logger getLogger(final Object value) { 468 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2)); 469 } 470 471 /** 472 * Returns a Logger using the fully qualified class name of the value as the Logger name. 473 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 474 * class will be used as the logger name. 475 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 476 * the logger but will log a warning if mismatched. 477 * @return The Logger. 478 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be determined. 479 */ 480 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 481 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory); 482 } 483 484 /** 485 * Returns a Logger with the specified name. 486 * 487 * @param name The logger name. If null the name of the calling class will be used. 488 * @return The Logger. 489 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 490 */ 491 public static Logger getLogger(final String name) { 492 return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2)); 493 } 494 495 /** 496 * Returns a Logger with the specified name. 497 * 498 * @param name The logger name. If null the name of the calling class will be used. 499 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 500 * the logger but will log a warning if mismatched. 501 * @return The Logger. 502 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 503 */ 504 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 505 return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger( 506 ReflectionUtil.getCallerClass(2), messageFactory); 507 } 508 509 /** 510 * Returns a Logger with the specified name. 511 * 512 * @param fqcn The fully qualified class name of the class that this method is a member of. 513 * @param name The logger name. 514 * @return The Logger. 515 */ 516 protected static Logger getLogger(final String fqcn, final String name) { 517 return factory.getContext(fqcn, null, null, false).getLogger(name); 518 } 519 520 /** 521 * Returns the root logger. 522 * 523 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 524 */ 525 public static Logger getRootLogger() { 526 return getLogger(ROOT_LOGGER_NAME); 527 } 528 529 /** 530 * Prevents instantiation 531 */ 532 protected LogManager() { 533 } 534 535 }