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 */ 017 package org.apache.logging.log4j; 018 019 import java.net.URI; 020 import java.util.Map; 021 import java.util.SortedMap; 022 import java.util.TreeMap; 023 024 import org.apache.logging.log4j.message.MessageFactory; 025 import org.apache.logging.log4j.message.StringFormatterMessageFactory; 026 import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; 027 import org.apache.logging.log4j.spi.LoggerContext; 028 import org.apache.logging.log4j.spi.LoggerContextFactory; 029 import org.apache.logging.log4j.spi.Provider; 030 import org.apache.logging.log4j.status.StatusLogger; 031 import org.apache.logging.log4j.util.PropertiesUtil; 032 import org.apache.logging.log4j.util.ProviderUtil; 033 import org.apache.logging.log4j.util.Strings; 034 035 /** 036 * The anchor point for the logging system. 037 */ 038 public class LogManager { 039 040 private static volatile LoggerContextFactory factory; 041 042 private static final String FACTORY_PROPERTY_NAME = "log4j2.loggerContextFactory"; 043 044 private static final Logger LOGGER = StatusLogger.getLogger(); 045 046 /** 047 * The name of the root Logger. 048 */ 049 public static final String ROOT_LOGGER_NAME = Strings.EMPTY; 050 051 /** 052 * Scans the classpath to find all logging implementation. Currently, only one will 053 * be used but this could be extended to allow multiple implementations to be used. 054 */ 055 static { 056 // Shortcut binding to force a specific logging implementation. 057 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 058 final String factoryClass = managerProps.getStringProperty(FACTORY_PROPERTY_NAME); 059 final ClassLoader cl = ProviderUtil.findClassLoader(); 060 if (factoryClass != null) { 061 try { 062 final Class<?> clazz = cl.loadClass(factoryClass); 063 if (LoggerContextFactory.class.isAssignableFrom(clazz)) { 064 factory = (LoggerContextFactory) clazz.newInstance(); 065 } 066 } catch (final ClassNotFoundException cnfe) { 067 LOGGER.error("Unable to locate configured LoggerContextFactory {}", factoryClass); 068 } catch (final Exception ex) { 069 LOGGER.error("Unable to create configured LoggerContextFactory {}", factoryClass, ex); 070 } 071 } 072 073 if (factory == null) { 074 final SortedMap<Integer, LoggerContextFactory> factories = new TreeMap<Integer, LoggerContextFactory>(); 075 076 if (ProviderUtil.hasProviders()) { 077 for (final Provider provider : ProviderUtil.getProviders()) { 078 final String className = provider.getClassName(); 079 if (className != null) { 080 try { 081 final Class<?> clazz = cl.loadClass(className); 082 if (LoggerContextFactory.class.isAssignableFrom(clazz)) { 083 factories.put(provider.getPriority(), (LoggerContextFactory) clazz.newInstance()); 084 } else { 085 LOGGER.error("{} does not implement {}", className, LoggerContextFactory.class.getName()); 086 } 087 } catch (final ClassNotFoundException cnfe) { 088 LOGGER.error("Unable to locate class {} specified in {}", className, 089 provider.getUrl().toString(), cnfe); 090 } catch (final IllegalAccessException iae) { 091 LOGGER.error("Unable to create class {} specified in {}", className, 092 provider.getUrl().toString(), iae); 093 } catch (final Exception e) { 094 LOGGER.error("Unable to create class {} specified in {}", className, 095 provider.getUrl().toString(), e); 096 } 097 } 098 } 099 100 if (factories.size() == 0) { 101 LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); 102 factory = new SimpleLoggerContextFactory(); 103 } else { 104 final StringBuilder sb = new StringBuilder("Multiple logging implementations found: \n"); 105 for (final Map.Entry<Integer, LoggerContextFactory> entry : factories.entrySet()) { 106 sb.append("Factory: ").append(entry.getValue().getClass().getName()); 107 sb.append(", Weighting: ").append(entry.getKey()).append('\n'); 108 } 109 factory = factories.get(factories.lastKey()); 110 sb.append("Using factory: ").append(factory.getClass().getName()); 111 LOGGER.warn(sb.toString()); 112 113 } 114 } else { 115 LOGGER.error("Unable to locate a logging implementation, using SimpleLogger"); 116 factory = new SimpleLoggerContextFactory(); 117 } 118 } 119 } 120 121 /** 122 * Detects if a Logger with the specified name exists. This is a convenience method for porting from version 1. 123 * 124 * @param name 125 * The Logger name to search for. 126 * @return true if the Logger exists, false otherwise. 127 * @see LoggerContext#hasLogger(String) 128 */ 129 public static boolean exists(final String name) { 130 return getContext().hasLogger(name); 131 } 132 133 /** 134 * Gets the class name of the caller in the current stack at the given {@code depth}. 135 * 136 * @param depth a 0-based index in the current stack. 137 * @return a class name 138 */ 139 private static String getClassName(final int depth) { 140 return new Throwable().getStackTrace()[depth].getClassName(); 141 } 142 143 /** 144 * Returns the current LoggerContext. 145 * <p> 146 * WARNING - The LoggerContext returned by this method may not be the LoggerContext used to create a Logger 147 * for the calling class. 148 * @return The current LoggerContext. 149 */ 150 public static LoggerContext getContext() { 151 return factory.getContext(LogManager.class.getName(), null, null, true); 152 } 153 154 /** 155 * Returns a LoggerContext. 156 * 157 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 158 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 159 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 160 * returned. If true then only a single LoggerContext will be returned. 161 * @return a LoggerContext. 162 */ 163 public static LoggerContext getContext(final boolean currentContext) { 164 return factory.getContext(LogManager.class.getName(), null, null, currentContext, null, null); 165 } 166 167 /** 168 * Returns a LoggerContext. 169 * 170 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 171 * ClassLoader. 172 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 173 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 174 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 175 * returned. If true then only a single LoggerContext will be returned. 176 * @return a LoggerContext. 177 */ 178 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { 179 return factory.getContext(LogManager.class.getName(), loader, null, currentContext); 180 } 181 182 /** 183 * Returns a LoggerContext. 184 * 185 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 186 * ClassLoader. 187 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 188 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 189 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 190 * returned. If true then only a single LoggerContext will be returned. 191 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 192 * @return a LoggerContext. 193 */ 194 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 195 final Object externalContext) { 196 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext); 197 } 198 199 /** 200 * Returns a LoggerContext. 201 * 202 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 203 * ClassLoader. 204 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 205 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 206 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 207 * returned. If true then only a single LoggerContext will be returned. 208 * @param configLocation The URI for the configuration to use. 209 * @return a LoggerContext. 210 */ 211 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 212 final URI configLocation) { 213 return factory.getContext(LogManager.class.getName(), loader, null, currentContext, configLocation, null); 214 } 215 216 217 /** 218 * Returns a LoggerContext. 219 * 220 * @param loader The ClassLoader for the context. If null the context will attempt to determine the appropriate 221 * ClassLoader. 222 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 223 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 224 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 225 * returned. If true then only a single LoggerContext will be returned. 226 * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. 227 * @param configLocation The URI for the configuration to use. 228 * @return a LoggerContext. 229 */ 230 public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext, 231 final Object externalContext, final URI configLocation) { 232 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 233 null); 234 } 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 be 245 * 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, 253 final String name) { 254 return factory.getContext(LogManager.class.getName(), loader, externalContext, currentContext, configLocation, 255 name); 256 } 257 258 /** 259 * Returns a LoggerContext 260 * @param fqcn The fully qualified class name of the Class that this method is a member of. 261 * @param currentContext if false the LoggerContext appropriate for the caller of this method is returned. For 262 * example, in a web application if the caller is a class in WEB-INF/lib then one LoggerContext may be 263 * returned and if the caller is a class in the container's classpath then a different LoggerContext may be 264 * returned. If true then only a single LoggerContext will be returned. 265 * @return a LoggerContext. 266 */ 267 protected static LoggerContext getContext(final String fqcn, final boolean currentContext) { 268 return factory.getContext(fqcn, null, null, currentContext); 269 } 270 271 /** 272 * Returns a LoggerContext 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 be 279 * 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 * @return The LoggerContextFactory. 290 */ 291 public static LoggerContextFactory getFactory() { 292 return factory; 293 } 294 295 /** 296 * Sets the current LoggerContextFactory to use. Normally, the appropriate LoggerContextFactory is created at 297 * startup, but in certain environments, a LoggerContextFactory implementation may not be available at this point. 298 * Thus, an alternative LoggerContextFactory can be set at runtime. 299 * 300 * <p> 301 * Note that any Logger or LoggerContext objects already created will still be valid, but they will no longer be 302 * accessible through LogManager. Thus, <strong>it is a bad idea to use this method without a good reason</strong>! 303 * Generally, this method should be used only during startup before any code starts caching Logger objects. 304 * </p> 305 * 306 * @param factory the LoggerContextFactory to use. 307 */ 308 // FIXME: should we allow only one update of the factory? 309 public static void setFactory(final LoggerContextFactory factory) { 310 LogManager.factory = factory; 311 } 312 313 /** 314 * Returns a formatter Logger using the fully qualified name of the Class as the Logger name. 315 * <p> 316 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 317 * </p> 318 * <p> 319 * Short-hand for {@code getLogger(clazz, StringFormatterMessageFactory.INSTANCE)} 320 * </p> 321 * 322 * @param clazz 323 * The Class whose name should be used as the Logger name. 324 * @return The Logger, created with a {@link StringFormatterMessageFactory} 325 * @see Logger#fatal(Marker, String, Object...) 326 * @see Logger#fatal(String, Object...) 327 * @see Logger#error(Marker, String, Object...) 328 * @see Logger#error(String, Object...) 329 * @see Logger#warn(Marker, String, Object...) 330 * @see Logger#warn(String, Object...) 331 * @see Logger#info(Marker, String, Object...) 332 * @see Logger#info(String, Object...) 333 * @see Logger#debug(Marker, String, Object...) 334 * @see Logger#debug(String, Object...) 335 * @see Logger#trace(Marker, String, Object...) 336 * @see Logger#trace(String, Object...) 337 * @see StringFormatterMessageFactory 338 */ 339 public static Logger getFormatterLogger(final Class<?> clazz) { 340 return getLogger(clazz != null ? clazz.getName() : getClassName(2), StringFormatterMessageFactory.INSTANCE); 341 } 342 343 /** 344 * Returns a formatter Logger using the fully qualified name of the value's Class as the Logger name. 345 * <p> 346 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 347 * </p> 348 * <p> 349 * Short-hand for {@code getLogger(value, StringFormatterMessageFactory.INSTANCE)} 350 * </p> 351 * 352 * @param value 353 * The value's whose class name should be used as the Logger name. 354 * @return The Logger, created with a {@link StringFormatterMessageFactory} 355 * @see Logger#fatal(Marker, String, Object...) 356 * @see Logger#fatal(String, Object...) 357 * @see Logger#error(Marker, String, Object...) 358 * @see Logger#error(String, Object...) 359 * @see Logger#warn(Marker, String, Object...) 360 * @see Logger#warn(String, Object...) 361 * @see Logger#info(Marker, String, Object...) 362 * @see Logger#info(String, Object...) 363 * @see Logger#debug(Marker, String, Object...) 364 * @see Logger#debug(String, Object...) 365 * @see Logger#trace(Marker, String, Object...) 366 * @see Logger#trace(String, Object...) 367 * @see StringFormatterMessageFactory 368 */ 369 public static Logger getFormatterLogger(final Object value) { 370 return getLogger(value != null ? value.getClass().getName() : getClassName(2), 371 StringFormatterMessageFactory.INSTANCE); 372 } 373 374 /** 375 * Returns a formatter Logger with the specified name. 376 * <p> 377 * This logger let you use a {@link java.util.Formatter} string in the message to format parameters. 378 * </p> 379 * <p> 380 * Short-hand for {@code getLogger(name, StringFormatterMessageFactory.INSTANCE)} 381 * </p> 382 * 383 * @param name The logger name. If null it will default to the name of the calling class. 384 * @return The Logger, created with a {@link StringFormatterMessageFactory} 385 * @see Logger#fatal(Marker, String, Object...) 386 * @see Logger#fatal(String, Object...) 387 * @see Logger#error(Marker, String, Object...) 388 * @see Logger#error(String, Object...) 389 * @see Logger#warn(Marker, String, Object...) 390 * @see Logger#warn(String, Object...) 391 * @see Logger#info(Marker, String, Object...) 392 * @see Logger#info(String, Object...) 393 * @see Logger#debug(Marker, String, Object...) 394 * @see Logger#debug(String, Object...) 395 * @see Logger#trace(Marker, String, Object...) 396 * @see Logger#trace(String, Object...) 397 * @see StringFormatterMessageFactory 398 */ 399 public static Logger getFormatterLogger(final String name) { 400 return getLogger(name != null ? name : getClassName(2), StringFormatterMessageFactory.INSTANCE); 401 } 402 403 /** 404 * Returns a Logger with the name of the calling class. 405 * @return The Logger for the calling class. 406 */ 407 public static Logger getLogger() { 408 return getLogger(getClassName(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 */ 417 public static Logger getLogger(final Class<?> clazz) { 418 return getLogger(clazz != null ? clazz.getName() : getClassName(2)); 419 } 420 421 /** 422 * Returns a Logger using the fully qualified name of the Class as the Logger name. 423 * @param clazz The Class whose name should be used as the Logger name. If null it will default to the calling 424 * class. 425 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 426 * the logger but will log a warning if mismatched. 427 * @return The Logger. 428 */ 429 public static Logger getLogger(final Class<?> clazz, final MessageFactory messageFactory) { 430 return getLogger(clazz != null ? clazz.getName() : getClassName(2), messageFactory); 431 } 432 433 /** 434 * Returns a Logger with the name of the calling class. 435 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 436 * the logger but will log a warning if mismatched. 437 * @return The Logger for the calling class. 438 */ 439 public static Logger getLogger(final MessageFactory messageFactory) { 440 return getLogger(getClassName(2), messageFactory); 441 } 442 443 /** 444 * Returns a Logger using the fully qualified class name of the value as the Logger name. 445 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 446 * class will be used as the logger name. 447 * @return The Logger. 448 */ 449 public static Logger getLogger(final Object value) { 450 return getLogger(value != null ? value.getClass().getName() : getClassName(2)); 451 } 452 453 /** 454 * Returns a Logger using the fully qualified class name of the value as the Logger name. 455 * @param value The value whose class name should be used as the Logger name. If null the name of the calling 456 * class will be used as the logger name. 457 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 458 * the logger but will log a warning if mismatched. 459 * @return The Logger. 460 */ 461 public static Logger getLogger(final Object value, final MessageFactory messageFactory) { 462 return getLogger(value != null ? value.getClass().getName() : getClassName(2), messageFactory); 463 } 464 465 /** 466 * Returns a Logger with the specified name. 467 * 468 * @param name The logger name. If null the name of the calling class will be used. 469 * @return The Logger. 470 */ 471 public static Logger getLogger(final String name) { 472 final String actualName = name != null ? name : getClassName(2); 473 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName); 474 } 475 476 /** 477 * Returns a Logger with the specified name. 478 * 479 * @param name The logger name. If null the name of the calling class will be used. 480 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 481 * the logger but will log a warning if mismatched. 482 * @return The Logger. 483 */ 484 public static Logger getLogger(final String name, final MessageFactory messageFactory) { 485 final String actualName = name != null ? name : getClassName(2); 486 return factory.getContext(LogManager.class.getName(), null, null, false).getLogger(actualName, messageFactory); 487 } 488 489 /** 490 * Returns a Logger with the specified name. 491 * 492 * @param fqcn The fully qualified class name of the class that this method is a member of. 493 * @param name The logger name. 494 * @return The Logger. 495 */ 496 protected static Logger getLogger(final String fqcn, final String name) { 497 return factory.getContext(fqcn, null, null, false).getLogger(name); 498 } 499 500 /** 501 * Returns the root logger. 502 * 503 * @return the root logger, named {@link #ROOT_LOGGER_NAME}. 504 */ 505 public static Logger getRootLogger() { 506 return getLogger(ROOT_LOGGER_NAME); 507 } 508 509 /** 510 * Prevents instantiation 511 */ 512 protected LogManager() { 513 } 514 515 }