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 18 package org.apache.commons.logging; 19 20 import java.io.FileOutputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.PrintStream; 24 import java.lang.ref.WeakReference; 25 import java.net.URL; 26 import java.net.URLConnection; 27 import java.nio.charset.StandardCharsets; 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import java.util.Enumeration; 31 import java.util.Hashtable; 32 import java.util.Iterator; 33 import java.util.Objects; 34 import java.util.Properties; 35 import java.util.ServiceConfigurationError; 36 import java.util.ServiceLoader; 37 import java.util.function.Supplier; 38 39 /** 40 * Factory for creating {@link Log} instances, with discovery and 41 * configuration features similar to that employed by standard Java APIs 42 * such as JAXP. 43 * <p> 44 * <strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily 45 * based on the SAXParserFactory and DocumentBuilderFactory implementations 46 * (corresponding to the JAXP pluggability APIs) found in Apache Xerces. 47 * </p> 48 */ 49 public abstract class LogFactory { 50 // Implementation note re AccessController usage 51 // 52 // It is important to keep code invoked via an AccessController to small 53 // auditable blocks. Such code must carefully evaluate all user input 54 // (parameters, system properties, configuration file contents, etc). As an 55 // example, a Log implementation should not write to its log file 56 // with an AccessController anywhere in the call stack, otherwise an 57 // insecure application could configure the log implementation to write 58 // to a protected file using the privileges granted to JCL rather than 59 // to the calling application. 60 // 61 // Under no circumstance should a non-private method return data that is 62 // retrieved via an AccessController. That would allow an insecure application 63 // to invoke that method and obtain data that it is not permitted to have. 64 // 65 // Invoking user-supplied code with an AccessController set is not a major 66 // issue (for example, invoking the constructor of the class specified by 67 // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different 68 // trust domain, and therefore must have permissions to do whatever it 69 // is trying to do regardless of the permissions granted to JCL. There is 70 // a slight issue in that untrusted code may point that environment variable 71 // to another trusted library, in which case the code runs if both that 72 // library and JCL have the necessary permissions even when the untrusted 73 // caller does not. That's a pretty hard route to exploit though. 74 75 /** 76 * The name ({@code priority}) of the key in the configuration file used to 77 * specify the priority of that particular configuration file. The associated value 78 * is a floating-point number; higher values take priority over lower values. 79 */ 80 public static final String PRIORITY_KEY = "priority"; 81 82 /** 83 * The name ({@code use_tccl}) of the key in the configuration file used 84 * to specify whether logging classes should be loaded via the thread 85 * context class loader (TCCL), or not. By default, the TCCL is used. 86 */ 87 public static final String TCCL_KEY = "use_tccl"; 88 89 /** 90 * The name ({@code org.apache.commons.logging.LogFactory}) of the property 91 * used to identify the LogFactory implementation 92 * class name. This can be used as a system property, or as an entry in a 93 * configuration properties file. 94 */ 95 public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory"; 96 97 private static final String FACTORY_LOG4J_API = "org.apache.commons.logging.impl.Log4jApiLogFactory"; 98 99 private static final String LOG4J_TO_SLF4J_BRIDGE = "org.apache.logging.slf4j.SLF4JProvider"; 100 101 private static final String FACTORY_SLF4J = "org.apache.commons.logging.impl.Slf4jLogFactory"; 102 103 /** 104 * The fully qualified class name of the fallback {@code LogFactory} 105 * implementation class to use, if no other can be found. 106 */ 107 public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl"; 108 109 /** 110 * The name ({@code commons-logging.properties}) of the properties file to search for. 111 */ 112 public static final String FACTORY_PROPERTIES = "commons-logging.properties"; 113 114 /** 115 * JDK 1.3+ <a href="https://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider"> 116 * 'Service Provider' specification</a>. 117 */ 118 protected static final String SERVICE_ID = "META-INF/services/org.apache.commons.logging.LogFactory"; 119 120 /** 121 * The name ({@code org.apache.commons.logging.diagnostics.dest}) 122 * of the property used to enable internal commons-logging 123 * diagnostic output, in order to get information on what logging 124 * implementations are being discovered, what class loaders they 125 * are loaded through, etc. 126 * <p> 127 * If a system property of this name is set then the value is 128 * assumed to be the name of a file. The special strings 129 * STDOUT or STDERR (case-sensitive) indicate output to 130 * System.out and System.err respectively. 131 * <p> 132 * Diagnostic logging should be used only to debug problematic 133 * configurations and should not be set in normal production use. 134 */ 135 public static final String DIAGNOSTICS_DEST_PROPERTY = "org.apache.commons.logging.diagnostics.dest"; 136 137 /** 138 * When null (the usual case), no diagnostic output will be 139 * generated by LogFactory or LogFactoryImpl. When non-null, 140 * interesting events will be written to the specified object. 141 */ 142 private static final PrintStream DIAGNOSTICS_STREAM; 143 144 /** 145 * A string that gets prefixed to every message output by the 146 * logDiagnostic method, so that users can clearly see which 147 * LogFactory class is generating the output. 148 */ 149 private static final String DIAGNOSTICS_PREFIX; 150 151 /** 152 * Setting this system property 153 * ({@code org.apache.commons.logging.LogFactory.HashtableImpl}) 154 * value allows the {@code Hashtable} used to store 155 * class loaders to be substituted by an alternative implementation. 156 * <p> 157 * <strong>Note:</strong> {@code LogFactory} will print: 158 * </p> 159 * <pre> 160 * [ERROR] LogFactory: Load of custom hash table failed 161 * </pre> 162 * <p> 163 * to system error and then continue using a standard Hashtable. 164 * </p> 165 * <p> 166 * <strong>Usage:</strong> Set this property when Java is invoked 167 * and {@code LogFactory} will attempt to load a new instance 168 * of the given implementation class. 169 * For example, running the following ant scriplet: 170 * </p> 171 * <pre> 172 * <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"> 173 * ... 174 * <sysproperty 175 * key="org.apache.commons.logging.LogFactory.HashtableImpl" 176 * value="org.apache.commons.logging.AltHashtable"/> 177 * </java> 178 * </pre> 179 * <p> 180 * will mean that {@code LogFactory} will load an instance of 181 * {@code org.apache.commons.logging.AltHashtable}. 182 * </p> 183 * <p> 184 * A typical use case is to allow a custom 185 * Hashtable implementation using weak references to be substituted. 186 * This will allow class loaders to be garbage collected without 187 * the need to release them (on 1.3+ JVMs only, of course ;). 188 * </p> 189 */ 190 public static final String HASHTABLE_IMPLEMENTATION_PROPERTY = "org.apache.commons.logging.LogFactory.HashtableImpl"; 191 192 /** Name used to load the weak hash table implementation by names. */ 193 private static final String WEAK_HASHTABLE_CLASSNAME = "org.apache.commons.logging.impl.WeakHashtable"; 194 195 /** 196 * A reference to the class loader that loaded this class. This is the 197 * same as LogFactory.class.getClassLoader(). However computing this 198 * value isn't quite as simple as that, as we potentially need to use 199 * AccessControllers etc. It's more efficient to compute it once and 200 * cache it here. 201 */ 202 private static final WeakReference<ClassLoader> thisClassLoaderRef; 203 204 /** 205 * Maximum number of {@link ServiceLoader} errors to ignore, while 206 * looking for an implementation. 207 */ 208 private static final int MAX_BROKEN_SERVICES = 3; 209 210 /** 211 * The previously constructed {@code LogFactory} instances, keyed by 212 * the {@code ClassLoader} with which it was created. 213 */ 214 protected static Hashtable<ClassLoader, LogFactory> factories; 215 216 /** 217 * Previously constructed {@code LogFactory} instance as in the 218 * {@code factories} map, but for the case where 219 * {@code getClassLoader} returns {@code null}. 220 * This can happen when: 221 * <ul> 222 * <li>using JDK1.1 and the calling code is loaded via the system 223 * class loader (very common)</li> 224 * <li>using JDK1.2+ and the calling code is loaded via the boot 225 * class loader (only likely for embedded systems work).</li> 226 * </ul> 227 * Note that {@code factories} is a <em>Hashtable</em> (not a HashMap), 228 * and hash tables don't allow null as a key. 229 * @deprecated since 1.1.2 230 */ 231 @Deprecated 232 protected static volatile LogFactory nullClassLoaderFactory; 233 234 static { 235 // note: it's safe to call methods before initDiagnostics (though 236 // diagnostic output gets discarded). 237 final ClassLoader thisClassLoader = getClassLoader(LogFactory.class); 238 thisClassLoaderRef = new WeakReference<>(thisClassLoader); 239 // In order to avoid confusion where multiple instances of JCL are 240 // being used via different class loaders within the same app, we 241 // ensure each logged message has a prefix of form 242 // [LogFactory from class loader OID] 243 // 244 // Note that this prefix should be kept consistent with that 245 // in LogFactoryImpl. However here we don't need to output info 246 // about the actual *instance* of LogFactory, as all methods that 247 // output diagnostics from this class are static. 248 String classLoaderName; 249 try { 250 classLoaderName = thisClassLoader != null ? objectId(thisClassLoader) : "BOOTLOADER"; 251 } catch (final SecurityException e) { 252 classLoaderName = "UNKNOWN"; 253 } 254 DIAGNOSTICS_PREFIX = "[LogFactory from " + classLoaderName + "] "; 255 DIAGNOSTICS_STREAM = initDiagnostics(); 256 logClassLoaderEnvironment(LogFactory.class); 257 factories = createFactoryStore(); 258 logDiagnostic("BOOTSTRAP COMPLETED"); 259 } 260 261 /** 262 * Remember this factory, so later calls to LogFactory.getCachedFactory 263 * can return the previously created object (together with all its 264 * cached Log objects). 265 * 266 * @param classLoader should be the current context class loader. Note that 267 * this can be null under some circumstances; this is ok. 268 * @param factory should be the factory to cache. This should never be null. 269 */ 270 private static void cacheFactory(final ClassLoader classLoader, final LogFactory factory) { 271 // Ideally we would assert(factory != null) here. However reporting 272 // errors from within a logging implementation is a little tricky! 273 if (factory != null) { 274 if (classLoader == null) { 275 nullClassLoaderFactory = factory; 276 } else { 277 factories.put(classLoader, factory); 278 } 279 } 280 } 281 282 /** 283 * Creates a LogFactory object or a LogConfigurationException object. 284 * 285 * @param factoryClassName Factory class. 286 * @param classLoader used to load the specified factory class. This is expected to be either the TCCL or the class loader which loaded this class. 287 * Note that the class loader which loaded this class might be "null" (for example, the boot loader) for embedded systems. 288 * @return either a LogFactory object or a LogConfigurationException object. 289 * @since 1.1 290 */ 291 protected static Object createFactory(final String factoryClassName, final ClassLoader classLoader) { 292 // This will be used to diagnose bad configurations 293 // and allow a useful message to be sent to the user 294 Class<?> logFactoryClass = null; 295 try { 296 if (classLoader != null) { 297 try { 298 // First the given class loader param (thread class loader) 299 300 // Warning: must typecast here & allow exception 301 // to be generated/caught & recast properly. 302 logFactoryClass = classLoader.loadClass(factoryClassName); 303 if (LogFactory.class.isAssignableFrom(logFactoryClass)) { 304 if (isDiagnosticsEnabled()) { 305 logDiagnostic("Loaded class " + logFactoryClass.getName() + " from class loader " + objectId(classLoader)); 306 } 307 } else // 308 // This indicates a problem with the ClassLoader tree. 309 // An incompatible ClassLoader was used to load the 310 // implementation. 311 // As the same classes 312 // must be available in multiple class loaders, 313 // it is very likely that multiple JCL jars are present. 314 // The most likely fix for this 315 // problem is to remove the extra JCL jars from the 316 // ClassLoader hierarchy. 317 // 318 if (isDiagnosticsEnabled()) { 319 logDiagnostic("Factory class " + logFactoryClass.getName() + " loaded from class loader " + objectId(logFactoryClass.getClassLoader()) 320 + " does not extend '" + LogFactory.class.getName() + "' as loaded by this class loader."); 321 logHierarchy("[BAD CL TREE] ", classLoader); 322 } 323 // Force a ClassCastException 324 return LogFactory.class.cast(logFactoryClass.getConstructor().newInstance()); 325 326 } catch (final ClassNotFoundException ex) { 327 if (classLoader == thisClassLoaderRef.get()) { 328 // Nothing more to try, onwards. 329 if (isDiagnosticsEnabled()) { 330 logDiagnostic("Unable to locate any class called '" + factoryClassName + "' via class loader " + objectId(classLoader)); 331 } 332 throw ex; 333 } 334 // ignore exception, continue 335 } catch (final NoClassDefFoundError e) { 336 if (classLoader == thisClassLoaderRef.get()) { 337 // Nothing more to try, onwards. 338 if (isDiagnosticsEnabled()) { 339 logDiagnostic("Class '" + factoryClassName + "' cannot be loaded" + " via class loader " + objectId(classLoader) 340 + " - it depends on some other class that cannot be found."); 341 } 342 throw e; 343 } 344 // ignore exception, continue 345 } catch (final ClassCastException e) { 346 if (classLoader == thisClassLoaderRef.get()) { 347 // There's no point in falling through to the code below that 348 // tries again with thisClassLoaderRef, because we've just tried 349 // loading with that loader (not the TCCL). Just throw an 350 // appropriate exception here. 351 final boolean implementsLogFactory = implementsLogFactory(logFactoryClass); 352 // 353 // Construct a good message: users may not actual expect that a custom implementation 354 // has been specified. Several well known containers use this mechanism to adapt JCL 355 // to their native logging system. 356 // 357 final StringBuilder msg = new StringBuilder(); 358 msg.append("The application has specified that a custom LogFactory implementation should be used but Class '"); 359 msg.append(factoryClassName); 360 msg.append("' cannot be converted to '"); 361 msg.append(LogFactory.class.getName()); 362 msg.append("'. "); 363 if (implementsLogFactory) { 364 msg.append("The conflict is caused by the presence of multiple LogFactory classes in incompatible class loaders. Background can"); 365 msg.append(" be found in https://commons.apache.org/logging/tech.html. If you have not explicitly specified a custom LogFactory"); 366 msg.append(" then it is likely that the container has set one without your knowledge. In this case, consider using the "); 367 msg.append("commons-logging-adapters.jar file or specifying the standard LogFactory from the command line. "); 368 } else { 369 msg.append("Please check the custom implementation. "); 370 } 371 msg.append("Help can be found at https://commons.apache.org/logging/troubleshooting.html."); 372 logDiagnostic(msg.toString()); 373 throw new ClassCastException(msg.toString()); 374 } 375 // Ignore exception, continue. Presumably the class loader was the 376 // TCCL; the code below will try to load the class via thisClassLoaderRef. 377 // This will handle the case where the original calling class is in 378 // a shared classpath but the TCCL has a copy of LogFactory and the 379 // specified LogFactory implementation; we will fall back to using the 380 // LogFactory implementation from the same class loader as this class. 381 // 382 // Issue: this doesn't handle the reverse case, where this LogFactory 383 // is in the webapp, and the specified LogFactory implementation is 384 // in a shared classpath. In that case: 385 // (a) the class really does implement LogFactory (bad log msg above) 386 // (b) the fallback code will result in exactly the same problem. 387 } 388 } 389 390 /* 391 * At this point, either classLoader == null, OR classLoader was unable to load factoryClass. 392 * 393 * In either case, we call Class.forName, which is equivalent to LogFactory.class.getClassLoader().load(name), that is, we ignore the class loader 394 * parameter the caller passed, and fall back to trying the class loader associated with this class. See the Javadoc for the newFactory method for 395 * more info on the consequences of this. 396 * 397 * Notes: * LogFactory.class.getClassLoader() may return 'null' if LogFactory is loaded by the bootstrap class loader. 398 */ 399 // Warning: must typecast here & allow exception 400 // to be generated/caught & recast properly. 401 if (isDiagnosticsEnabled()) { 402 logDiagnostic( 403 "Unable to load factory class via class loader " + objectId(classLoader) + " - trying the class loader associated with this LogFactory."); 404 } 405 logFactoryClass = Class.forName(factoryClassName); 406 // Force a ClassCastException 407 return LogFactory.class.cast(logFactoryClass.getConstructor().newInstance()); 408 } catch (final Exception e) { 409 // Check to see if we've got a bad configuration 410 if (isDiagnosticsEnabled()) { 411 logDiagnostic("Unable to create LogFactory instance."); 412 } 413 if (logFactoryClass != null && !LogFactory.class.isAssignableFrom(logFactoryClass)) { 414 return new LogConfigurationException("The chosen LogFactory implementation does not extend LogFactory. Please check your configuration.", e); 415 } 416 return new LogConfigurationException(e); 417 } 418 } 419 420 /** 421 * Creates the hash table which will be used to store a map of 422 * (context class loader -> logfactory-object). Version 1.2+ of Java 423 * supports "weak references", allowing a custom Hashtable class 424 * to be used which uses only weak references to its keys. Using weak 425 * references can fix memory leaks on webapp unload in some cases (though 426 * not all). Version 1.1 of Java does not support weak references, so we 427 * must dynamically determine which we are using. And just for fun, this 428 * code also supports the ability for a system property to specify an 429 * arbitrary Hashtable implementation name. 430 * <p> 431 * Note that the correct way to ensure no memory leaks occur is to ensure 432 * that LogFactory.release(contextClassLoader) is called whenever a 433 * webapp is undeployed. 434 * </p> 435 */ 436 private static Hashtable<ClassLoader, LogFactory> createFactoryStore() { 437 Hashtable<ClassLoader, LogFactory> result = null; 438 String storeImplementationClass; 439 try { 440 storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null); 441 } catch (final SecurityException ex) { 442 // Permissions don't allow this to be accessed. Default to the "modern" 443 // weak hash table implementation if it is available. 444 storeImplementationClass = null; 445 } 446 if (storeImplementationClass == null) { 447 storeImplementationClass = WEAK_HASHTABLE_CLASSNAME; 448 } 449 try { 450 final Class<Hashtable<ClassLoader, LogFactory>> implementationClass = (Class<Hashtable<ClassLoader, LogFactory>>) Class 451 .forName(storeImplementationClass); 452 result = implementationClass.getConstructor().newInstance(); 453 } catch (final Throwable t) { 454 handleThrowable(t); // may re-throw t 455 // ignore 456 if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) { 457 // if the user's trying to set up a custom implementation, give a clue 458 if (isDiagnosticsEnabled()) { 459 // use internal logging to issue the warning 460 logDiagnostic("[ERROR] LogFactory: Load of custom Hashtable failed"); 461 } else { 462 // we *really* want this output, even if diagnostics weren't 463 // explicitly enabled by the user. 464 System.err.println("[ERROR] LogFactory: Load of custom Hashtable failed"); 465 } 466 } 467 } 468 if (result == null) { 469 result = new Hashtable<>(); 470 } 471 return result; 472 } 473 474 /** 475 * Gets the thread context class loader if available; otherwise return null. 476 * <p> 477 * Most/all code should call getContextClassLoaderInternal rather than 478 * calling this method directly. 479 * </p> 480 * <p> 481 * The thread context class loader is available for JDK 1.2 482 * or later, if certain security conditions are met. 483 * </p> 484 * <p> 485 * Note that no internal logging is done within this method because 486 * this method is called every time LogFactory.getLogger() is called, 487 * and we don't want too much output generated here. 488 * </p> 489 * 490 * @throws LogConfigurationException if a suitable class loader 491 * cannot be identified. 492 * @return the thread's context class loader or {@code null} if the Java security 493 * policy forbids access to the context class loader from one of the classes 494 * in the current call stack. 495 * @since 1.1 496 */ 497 protected static ClassLoader directGetContextClassLoader() throws LogConfigurationException { 498 ClassLoader classLoader = null; 499 try { 500 classLoader = Thread.currentThread().getContextClassLoader(); 501 } catch (final SecurityException ignore) { 502 // getContextClassLoader() throws SecurityException when 503 // the context class loader isn't an ancestor of the 504 // calling class's class loader, or if security 505 // permissions are restricted. 506 // 507 // We ignore this exception to be consistent with the previous 508 // behavior (e.g. 1.1.3 and earlier). 509 } 510 // Return the selected class loader 511 return classLoader; 512 } 513 514 /** 515 * Gets a cached log factory (keyed by contextClassLoader) 516 * 517 * @param contextClassLoader is the context class loader associated 518 * with the current thread. This allows separate LogFactory objects 519 * per component within a container, provided each component has 520 * a distinct context class loader set. This parameter may be null 521 * in JDK1.1, and in embedded systems where jcl-using code is 522 * placed in the bootclasspath. 523 * 524 * @return the factory associated with the specified class loader if 525 * one has previously been created, or null if this is the first time 526 * we have seen this particular class loader. 527 */ 528 private static LogFactory getCachedFactory(final ClassLoader contextClassLoader) { 529 if (contextClassLoader == null) { 530 // We have to handle this specially, as factories is a Hashtable 531 // and those don't accept null as a key value. 532 // 533 // nb: nullClassLoaderFactory might be null. That's ok. 534 return nullClassLoaderFactory; 535 } 536 return factories.get(contextClassLoader); 537 } 538 539 /** 540 * Safely get access to the class loader for the specified class. 541 * <p> 542 * Theoretically, calling getClassLoader can throw a security exception, 543 * and so should be done under an AccessController in order to provide 544 * maximum flexibility. However in practice people don't appear to use 545 * security policies that forbid getClassLoader calls. So for the moment 546 * all code is written to call this method rather than Class.getClassLoader, 547 * so that we could put AccessController stuff in this method without any 548 * disruption later if we need to. 549 * </p> 550 * <p> 551 * Even when using an AccessController, however, this method can still 552 * throw SecurityException. Commons Logging basically relies on the 553 * ability to access class loaders. A policy that forbids all 554 * class loader access will also prevent commons-logging from working: 555 * currently this method will throw an exception preventing the entire app 556 * from starting up. Maybe it would be good to detect this situation and 557 * just disable all commons-logging? Not high priority though - as stated 558 * above, security policies that prevent class loader access aren't common. 559 * </p> 560 * <p> 561 * Note that returning an object fetched via an AccessController would 562 * technically be a security flaw anyway; untrusted code that has access 563 * to a trusted JCL library could use it to fetch the class loader for 564 * a class even when forbidden to do so directly. 565 * </p> 566 * 567 * @param clazz Class. 568 * @return a ClassLoader. 569 * 570 * @since 1.1 571 */ 572 protected static ClassLoader getClassLoader(final Class<?> clazz) { 573 try { 574 return clazz.getClassLoader(); 575 } catch (final SecurityException ex) { 576 logDiagnostic(() -> "Unable to get class loader for class '" + clazz + "' due to security restrictions - " + ex.getMessage()); 577 throw ex; 578 } 579 } 580 581 /** 582 * Gets a user-provided configuration file. 583 * <p> 584 * The classpath of the specified classLoader (usually the context class loader) 585 * is searched for properties files of the specified name. If none is found, 586 * null is returned. If more than one is found, then the file with the greatest 587 * value for its PRIORITY property is returned. If multiple files have the 588 * same PRIORITY value then the first in the classpath is returned. 589 * </p> 590 * <p> 591 * This differs from the 1.0.x releases; those always use the first one found. 592 * However as the priority is a new field, this change is backwards compatible. 593 * </p> 594 * <p> 595 * The purpose of the priority field is to allow a webserver administrator to 596 * override logging settings in all webapps by placing a commons-logging.properties 597 * file in a shared classpath location with a priority > 0; this overrides any 598 * commons-logging.properties files without priorities which are in the 599 * webapps. Webapps can also use explicit priorities to override a configuration 600 * file in the shared classpath if needed. 601 * </p> 602 */ 603 private static Properties getConfigurationFile(final ClassLoader classLoader, final String fileName) { 604 Properties props = null; 605 double priority = 0.0; 606 URL propsUrl = null; 607 try { 608 final Enumeration<URL> urls = getResources(classLoader, fileName); 609 if (urls == null) { 610 return null; 611 } 612 while (urls.hasMoreElements()) { 613 final URL url = urls.nextElement(); 614 final Properties newProps = getProperties(url); 615 if (newProps != null) { 616 if (props == null) { 617 propsUrl = url; 618 props = newProps; 619 final String priorityStr = props.getProperty(PRIORITY_KEY); 620 priority = 0.0; 621 if (priorityStr != null) { 622 priority = Double.parseDouble(priorityStr); 623 } 624 if (isDiagnosticsEnabled()) { 625 logDiagnostic("[LOOKUP] Properties file found at '" + url + "'" + " with priority " + priority); 626 } 627 } else { 628 final String newPriorityStr = newProps.getProperty(PRIORITY_KEY); 629 double newPriority = 0.0; 630 if (newPriorityStr != null) { 631 newPriority = Double.parseDouble(newPriorityStr); 632 } 633 if (newPriority > priority) { 634 if (isDiagnosticsEnabled()) { 635 logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + " with priority " + newPriority + " overrides file at '" + propsUrl 636 + "'" + " with priority " + priority); 637 } 638 propsUrl = url; 639 props = newProps; 640 priority = newPriority; 641 } else if (isDiagnosticsEnabled()) { 642 logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + " with priority " + newPriority + " does not override file at '" 643 + propsUrl + "'" + " with priority " + priority); 644 } 645 } 646 647 } 648 } 649 } catch (final SecurityException e) { 650 logDiagnostic("SecurityException thrown while trying to find/read config files."); 651 } 652 if (isDiagnosticsEnabled()) { 653 if (props == null) { 654 logDiagnostic("[LOOKUP] No properties file of name '" + fileName + "' found."); 655 } else { 656 logDiagnostic("[LOOKUP] Properties file of name '" + fileName + "' found at '" + propsUrl + '"'); 657 } 658 } 659 return props; 660 } 661 662 /** 663 * Gets the current context class loader. 664 * <p> 665 * In versions prior to 1.1, this method did not use an AccessController. 666 * In version 1.1, an AccessController wrapper was incorrectly added to 667 * this method, causing a minor security flaw. 668 * </p> 669 * <p> 670 * In version 1.1.1 this change was reverted; this method no longer uses 671 * an AccessController. User code wishing to obtain the context class loader 672 * must invoke this method via AccessController.doPrivileged if it needs 673 * support for that. 674 * </p> 675 * 676 * @return the context class loader associated with the current thread, 677 * or null if security doesn't allow it. 678 * @throws LogConfigurationException if there was some weird error while 679 * attempting to get the context class loader. 680 */ 681 protected static ClassLoader getContextClassLoader() throws LogConfigurationException { 682 return directGetContextClassLoader(); 683 } 684 685 /** 686 * Calls {@link LogFactory#directGetContextClassLoader()} under the control of an 687 * AccessController class. This means that Java code running under a 688 * security manager that forbids access to ClassLoaders will still work 689 * if this class is given appropriate privileges, even when the caller 690 * doesn't have such privileges. Without using an AccessController, the 691 * the entire call stack must have the privilege before the call is 692 * allowed. 693 * 694 * @return the context class loader associated with the current thread, 695 * or null if security doesn't allow it. 696 * @throws LogConfigurationException if there was some weird error while 697 * attempting to get the context class loader. 698 */ 699 private static ClassLoader getContextClassLoaderInternal() throws LogConfigurationException { 700 return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) LogFactory::directGetContextClassLoader); 701 } 702 703 /** 704 * Constructs (if necessary) and return a {@code LogFactory} instance, using the following ordered lookup procedure to determine the name of the 705 * implementation class to be loaded. 706 * <ul> 707 * <li>The {@code org.apache.commons.logging.LogFactory} system property.</li> 708 * <li>The JDK 1.3 Service Discovery mechanism</li> 709 * <li>Use the properties file {@code commons-logging.properties} file, if found in the class path of this class. The configuration file is in standard 710 * {@link java.util.Properties} format and contains the fully qualified name of the implementation class with the key being the system property defined 711 * above.</li> 712 * <li>Fall back to a default implementation class ({@code org.apache.commons.logging.impl.LogFactoryImpl}).</li> 713 * </ul> 714 * <p> 715 * <em>NOTE</em> - If the properties file method of identifying the {@code LogFactory} implementation class is utilized, all of the properties defined in 716 * this file will be set as configuration attributes on the corresponding {@code LogFactory} instance. 717 * </p> 718 * <p> 719 * <em>NOTE</em> - In a multi-threaded environment it is possible that two different instances will be returned for the same class loader environment. 720 * </p> 721 * 722 * @return a {@code LogFactory}. 723 * @throws LogConfigurationException if the implementation class is not available or cannot be instantiated. 724 */ 725 public static LogFactory getFactory() throws LogConfigurationException { 726 // Identify the class loader we will be using 727 final ClassLoader contextClassLoader = getContextClassLoaderInternal(); 728 729 // This is an odd enough situation to report about. This 730 // output will be a nuisance on JDK1.1, as the system 731 // class loader is null in that environment. 732 if (contextClassLoader == null) { 733 logDiagnostic("Context class loader is null."); 734 } 735 736 // Return any previously registered factory for this class loader 737 LogFactory factory = getCachedFactory(contextClassLoader); 738 if (factory != null) { 739 return factory; 740 } 741 742 if (isDiagnosticsEnabled()) { 743 logDiagnostic( 744 "[LOOKUP] LogFactory implementation requested for the first time for context class loader " + 745 objectId(contextClassLoader)); 746 logHierarchy("[LOOKUP] ", contextClassLoader); 747 } 748 749 // Load properties file. 750 // 751 // If the properties file exists, then its contents are used as 752 // "attributes" on the LogFactory implementation class. One particular 753 // property may also control which LogFactory concrete subclass is 754 // used, but only if other discovery mechanisms fail.. 755 // 756 // As the properties file (if it exists) will be used one way or 757 // another in the end we may as well look for it first. 758 759 final Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES); 760 761 // Determine whether we will be using the thread context class loader to 762 // load logging classes or not by checking the loaded properties file (if any). 763 boolean useTccl = contextClassLoader != null; 764 if (props != null) { 765 final String useTCCLStr = props.getProperty(TCCL_KEY); 766 useTccl &= useTCCLStr == null || Boolean.parseBoolean(useTCCLStr); 767 } 768 // If TCCL is still enabled at this point, we check if it resolves this class 769 if (useTccl) { 770 try { 771 if (!LogFactory.class.equals(Class.forName(LogFactory.class.getName(), false, contextClassLoader))) { 772 logDiagnostic(() -> "The class " + LogFactory.class.getName() + " loaded by the context class loader " + objectId(contextClassLoader) 773 + " and this class differ. Disabling the usage of the context class loader." 774 + "Background can be found in https://commons.apache.org/logging/tech.html. "); 775 logHierarchy("[BAD CL TREE] ", contextClassLoader); 776 useTccl = false; 777 } 778 } catch (final ClassNotFoundException ignored) { 779 logDiagnostic(() -> "The class " + LogFactory.class.getName() + " is not present in the the context class loader " 780 + objectId(contextClassLoader) + ". Disabling the usage of the context class loader." 781 + "Background can be found in https://commons.apache.org/logging/tech.html. "); 782 logHierarchy("[BAD CL TREE] ", contextClassLoader); 783 useTccl = false; 784 } 785 } 786 final ClassLoader baseClassLoader = useTccl ? contextClassLoader : thisClassLoaderRef.get(); 787 788 // Determine which concrete LogFactory subclass to use. 789 // First, try a global system property 790 logDiagnostic(() -> "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY + 791 "] to define the LogFactory subclass to use..."); 792 793 try { 794 final String factoryClass = getSystemProperty(FACTORY_PROPERTY, null); 795 if (factoryClass != null) { 796 logDiagnostic(() -> "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass + 797 "' as specified by system property " + FACTORY_PROPERTY); 798 factory = newFactory(factoryClass, baseClassLoader, contextClassLoader); 799 } else { 800 logDiagnostic(() -> "[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined."); 801 } 802 } catch (final SecurityException e) { 803 logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class" + ": [" 804 + trim(e.getMessage()) + "]. Trying alternative implementations..."); 805 // ignore 806 } catch (final RuntimeException e) { 807 // This is not consistent with the behavior when a bad LogFactory class is 808 // specified in a services file. 809 // 810 // One possible exception that can occur here is a ClassCastException when 811 // the specified class wasn't castable to this LogFactory type. 812 logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to create an instance of the custom factory class: [" + trim(e.getMessage()) 813 + "] as specified by a system property."); 814 throw e; 815 } 816 // 817 // Second, try to find a service by using the JDK 1.3 class 818 // discovery mechanism, which involves putting a file with the name 819 // of an interface class in the META-INF/services directory, where the 820 // contents of the file is a single line specifying a concrete class 821 // that implements the desired interface. 822 if (factory == null) { 823 logDiagnostic("[LOOKUP] Using ServiceLoader to define the LogFactory subclass to use..."); 824 try { 825 final ServiceLoader<LogFactory> serviceLoader = ServiceLoader.load(LogFactory.class, baseClassLoader); 826 final Iterator<LogFactory> iterator = serviceLoader.iterator(); 827 828 int i = MAX_BROKEN_SERVICES; 829 while (factory == null && i-- > 0) { 830 try { 831 if (iterator.hasNext()) { 832 factory = iterator.next(); 833 } 834 } catch (final ServiceConfigurationError | LinkageError ex) { 835 logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to find an instance of LogFactory: [" + trim(ex.getMessage()) 836 + "]. Trying alternative implementations..."); 837 } 838 } 839 } catch (final Exception ex) { 840 // note: if the specified LogFactory class wasn't compatible with LogFactory 841 // for some reason, a ClassCastException will be caught here, and attempts will 842 // continue to find a compatible class. 843 logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class: [" 844 + trim(ex.getMessage()) + "]. Trying alternative implementations..."); 845 // ignore 846 } 847 } 848 // 849 // Third try looking into the properties file read earlier (if found) 850 if (factory == null) { 851 if (props != null) { 852 logDiagnostic(() -> 853 "[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY + 854 "' to define the LogFactory subclass to use..."); 855 final String factoryClass = props.getProperty(FACTORY_PROPERTY); 856 if (factoryClass != null) { 857 logDiagnostic(() -> 858 "[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'"); 859 factory = newFactory(factoryClass, baseClassLoader, contextClassLoader); 860 // TODO: think about whether we need to handle exceptions from newFactory 861 } else { 862 logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass."); 863 } 864 } else { 865 logDiagnostic("[LOOKUP] No properties file available to determine LogFactory subclass from.."); 866 } 867 } 868 // 869 // Fourth, try one of the three provided factories first from the specified classloader 870 // and then from the current one. 871 if (factory == null) { 872 factory = newStandardFactory(baseClassLoader); 873 } 874 if (factory == null && baseClassLoader != thisClassLoaderRef.get()) { 875 factory = newStandardFactory(thisClassLoaderRef.get()); 876 } 877 if (factory != null) { 878 if (isDiagnosticsEnabled()) { 879 logDiagnostic("Created object " + objectId(factory) + " to manage class loader " + objectId(contextClassLoader)); 880 } 881 } else { 882 logDiagnostic(() -> 883 "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT + 884 "' via the same class loader that loaded this LogFactory class (ie not looking in the context class loader)."); 885 // Note: unlike the above code which can try to load custom LogFactory 886 // implementations via the TCCL, we don't try to load the default LogFactory 887 // implementation via the context class loader because: 888 // * that can cause problems (see comments in newFactory method) 889 // * no-one should be customising the code of the default class 890 // Yes, we do give up the ability for the child to ship a newer 891 // version of the LogFactoryImpl class and have it used dynamically 892 // by an old LogFactory class in the parent, but that isn't 893 // necessarily a good idea anyway. 894 factory = newFactory(FACTORY_DEFAULT, thisClassLoaderRef.get(), contextClassLoader); 895 } 896 if (factory != null) { 897 /** 898 * Always cache using context class loader. 899 */ 900 cacheFactory(contextClassLoader, factory); 901 if (props != null) { 902 final Enumeration<?> names = props.propertyNames(); 903 while (names.hasMoreElements()) { 904 final String name = Objects.toString(names.nextElement(), null); 905 final String value = props.getProperty(name); 906 factory.setAttribute(name, value); 907 } 908 } 909 } 910 return factory; 911 } 912 913 /** 914 * Gets a named logger, without the application having to care about factories. 915 * 916 * @param clazz Class from which a log name will be derived 917 * @return a named logger. 918 * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned 919 */ 920 public static Log getLog(final Class<?> clazz) throws LogConfigurationException { 921 return getFactory().getInstance(clazz); 922 } 923 924 /** 925 * Gets a named logger, without the application having to care about factories. 926 * 927 * @param name Logical name of the {@code Log} instance to be returned (the meaning of this name is only known to the underlying logging implementation that 928 * is being wrapped) 929 * @return a named logger. 930 * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned 931 */ 932 public static Log getLog(final String name) throws LogConfigurationException { 933 return getFactory().getInstance(name); 934 } 935 936 /** 937 * Given a URL that refers to a .properties file, load that file. 938 * This is done under an AccessController so that this method will 939 * succeed when this jarfile is privileged but the caller is not. 940 * This method must therefore remain private to avoid security issues. 941 * <p> 942 * {@code Null} is returned if the URL cannot be opened. 943 * </p> 944 */ 945 private static Properties getProperties(final URL url) { 946 return AccessController.doPrivileged((PrivilegedAction<Properties>) () -> { 947 // We must ensure that useCaches is set to false, as the 948 // default behavior of java is to cache file handles, and 949 // this "locks" files, preventing hot-redeploy on windows. 950 try { 951 final URLConnection connection = url.openConnection(); 952 connection.setUseCaches(false); 953 try (InputStream stream = connection.getInputStream()) { 954 if (stream != null) { 955 final Properties props = new Properties(); 956 props.load(stream); 957 return props; 958 } 959 } catch (final IOException e) { 960 logDiagnostic(() -> "Unable to close stream for URL " + url); 961 } 962 } catch (final IOException e) { 963 logDiagnostic(() -> "Unable to read URL " + url); 964 } 965 966 return null; 967 }); 968 } 969 970 /** 971 * Given a file name, return an enumeration of URLs pointing to 972 * all the occurrences of that file name in the classpath. 973 * <p> 974 * This is just like ClassLoader.getResources except that the 975 * operation is done under an AccessController so that this method will 976 * succeed when this jarfile is privileged but the caller is not. 977 * This method must therefore remain private to avoid security issues. 978 * </p> 979 * <p> 980 * If no instances are found, an Enumeration is returned whose 981 * hasMoreElements method returns false (ie an "empty" enumeration). 982 * If resources could not be listed for some reason, null is returned. 983 * </p> 984 */ 985 private static Enumeration<URL> getResources(final ClassLoader loader, final String name) { 986 return AccessController.doPrivileged((PrivilegedAction<Enumeration<URL>>) () -> { 987 try { 988 if (loader != null) { 989 return loader.getResources(name); 990 } 991 return ClassLoader.getSystemResources(name); 992 } catch (final IOException e) { 993 logDiagnostic(() -> "Exception while trying to find configuration file " + name + ":" + e.getMessage()); 994 return null; 995 } catch (final NoSuchMethodError e) { 996 // we must be running on a 1.1 JVM which doesn't support 997 // ClassLoader.getSystemResources; just return null in 998 // this case. 999 return null; 1000 } 1001 }); 1002 } 1003 1004 /** 1005 * Read the specified system property, using an AccessController so that 1006 * the property can be read if JCL has been granted the appropriate 1007 * security rights even if the calling code has not. 1008 * <p> 1009 * Take care not to expose the value returned by this method to the 1010 * calling application in any way; otherwise the calling app can use that 1011 * info to access data that should not be available to it. 1012 * </p> 1013 */ 1014 private static String getSystemProperty(final String key, final String def) 1015 throws SecurityException { 1016 return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(key, def)); 1017 } 1018 1019 /** 1020 * Checks whether the supplied Throwable is one that needs to be 1021 * re-thrown and ignores all others. 1022 * 1023 * The following errors are re-thrown: 1024 * <ul> 1025 * <li>ThreadDeath</li> 1026 * <li>VirtualMachineError</li> 1027 * </ul> 1028 * 1029 * @param t the Throwable to check 1030 */ 1031 protected static void handleThrowable(final Throwable t) { 1032 if (t instanceof ThreadDeath) { 1033 throw (ThreadDeath) t; 1034 } 1035 if (t instanceof VirtualMachineError) { 1036 throw (VirtualMachineError) t; 1037 } 1038 // All other instances of Throwable will be silently ignored 1039 } 1040 1041 /** 1042 * Determines whether the given class actually implements {@code LogFactory}. 1043 * Diagnostic information is also logged. 1044 * <p> 1045 * <strong>Usage:</strong> to diagnose whether a class loader conflict is the cause 1046 * of incompatibility. The test used is whether the class is assignable from 1047 * the {@code LogFactory} class loaded by the class's class loader. 1048 * @param logFactoryClass {@code Class} which may implement {@code LogFactory} 1049 * @return true if the {@code logFactoryClass} does extend 1050 * {@code LogFactory} when that class is loaded via the same 1051 * class loader that loaded the {@code logFactoryClass}. 1052 * </p> 1053 */ 1054 private static boolean implementsLogFactory(final Class<?> logFactoryClass) { 1055 boolean implementsLogFactory = false; 1056 if (logFactoryClass != null) { 1057 try { 1058 final ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader(); 1059 if (logFactoryClassLoader == null) { 1060 logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot class loader"); 1061 } else { 1062 logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader); 1063 final Class<?> factoryFromCustomLoader = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader); 1064 implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass); 1065 final String logFactoryClassName = logFactoryClass.getName(); 1066 if (implementsLogFactory) { 1067 logDiagnostic(() -> "[CUSTOM LOG FACTORY] " + logFactoryClassName + " implements LogFactory but was loaded by an incompatible class loader."); 1068 } else { 1069 logDiagnostic(() -> "[CUSTOM LOG FACTORY] " + logFactoryClassName + " does not implement LogFactory."); 1070 } 1071 } 1072 } catch (final SecurityException e) { 1073 // 1074 // The application is running within a hostile security environment. 1075 // This will make it very hard to diagnose issues with JCL. 1076 // Consider running less securely whilst debugging this issue. 1077 // 1078 logDiagnostic( 1079 () -> "[CUSTOM LOG FACTORY] SecurityException caught trying to determine whether the compatibility was caused by a class loader conflict: " 1080 + e.getMessage()); 1081 } catch (final LinkageError e) { 1082 // 1083 // This should be an unusual circumstance. 1084 // LinkageError's usually indicate that a dependent class has incompatibly changed. 1085 // Another possibility may be an exception thrown by an initializer. 1086 // Time for a clean rebuild? 1087 // 1088 logDiagnostic( 1089 () -> "[CUSTOM LOG FACTORY] LinkageError caught trying to determine whether the compatibility was caused by a class loader conflict: " 1090 + e.getMessage()); 1091 } catch (final ClassNotFoundException e) { 1092 // 1093 // LogFactory cannot be loaded by the class loader which loaded the custom factory implementation. 1094 // The custom implementation is not viable until this is corrected. 1095 // Ensure that the JCL jar and the custom class are available from the same class loader. 1096 // Running with diagnostics on should give information about the class loaders used 1097 // to load the custom factory. 1098 // 1099 logDiagnostic(() -> "[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by the class loader which loaded " 1100 + "the custom LogFactory implementation. Is the custom factory in the right class loader?"); 1101 } 1102 } 1103 return implementsLogFactory; 1104 } 1105 1106 /** 1107 * Tests whether the user wants internal diagnostic output. If so, 1108 * returns an appropriate writer object. Users can enable diagnostic 1109 * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to 1110 * a file name, or the special values STDOUT or STDERR. 1111 */ 1112 private static PrintStream initDiagnostics() { 1113 String dest; 1114 try { 1115 dest = getSystemProperty(DIAGNOSTICS_DEST_PROPERTY, null); 1116 if (dest == null) { 1117 return null; 1118 } 1119 } catch (final SecurityException ex) { 1120 // We must be running in some very secure environment. 1121 // We just have to assume output is not wanted.. 1122 return null; 1123 } 1124 1125 if (dest.equals("STDOUT")) { 1126 return System.out; 1127 } 1128 if (dest.equals("STDERR")) { 1129 return System.err; 1130 } 1131 try { 1132 // open the file in append mode 1133 final FileOutputStream fos = new FileOutputStream(dest, true); 1134 return new PrintStream(fos, false, StandardCharsets.UTF_8.name()); 1135 } catch (final IOException ex) { 1136 // We should report this to the user - but how? 1137 return null; 1138 } 1139 } 1140 1141 private static boolean isClassAvailable(final String className, final ClassLoader classLoader) { 1142 logDiagnostic(() -> "Checking if class '" + className + "' is available in class loader " + objectId(classLoader)); 1143 try { 1144 Class.forName(className, true, classLoader); 1145 return true; 1146 } catch (final ClassNotFoundException | LinkageError e) { 1147 logDiagnostic(() -> "Failed to load class '" + className + "' from class loader " + objectId(classLoader) + ": " + e.getMessage()); 1148 } 1149 return false; 1150 } 1151 1152 /** 1153 * Tests whether the user enabled internal logging. 1154 * <p> 1155 * By the way, sorry for the incorrect grammar, but calling this method 1156 * areDiagnosticsEnabled just isn't Java beans style. 1157 * </p> 1158 * 1159 * @return true if calls to logDiagnostic will have any effect. 1160 * @since 1.1 1161 */ 1162 protected static boolean isDiagnosticsEnabled() { 1163 return DIAGNOSTICS_STREAM != null; 1164 } 1165 1166 /** 1167 * Generates useful diagnostics regarding the class loader tree for 1168 * the specified class. 1169 * <p> 1170 * As an example, if the specified class was loaded via a webapp's 1171 * class loader, then you may get the following output: 1172 * </p> 1173 * <pre> 1174 * Class com.acme.Foo was loaded via class loader 11111 1175 * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT 1176 * </pre> 1177 * <p> 1178 * This method returns immediately if isDiagnosticsEnabled() 1179 * returns false. 1180 * </p> 1181 * 1182 * @param clazz is the class whose class loader + tree are to be 1183 * output. 1184 */ 1185 private static void logClassLoaderEnvironment(final Class<?> clazz) { 1186 if (!isDiagnosticsEnabled()) { 1187 return; 1188 } 1189 try { 1190 // Deliberately use System.getProperty here instead of getSystemProperty; if 1191 // the overall security policy for the calling application forbids access to 1192 // these variables then we do not want to output them to the diagnostic stream. 1193 logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir")); 1194 logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path")); 1195 } catch (final SecurityException ex) { 1196 logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths."); 1197 } 1198 final String className = clazz.getName(); 1199 ClassLoader classLoader; 1200 try { 1201 classLoader = getClassLoader(clazz); 1202 } catch (final SecurityException ex) { 1203 // not much useful diagnostics we can print here! 1204 logDiagnostic("[ENV] Security forbids determining the class loader for " + className); 1205 return; 1206 } 1207 logDiagnostic("[ENV] Class " + className + " was loaded via class loader " + objectId(classLoader)); 1208 logHierarchy("[ENV] Ancestry of class loader which loaded " + className + " is ", classLoader); 1209 } 1210 1211 /** 1212 * Writes the specified message to the internal logging destination. 1213 * <p> 1214 * Note that this method is private; concrete subclasses of this class 1215 * should not call it because the diagnosticPrefix string this 1216 * method puts in front of all its messages is LogFactory@...., 1217 * while subclasses should put SomeSubClass@... 1218 * </p> 1219 * <p> 1220 * Subclasses should instead compute their own prefix, then call 1221 * logRawDiagnostic. Note that calling isDiagnosticsEnabled is 1222 * fine for subclasses. 1223 * </p> 1224 * <p> 1225 * Note that it is safe to call this method before initDiagnostics 1226 * is called; any output will just be ignored (as isDiagnosticsEnabled 1227 * will return false). 1228 * </p> 1229 * 1230 * @param msg is the diagnostic message to be output. 1231 */ 1232 private static void logDiagnostic(final String msg) { 1233 if (DIAGNOSTICS_STREAM != null) { 1234 logDiagnosticDirect(msg); 1235 } 1236 } 1237 1238 /** 1239 * Writes the specified message to the internal logging destination. 1240 * <p> 1241 * Note that this method is private; concrete subclasses of this class 1242 * should not call it because the diagnosticPrefix string this 1243 * method puts in front of all its messages is LogFactory@...., 1244 * while subclasses should put SomeSubClass@... 1245 * </p> 1246 * <p> 1247 * Subclasses should instead compute their own prefix, then call 1248 * logRawDiagnostic. Note that calling isDiagnosticsEnabled is 1249 * fine for subclasses. 1250 * </p> 1251 * <p> 1252 * Note that it is safe to call this method before initDiagnostics 1253 * is called; any output will just be ignored (as isDiagnosticsEnabled 1254 * will return false). 1255 * </p> 1256 * 1257 * @param msg is the diagnostic message to be output. 1258 */ 1259 private static void logDiagnostic(final Supplier<String> msg) { 1260 if (DIAGNOSTICS_STREAM != null) { 1261 logDiagnosticDirect(msg.get()); 1262 } 1263 } 1264 1265 private static void logDiagnosticDirect(final String msg) { 1266 DIAGNOSTICS_STREAM.print(DIAGNOSTICS_PREFIX); 1267 DIAGNOSTICS_STREAM.println(msg); 1268 DIAGNOSTICS_STREAM.flush(); 1269 } 1270 1271 /** 1272 * Logs diagnostic messages about the given class loader 1273 * and it's hierarchy. The prefix is prepended to the message 1274 * and is intended to make it easier to understand the logs. 1275 * @param prefix 1276 * @param classLoader 1277 */ 1278 private static void logHierarchy(final String prefix, ClassLoader classLoader) { 1279 if (!isDiagnosticsEnabled()) { 1280 return; 1281 } 1282 ClassLoader systemClassLoader; 1283 if (classLoader != null) { 1284 logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoader.toString() + "'"); 1285 } 1286 try { 1287 systemClassLoader = ClassLoader.getSystemClassLoader(); 1288 } catch (final SecurityException ex) { 1289 logDiagnostic(prefix + "Security forbids determining the system class loader."); 1290 return; 1291 } 1292 if (classLoader != null) { 1293 final StringBuilder buf = new StringBuilder(prefix + "ClassLoader tree:"); 1294 for(;;) { 1295 buf.append(objectId(classLoader)); 1296 if (classLoader == systemClassLoader) { 1297 buf.append(" (SYSTEM) "); 1298 } 1299 try { 1300 classLoader = classLoader.getParent(); 1301 } catch (final SecurityException ex) { 1302 buf.append(" --> SECRET"); 1303 break; 1304 } 1305 buf.append(" --> "); 1306 if (classLoader == null) { 1307 buf.append("BOOT"); 1308 break; 1309 } 1310 } 1311 logDiagnostic(buf.toString()); 1312 } 1313 } 1314 1315 /** 1316 * Writes the specified message to the internal logging destination. 1317 * 1318 * @param msg is the diagnostic message to be output. 1319 * @since 1.1 1320 */ 1321 protected static final void logRawDiagnostic(final String msg) { 1322 if (DIAGNOSTICS_STREAM != null) { 1323 DIAGNOSTICS_STREAM.println(msg); 1324 DIAGNOSTICS_STREAM.flush(); 1325 } 1326 } 1327 1328 /** 1329 * Method provided for backwards compatibility; see newFactory version that 1330 * takes 3 parameters. 1331 * <p> 1332 * This method would only ever be called in some rather odd situation. 1333 * Note that this method is static, so overriding in a subclass doesn't 1334 * have any effect unless this method is called from a method in that 1335 * subclass. However this method only makes sense to use from the 1336 * getFactory method, and as that is almost always invoked via 1337 * LogFactory.getFactory, any custom definition in a subclass would be 1338 * pointless. Only a class with a custom getFactory method, then invoked 1339 * directly via CustomFactoryImpl.getFactory or similar would ever call 1340 * this. Anyway, it's here just in case, though the "managed class loader" 1341 * value output to the diagnostics will not report the correct value. 1342 * </p> 1343 * 1344 * @param factoryClass factory class. 1345 * @param classLoader class loader. 1346 * @return a LogFactory. 1347 */ 1348 protected static LogFactory newFactory(final String factoryClass, 1349 final ClassLoader classLoader) { 1350 return newFactory(factoryClass, classLoader, null); 1351 } 1352 1353 /** 1354 * Gets a new instance of the specified {@code LogFactory} implementation class, loaded by the specified class loader. If that fails, try the class loader 1355 * used to load this (abstract) LogFactory. 1356 * <p> 1357 * <b>ClassLoader conflicts</b> 1358 * </p> 1359 * <p> 1360 * Note that there can be problems if the specified ClassLoader is not the same as the class loader that loaded this class, that is, when loading a concrete 1361 * LogFactory subclass via a context class loader. 1362 * </p> 1363 * <p> 1364 * The problem is the same one that can occur when loading a concrete Log subclass via a context class loader. 1365 * </p> 1366 * <p> 1367 * The problem occurs when code running in the context class loader calls class X which was loaded via a parent class loader, and class X then calls 1368 * LogFactory.getFactory (either directly or via LogFactory.getLog). Because class X was loaded via the parent, it binds to LogFactory loaded via the 1369 * parent. When the code in this method finds some LogFactoryYYYY class in the child (context) class loader, and there also happens to be a LogFactory class 1370 * defined in the child class loader, then LogFactoryYYYY will be bound to LogFactory@childloader. It cannot be cast to LogFactory@parentloader, that is, 1371 * this method cannot return the object as the desired type. Note that it doesn't matter if the LogFactory class in the child class loader is identical to 1372 * the LogFactory class in the parent class loader, they are not compatible. 1373 * </p> 1374 * <p> 1375 * The solution taken here is to simply print out an error message when this occurs then throw an exception. The deployer of the application must ensure 1376 * they remove all occurrences of the LogFactory class from the child class loader in order to resolve the issue. Note that they do not have to move the 1377 * custom LogFactory subclass; that is ok as long as the only LogFactory class it can find to bind to is in the parent class loader. 1378 * </p> 1379 * 1380 * @param factoryClass Fully qualified name of the {@code LogFactory} implementation class 1381 * @param classLoader ClassLoader from which to load this class 1382 * @param contextClassLoader is the context that this new factory will manage logging for. 1383 * @return a new instance of the specified {@code LogFactory}. 1384 * @throws LogConfigurationException if a suitable instance cannot be created 1385 * @since 1.1 1386 */ 1387 protected static LogFactory newFactory(final String factoryClass, 1388 final ClassLoader classLoader, 1389 final ClassLoader contextClassLoader) 1390 throws LogConfigurationException { 1391 // Note that any unchecked exceptions thrown by the createFactory 1392 // method will propagate out of this method; in particular a 1393 // ClassCastException can be thrown. 1394 final Object result = AccessController.doPrivileged((PrivilegedAction<?>) () -> createFactory(factoryClass, classLoader)); 1395 if (result instanceof LogConfigurationException) { 1396 final LogConfigurationException ex = (LogConfigurationException) result; 1397 logDiagnostic(() -> "An error occurred while loading the factory class:" + ex.getMessage()); 1398 throw ex; 1399 } 1400 logDiagnostic(() -> "Created object " + objectId(result) + " to manage class loader " + objectId(contextClassLoader)); 1401 return (LogFactory) result; 1402 } 1403 1404 /** 1405 * Tries to load one of the standard three implementations from the given classloader. 1406 * <p> 1407 * We assume that {@code classLoader} can load this class. 1408 * </p> 1409 * @param classLoader The classloader to use. 1410 * @return An implementation of this class. 1411 */ 1412 private static LogFactory newStandardFactory(final ClassLoader classLoader) { 1413 if (isClassAvailable(LOG4J_TO_SLF4J_BRIDGE, classLoader)) { 1414 try { 1415 return (LogFactory) Class.forName(FACTORY_SLF4J, true, classLoader).getConstructor().newInstance(); 1416 } catch (final LinkageError | ReflectiveOperationException ignored) { 1417 } finally { 1418 logDiagnostic(() -> 1419 "[LOOKUP] Log4j API to SLF4J redirection detected. Loading the SLF4J LogFactory implementation '" + FACTORY_SLF4J + "'."); 1420 } 1421 } 1422 try { 1423 return (LogFactory) Class.forName(FACTORY_LOG4J_API, true, classLoader).getConstructor().newInstance(); 1424 } catch (final LinkageError | ReflectiveOperationException ignored) { 1425 } finally { 1426 logDiagnostic(() -> "[LOOKUP] Loading the Log4j API LogFactory implementation '" + FACTORY_LOG4J_API + "'."); 1427 } 1428 try { 1429 return (LogFactory) Class.forName(FACTORY_SLF4J, true, classLoader).getConstructor().newInstance(); 1430 } catch (final LinkageError | ReflectiveOperationException ignored) { 1431 } finally { 1432 logDiagnostic(() -> "[LOOKUP] Loading the SLF4J LogFactory implementation '" + FACTORY_SLF4J + "'."); 1433 } 1434 try { 1435 return (LogFactory) Class.forName(FACTORY_DEFAULT, true, classLoader).getConstructor().newInstance(); 1436 } catch (final LinkageError | ReflectiveOperationException ignored) { 1437 } finally { 1438 logDiagnostic(() -> "[LOOKUP] Loading the legacy LogFactory implementation '" + FACTORY_DEFAULT + "'."); 1439 } 1440 return null; 1441 } 1442 1443 /** 1444 * Returns a string that uniquely identifies the specified object, including 1445 * its class. 1446 * <p> 1447 * The returned string is of form {@code "className@hashCode"}, that is, is the same as 1448 * the return value of the {@link Object#toString()} method, but works even when 1449 * the specified object's class has overridden the toString method. 1450 * </p> 1451 * 1452 * @param obj may be null. 1453 * @return a string of form {@code className@hashCode}, or "null" if obj is null. 1454 * @since 1.1 1455 */ 1456 public static String objectId(final Object obj) { 1457 if (obj == null) { 1458 return "null"; 1459 } 1460 return obj.getClass().getName() + "@" + System.identityHashCode(obj); 1461 } 1462 1463 /** 1464 * Releases any internal references to previously created {@link LogFactory} 1465 * instances that have been associated with the specified class loader 1466 * (if any), after calling the instance method {@code release()} on 1467 * each of them. 1468 * 1469 * @param classLoader ClassLoader for which to release the LogFactory 1470 */ 1471 public static void release(final ClassLoader classLoader) { 1472 logDiagnostic(() -> "Releasing factory for class loader " + objectId(classLoader)); 1473 // factories is not final and could be replaced in this block. 1474 final Hashtable<ClassLoader, LogFactory> factories = LogFactory.factories; 1475 synchronized (factories) { 1476 if (classLoader == null) { 1477 if (nullClassLoaderFactory != null) { 1478 nullClassLoaderFactory.release(); 1479 nullClassLoaderFactory = null; 1480 } 1481 } else { 1482 final LogFactory factory = factories.get(classLoader); 1483 if (factory != null) { 1484 factory.release(); 1485 factories.remove(classLoader); 1486 } 1487 } 1488 } 1489 } 1490 1491 /** 1492 * Release any internal references to previously created {@link LogFactory} 1493 * instances, after calling the instance method {@code release()} on 1494 * each of them. This is useful in environments like servlet containers, 1495 * which implement application reloading by throwing away a ClassLoader. 1496 * Dangling references to objects in that class loader would prevent 1497 * garbage collection. 1498 */ 1499 public static void releaseAll() { 1500 logDiagnostic("Releasing factory for all class loaders."); 1501 // factories is not final and could be replaced in this block. 1502 final Hashtable<ClassLoader, LogFactory> factories = LogFactory.factories; 1503 synchronized (factories) { 1504 factories.values().forEach(LogFactory::release); 1505 factories.clear(); 1506 if (nullClassLoaderFactory != null) { 1507 nullClassLoaderFactory.release(); 1508 nullClassLoaderFactory = null; 1509 } 1510 } 1511 } 1512 1513 /** Trims the given string in a null-safe manner. */ 1514 private static String trim(final String src) { 1515 return src != null ? src.trim() : null; 1516 } 1517 1518 /** 1519 * Constructs a new instance. 1520 */ 1521 protected LogFactory() { 1522 } 1523 1524 /** 1525 * Gets the configuration attribute with the specified name (if any), 1526 * or {@code null} if there is no such attribute. 1527 * 1528 * @param name Name of the attribute to return 1529 * @return the configuration attribute with the specified name. 1530 */ 1531 public abstract Object getAttribute(String name); 1532 1533 /** 1534 * Gets an array containing the names of all currently defined configuration attributes. If there are no such attributes, a zero length array is returned. 1535 * 1536 * @return an array containing the names of all currently defined configuration attributes 1537 */ 1538 public abstract String[] getAttributeNames(); 1539 1540 /** 1541 * Gets a Log for the given class. 1542 * 1543 * @param clazz Class for which a suitable Log name will be derived 1544 * @return a name from the specified class. 1545 * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned 1546 */ 1547 public abstract Log getInstance(Class<?> clazz) throws LogConfigurationException; 1548 1549 /** 1550 * Gets a (possibly new) {@code Log} instance, using the factory's current set of configuration attributes. 1551 * <p> 1552 * <strong>NOTE</strong> - Depending upon the implementation of the {@code LogFactory} you are using, the {@code Log} instance you are returned may or may 1553 * not be local to the current application, and may or may not be returned again on a subsequent call with the same name argument. 1554 * </p> 1555 * 1556 * @param name Logical name of the {@code Log} instance to be returned (the meaning of this name is only known to the underlying logging implementation that 1557 * is being wrapped) 1558 * @return a {@code Log} instance. 1559 * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned 1560 */ 1561 public abstract Log getInstance(String name) 1562 throws LogConfigurationException; 1563 1564 /** 1565 * Releases any internal references to previously created {@link Log} 1566 * instances returned by this factory. This is useful in environments 1567 * like servlet containers, which implement application reloading by 1568 * throwing away a ClassLoader. Dangling references to objects in that 1569 * class loader would prevent garbage collection. 1570 */ 1571 public abstract void release(); 1572 1573 /** 1574 * Removes any configuration attribute associated with the specified name. 1575 * If there is no such attribute, no action is taken. 1576 * 1577 * @param name Name of the attribute to remove 1578 */ 1579 public abstract void removeAttribute(String name); 1580 1581 // 1582 // We can't do this in the class constructor, as there are many 1583 // static methods on this class that can be called before any 1584 // LogFactory instances are created, and they depend upon this 1585 // stuff having been set up. 1586 // 1587 // Note that this block must come after any variable declarations used 1588 // by any methods called from this block, as we want any static initializer 1589 // associated with the variable to run first. If static initializers for 1590 // variables run after this code, then (a) their value might be needed 1591 // by methods called from here, and (b) they might *override* any value 1592 // computed here! 1593 // 1594 // So the wisest thing to do is just to place this code at the very end 1595 // of the class file. 1596 1597 /** 1598 * Sets the configuration attribute with the specified name. Calling 1599 * this with a {@code null} value is equivalent to calling 1600 * {@code removeAttribute(name)}. 1601 * 1602 * @param name Name of the attribute to set 1603 * @param value Value of the attribute to set, or {@code null} 1604 * to remove any setting for this attribute 1605 */ 1606 public abstract void setAttribute(String name, Object value); 1607 1608 }