001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j; 018 019import java.net.URI; 020import java.util.Map; 021import java.util.SortedMap; 022import java.util.TreeMap; 023 024import org.apache.logging.log4j.message.MessageFactory; 025import org.apache.logging.log4j.message.StringFormatterMessageFactory; 026import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; 027import org.apache.logging.log4j.spi.LoggerContext; 028import org.apache.logging.log4j.spi.LoggerContextFactory; 029import org.apache.logging.log4j.spi.Provider; 030import org.apache.logging.log4j.status.StatusLogger; 031import org.apache.logging.log4j.util.LoaderUtil; 032import org.apache.logging.log4j.util.PropertiesUtil; 033import org.apache.logging.log4j.util.ProviderUtil; 034import org.apache.logging.log4j.util.ReflectionUtil; 035import org.apache.logging.log4j.util.Strings; 036 037/** 038 * The anchor point for the logging system. The most common usage of this class is to obtain a named {@link Logger}. The 039 * method {@link #getLogger()} is provided as the most convenient way to obtain a named Logger based on the calling 040 * class name. This class also provides method for obtaining named Loggers that use 041 * {@link String#format(String, Object...)} style messages instead of the default type of parameterized messages. These 042 * are obtained through the {@link #getFormatterLogger(Class)} family of methods. Other service provider methods are 043 * given through the {@link #getContext()} and {@link #getFactory()} family of methods; these methods are not normally 044 * useful for typical usage of Log4j. 045 */ 046public class LogManager { 047 048 /** 049 * Log4j property to set to the fully qualified class name of a custom implementation of 050 * {@link org.apache.logging.log4j.spi.LoggerContextFactory}. 051 */ 052 public static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 053 054 /** 055 * The name of the root Logger. 056 */ 057 public static final String ROOT_LOGGER_NAME = Strings.EMPTY; 058 059 private static final Logger LOGGER = StatusLogger.getLogger(); 060 061 // for convenience 062 private static final String FQCN = LogManager.class.getName(); 063 064 private static volatile LoggerContextFactory factory; 065 066 /** 067 * Scans the classpath to find all logging implementation. Currently, only one will be used but this could be 068 * extended to allow multiple implementations to be used. 069 */ 070 static { 071 // Shortcut binding to force a specific logging implementation. 072 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 073 final String factoryClassName = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); 074 if (factoryClassName != null) { 075 try { 076 final Class<?> clazz = LoaderUtil.loadClass(factoryClassName); 077 if (LoggerContextFactory.class.isAssignableFrom(clazz)) { 078 factory = clazz.asSubclass(LoggerContextFactory.class).newInstance(); 079 } 080 } catch (final ClassNotFoundException cnfe) { 081 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClassName); 082 } catch (final Exception ex) { 083 LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClassName, ex); 084 } 085 } 086 087 if (factory == null) { 088 final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<>(); 089 // note that the following initial call to ProviderUtil may block until a Provider has been installed when 090 // running in an OSGi environment 091 if (ProviderUtil.hasProviders()) { 092 for (final Provider provider : ProviderUtil.getProviders()) { 093 final Class<? extends LoggerContextFactory> factoryClass = provider.loadLoggerContextFactory(); 094 if (factoryClass != null) { 095 try { 096 factories.put(provider.getPriority(), factoryClass.newInstance()); 097 } catch (final Exception e) { 098 LOGGER.error("Unable to create class {} specified in {}", factoryClass.getName(), provider 099 .getUrl().toString(), e); 100 } 101 } 102 } 103 104 if (factories.isEmpty()) { 105 LOGGER.error("Log4j2 could not find a logging implementation. " 106 + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 107 factory = new SimpleLoggerContextFactory(); 108 } else if (factories.size() == 1) { 109 factory = factories.get(factories.lastKey()); 110 } else { 111 final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); 112 for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { 113 sb.append("Factory: ").append(entry.getValue().getClass().getName()); 114 sb.append(", Weighting: ").append(entry.getKey()).append('\n'); 115 } 116 factory = factories.get(factories.lastKey()); 117 sb.append("Using factory: ").append(factory.getClass().getName()); 118 LOGGER.warn(sb.toString()); 119 120 } 121 } else { 122 LOGGER.error("Log4j2 could not find a logging implementation. " 123 + "Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."); 124 factory = new SimpleLoggerContextFactory(); 125 } 126 } 127 } 128 129 /** 130 * Prevents instantiation 131 */ 132 protected LogManager() { 133 } 134 135 /** 136 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. 137 * 138 * @param name The Logger name to search for. 139 * @return true if the Logger exists, false otherwise. 140 * @see LoggerContext#hasLogger(String) 141 */ 142 public static boolean exists(final String name) { 143 return getContext().hasLogger(name); 144 } 145 146 /** 147 * Returns the current LoggerContext. 148 * <p> 149 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger for the 150 * calling class. 151 * </p> 152 * 153 * @return The current LoggerContext. 154 */ 155 public static LoggerContext getContext() { 156 return factory.getContext(FQCN, null, null, true); 157 } 158 159 /** 160 * Returns a LoggerContext. 161 * 162 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 163 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 164 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 165 * be returned. If true then only a single LoggerContext will be returned. 166 * @return a LoggerContext. 167 */ 168 public static LoggerContext getContext(final boolean currentContext) { 169 // TODO: would it be a terrible idea to try and find the caller ClassLoader here? 170 return factory.getContext(FQCN, null, null, currentContext, null, null); 171 } 172 173 /** 174 * Returns a LoggerContext. 175 * 176 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 177 * ClassLoader. 178 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 179 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 180 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 181 * be returned. If true then only a single LoggerContext will be returned. 182 * @return a LoggerContext. 183 */ 184 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 185 return factory.getContext(FQCN, loader, null, currentContext); 186 } 187 188 /** 189 * Returns a LoggerContext. 190 * 191 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 192 * ClassLoader. 193 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 194 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 195 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 196 * be returned. If true then only a single LoggerContext will be returned. 197 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 198 * @return a LoggerContext. 199 */ 200 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 201 final Object externalContext) { 202 return factory.getContext(FQCN, loader, externalContext, currentContext); 203 } 204 205 /** 206 * Returns a LoggerContext. 207 * 208 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 209 * ClassLoader. 210 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 211 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 212 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 213 * be returned. If true then only a single LoggerContext will be returned. 214 * @param configLocation The URI for the configuration to use. 215 * @return a LoggerContext. 216 */ 217 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 218 final URI configLocation) { 219 return factory.getContext(FQCN, loader, null, currentContext, configLocation, null); 220 } 221 222 /** 223 * Returns a LoggerContext. 224 * 225 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 226 * ClassLoader. 227 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 228 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 229 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 230 * be returned. If true then only a single LoggerContext will be returned. 231 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 232 * @param configLocation The URI for the configuration to use. 233 * @return a LoggerContext. 234 */ 235 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 236 final Object externalContext, final URI configLocation) { 237 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, null); 238 } 239 240 /** 241 * Returns a LoggerContext. 242 * 243 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 244 * ClassLoader. 245 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 246 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 247 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 248 * be returned. If true then only a single LoggerContext will be returned. 249 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 250 * @param configLocation The URI for the configuration to use. 251 * @param name The LoggerContext name. 252 * @return a LoggerContext. 253 */ 254 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 255 final Object externalContext, final URI configLocation, final String name) { 256 return factory.getContext(FQCN, loader, externalContext, currentContext, configLocation, name); 257 } 258 259 /** 260 * Returns a LoggerContext 261 * 262 * @param fqcn The fully qualified class name of the Class that this method is a member of. 263 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 264 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 265 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 266 * be returned. If true then only a single LoggerContext will be returned. 267 * @return a LoggerContext. 268 */ 269 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 270 return factory.getContext(fqcn, null, null, currentContext); 271 } 272 273 /** 274 * Returns a LoggerContext 275 * 276 * @param fqcn The fully qualified class name of the Class that this method is a member of. 277 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 278 * ClassLoader. 279 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 280 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 281 * returned and if the caller is a class in the container's classpath then a different LoggerContext may 282 * be returned. If true then only a single LoggerContext will be returned. 283 * @return a LoggerContext. 284 */ 285 protected static LoggerContext getContext(final String fqcn, final ClassLoader loader, 286 final boolean currentContext) { 287 return factory.getContext(fqcn, loader, null, currentContext); 288 } 289 290 /** 291 * Returns the current LoggerContextFactory. 292 * 293 * @return The LoggerContextFactory. 294 */ 295 public static LoggerContextFactory getFactory() { 296 return factory; 297 } 298 299 /** 300 * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at 301 * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. 302 * Thus, an alternative LoggerContextFactory can be set at runtime. 303 * 304 * <p> 305 * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be 306 * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! 307 * Generally, this method should be used only during startup before any code starts caching Logger objects. 308 * </p> 309 * 310 * @param factory the LoggerContextFactory to use. 311 */ 312 // FIXME: should we allow only one update of the factory? 313 public static void setFactory(final LoggerContextFactory factory) { 314 LogManager.factory = factory; 315 } 316 317 /** 318 * Returns a formatter Logger using the fully qualified name of the calling Class as the Logger name. 319 * <p> 320 * This logger lets you use a {@link java.util.Formatter} string in the message to format parameters. 321 * </p> 322 * 323 * @return The Logger for the calling class. 324 * @throws UnsupportedOperationException if the calling class cannot be determined. 325 * @since 2.4 326 */ 327 public static Logger getFormatterLogger() { 328 return getFormatterLogger(ReflectionUtil.getCallerClass(2)); 329 } 330 331 /** 332 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. 333 * <p> 334 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 335 * </p> 336 * <p> 337 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} 338 * </p> 339 * 340 * @param clazz The Class whose name should be used as the Logger name. 341 * @return The Logger, created with a {@link StringFormatterMessageFactory} 342 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 343 * determined. 344 * @see Logger#fatal(Marker, String, Object...) 345 * @see Logger#fatal(String, Object...) 346 * @see Logger#error(Marker, String, Object...) 347 * @see Logger#error(String, Object...) 348 * @see Logger#warn(Marker, String, Object...) 349 * @see Logger#warn(String, Object...) 350 * @see Logger#info(Marker, String, Object...) 351 * @see Logger#info(String, Object...) 352 * @see Logger#debug(Marker, String, Object...) 353 * @see Logger#debug(String, Object...) 354 * @see Logger#trace(Marker, String, Object...) 355 * @see Logger#trace(String, Object...) 356 * @see StringFormatterMessageFactory 357 */ 358 public static Logger getFormatterLogger(final Class<?> clazz) { 359 return getLogger(clazz != null ? clazz : ReflectionUtil.getCallerClass(2), 360 StringFormatterMessageFactory.INSTANCE); 361 } 362 363 /** 364 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 365 * <p> 366 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 367 * </p> 368 * <p> 369 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 370 * </p> 371 * 372 * @param value The value's whose class name should be used as the Logger name. 373 * @return The Logger, created with a {@link StringFormatterMessageFactory} 374 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 375 * determined. 376 * @see Logger#fatal(Marker, String, Object...) 377 * @see Logger#fatal(String, Object...) 378 * @see Logger#error(Marker, String, Object...) 379 * @see Logger#error(String, Object...) 380 * @see Logger#warn(Marker, String, Object...) 381 * @see Logger#warn(String, Object...) 382 * @see Logger#info(Marker, String, Object...) 383 * @see Logger#info(String, Object...) 384 * @see Logger#debug(Marker, String, Object...) 385 * @see Logger#debug(String, Object...) 386 * @see Logger#trace(Marker, String, Object...) 387 * @see Logger#trace(String, Object...) 388 * @see StringFormatterMessageFactory 389 */ 390 public static Logger getFormatterLogger(final Object value) { 391 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), 392 StringFormatterMessageFactory.INSTANCE); 393 } 394 395 /** 396 * Returns a formatter Logger with the specified name. 397 * <p> 398 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 399 * </p> 400 * <p> 401 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 402 * </p> 403 * 404 * @param name The logger name. If null it will default to the name of the calling class. 405 * @return The Logger, created with a {@link StringFormatterMessageFactory} 406 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 407 * @see Logger#fatal(Marker, String, Object...) 408 * @see Logger#fatal(String, Object...) 409 * @see Logger#error(Marker, String, Object...) 410 * @see Logger#error(String, Object...) 411 * @see Logger#warn(Marker, String, Object...) 412 * @see Logger#warn(String, Object...) 413 * @see Logger#info(Marker, String, Object...) 414 * @see Logger#info(String, Object...) 415 * @see Logger#debug(Marker, String, Object...) 416 * @see Logger#debug(String, Object...) 417 * @see Logger#trace(Marker, String, Object...) 418 * @see Logger#trace(String, Object...) 419 * @see StringFormatterMessageFactory 420 */ 421 public static Logger getFormatterLogger(final String name) { 422 return name == null ? getFormatterLogger(ReflectionUtil.getCallerClass(2)) : getLogger(name, 423 StringFormatterMessageFactory.INSTANCE); 424 } 425 426 private static Class<?> callerClass(final Class<?> clazz) { 427 if (clazz != null) { 428 return clazz; 429 } 430 final Class<?> candidate = ReflectionUtil.getCallerClass(3); 431 if (candidate == null) { 432 throw new UnsupportedOperationException("No class provided, and an appropriate one cannot be found."); 433 } 434 return candidate; 435 } 436 437 /** 438 * Returns a Logger with the name of the calling class. 439 * 440 * @return The Logger for the calling class. 441 * @throws UnsupportedOperationException if the calling class cannot be determined. 442 */ 443 public static Logger getLogger() { 444 return getLogger(ReflectionUtil.getCallerClass(2)); 445 } 446 447 /** 448 * Returns a Logger using the fully qualified name of the Class as the Logger name. 449 * 450 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 451 * class. 452 * @return The Logger. 453 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 454 * determined. 455 */ 456 public static Logger getLogger(final Class<?> clazz) { 457 final Class<?> cls = callerClass(clazz); 458 return getContext(cls.getClassLoader(), false).getLogger(cls.getName()); 459 } 460 461 /** 462 * Returns a Logger using the fully qualified name of the Class as the Logger name. 463 * 464 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 465 * class. 466 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 467 * logger but will log a warning if mismatched. 468 * @return The Logger. 469 * @throws UnsupportedOperationException if {@code clazz} is {@code null} and the calling class cannot be 470 * determined. 471 */ 472 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 473 final Class<?> cls = callerClass(clazz); 474 return getContext(cls.getClassLoader(), false).getLogger(cls.getName(), messageFactory); 475 } 476 477 /** 478 * Returns a Logger with the name of the calling class. 479 * 480 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 481 * logger but will log a warning if mismatched. 482 * @return The Logger for the calling class. 483 * @throws UnsupportedOperationException if the calling class cannot be determined. 484 */ 485 public static Logger getLogger(final MessageFactory messageFactory) { 486 return getLogger(ReflectionUtil.getCallerClass(2), messageFactory); 487 } 488 489 /** 490 * Returns a Logger using the fully qualified class name of the value as the Logger name. 491 * 492 * @param value The value whose class name should be used as the Logger name. If null the name of the calling class 493 * will be used as the logger name. 494 * @return The Logger. 495 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 496 * determined. 497 */ 498 public static Logger getLogger(final Object value) { 499 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2)); 500 } 501 502 /** 503 * Returns a Logger using the fully qualified class name of the value as the Logger name. 504 * 505 * @param value The value whose class name should be used as the Logger name. If null the name of the calling class 506 * will be used as the logger name. 507 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 508 * logger but will log a warning if mismatched. 509 * @return The Logger. 510 * @throws UnsupportedOperationException if {@code value} is {@code null} and the calling class cannot be 511 * determined. 512 */ 513 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 514 return getLogger(value != null ? value.getClass() : ReflectionUtil.getCallerClass(2), messageFactory); 515 } 516 517 /** 518 * Returns a Logger with the specified name. 519 * 520 * @param name The logger name. If null the name of the calling class will be used. 521 * @return The Logger. 522 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 523 */ 524 public static Logger getLogger(final String name) { 525 return name != null ? getContext(false).getLogger(name) : getLogger(ReflectionUtil.getCallerClass(2)); 526 } 527 528 /** 529 * Returns a Logger with the specified name. 530 * 531 * @param name The logger name. If null the name of the calling class will be used. 532 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change the 533 * logger but will log a warning if mismatched. 534 * @return The Logger. 535 * @throws UnsupportedOperationException if {@code name} is {@code null} and the calling class cannot be determined. 536 */ 537 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 538 return name != null ? getContext(false).getLogger(name, messageFactory) : getLogger( 539 ReflectionUtil.getCallerClass(2), messageFactory); 540 } 541 542 /** 543 * Returns a Logger with the specified name. 544 * 545 * @param fqcn The fully qualified class name of the class that this method is a member of. 546 * @param name The logger name. 547 * @return The Logger. 548 */ 549 protected static Logger getLogger(final String fqcn, final String name) { 550 return factory.getContext(fqcn, null, null, false).getLogger(name); 551 } 552 553 /** 554 * Returns the root logger. 555 * 556 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 557 */ 558 public static Logger getRootLogger() { 559 return getLogger(ROOT_LOGGER_NAME); 560 } 561}