1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package java.util.logging; 28 29 import java.io.*; 30 import java.util.*; 31 import java.security.*; 32 import java.lang.ref.ReferenceQueue; 33 import java.lang.ref.WeakReference; 34 import java.lang.reflect.Constructor; 35 import java.lang.reflect.InvocationTargetException; 36 import java.lang.reflect.Method; 37 import java.beans.PropertyChangeListener; 38 import sun.misc.JavaAWTAccess; 39 import sun.misc.SharedSecrets; 40 41 /** 42 * There is a single global LogManager object that is used to 43 * maintain a set of shared state about Loggers and log services. 44 * <p> 45 * This LogManager object: 46 * <ul> 47 * <li> Manages a hierarchical namespace of Logger objects. All 48 * named Loggers are stored in this namespace. 49 * <li> Manages a set of logging control properties. These are 50 * simple key-value pairs that can be used by Handlers and 51 * other logging objects to configure themselves. 52 * </ul> 53 * <p> 54 * The global LogManager object can be retrieved using LogManager.getLogManager(). 55 * The LogManager object is created during class initialization and 56 * cannot subsequently be changed. 57 * <p> 58 * At startup the LogManager class is located using the 59 * java.util.logging.manager system property. 60 * <p> 61 * The LogManager defines two optional system properties that allow control over 62 * the initial configuration: 63 * <ul> 64 * <li>"java.util.logging.config.class" 65 * <li>"java.util.logging.config.file" 66 * </ul> 67 * These two properties may be specified on the command line to the "java" 68 * command, or as system property definitions passed to JNI_CreateJavaVM. 69 * <p> 70 * If the "java.util.logging.config.class" property is set, then the 71 * property value is treated as a class name. The given class will be 72 * loaded, an object will be instantiated, and that object's constructor 73 * is responsible for reading in the initial configuration. (That object 74 * may use other system properties to control its configuration.) The 75 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 76 * to define properties in the LogManager. 77 * <p> 78 * If "java.util.logging.config.class" property is <b>not</b> set, 79 * then the "java.util.logging.config.file" system property can be used 80 * to specify a properties file (in java.util.Properties format). The 81 * initial logging configuration will be read from this file. 82 * <p> 83 * If neither of these properties is defined then the LogManager uses its 84 * default configuration. The default configuration is typically loaded from the 85 * properties file "{@code lib/logging.properties}" in the Java installation 86 * directory. 87 * <p> 88 * The properties for loggers and Handlers will have names starting 89 * with the dot-separated name for the handler or logger. 90 * <p> 91 * The global logging properties may include: 92 * <ul> 93 * <li>A property "handlers". This defines a whitespace or comma separated 94 * list of class names for handler classes to load and register as 95 * handlers on the root Logger (the Logger named ""). Each class 96 * name must be for a Handler class which has a default constructor. 97 * Note that these Handlers may be created lazily, when they are 98 * first used. 99 * 100 * <li>A property "<logger>.handlers". This defines a whitespace or 101 * comma separated list of class names for handlers classes to 102 * load and register as handlers to the specified logger. Each class 103 * name must be for a Handler class which has a default constructor. 104 * Note that these Handlers may be created lazily, when they are 105 * first used. 106 * 107 * <li>A property "<logger>.useParentHandlers". This defines a boolean 108 * value. By default every logger calls its parent in addition to 109 * handling the logging message itself, this often result in messages 110 * being handled by the root logger as well. When setting this property 111 * to false a Handler needs to be configured for this logger otherwise 112 * no logging messages are delivered. 113 * 114 * <li>A property "config". This property is intended to allow 115 * arbitrary configuration code to be run. The property defines a 116 * whitespace or comma separated list of class names. A new instance will be 117 * created for each named class. The default constructor of each class 118 * may execute arbitrary code to update the logging configuration, such as 119 * setting logger levels, adding handlers, adding filters, etc. 120 * </ul> 121 * <p> 122 * Note that all classes loaded during LogManager configuration are 123 * first searched on the system class path before any user class path. 124 * That includes the LogManager class, any config classes, and any 125 * handler classes. 126 * <p> 127 * Loggers are organized into a naming hierarchy based on their 128 * dot separated names. Thus "a.b.c" is a child of "a.b", but 129 * "a.b1" and a.b2" are peers. 130 * <p> 131 * All properties whose names end with ".level" are assumed to define 132 * log levels for Loggers. Thus "foo.level" defines a log level for 133 * the logger called "foo" and (recursively) for any of its children 134 * in the naming hierarchy. Log Levels are applied in the order they 135 * are defined in the properties file. Thus level settings for child 136 * nodes in the tree should come after settings for their parents. 137 * The property name ".level" can be used to set the level for the 138 * root of the tree. 139 * <p> 140 * All methods on the LogManager object are multi-thread safe. 141 * 142 * @since 1.4 143 */ 144 145 public class LogManager { 146 // The global LogManager object 147 private static final LogManager manager; 148 149 // 'props' is assigned within a lock but accessed without it. 150 // Declaring it volatile makes sure that another thread will not 151 // be able to see a partially constructed 'props' object. 152 // (seeing a partially constructed 'props' object can result in 153 // NPE being thrown in Hashtable.get(), because it leaves the door 154 // open for props.getProperties() to be called before the construcor 155 // of Hashtable is actually completed). 156 private volatile Properties props = new Properties(); 157 private final static Level defaultLevel = Level.INFO; 158 159 // The map of the registered listeners. The map value is the registration 160 // count to allow for cases where the same listener is registered many times. 161 private final Map<Object,Integer> listenerMap = new HashMap<>(); 162 163 // LoggerContext for system loggers and user loggers 164 private final LoggerContext systemContext = new SystemLoggerContext(); 165 private final LoggerContext userContext = new LoggerContext(); 166 // non final field - make it volatile to make sure that other threads 167 // will see the new value once ensureLogManagerInitialized() has finished 168 // executing. 169 private volatile Logger rootLogger; 170 // Have we done the primordial reading of the configuration file? 171 // (Must be done after a suitable amount of java.lang.System 172 // initialization has been done) 173 private volatile boolean readPrimordialConfiguration; 174 // Have we initialized global (root) handlers yet? 175 // This gets set to false in readConfiguration 176 private boolean initializedGlobalHandlers = true; 177 // True if JVM death is imminent and the exit hook has been called. 178 private boolean deathImminent; 179 180 static { 181 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() { 182 @Override 183 public LogManager run() { 184 LogManager mgr = null; 185 String cname = null; 186 try { 187 cname = System.getProperty("java.util.logging.manager"); 188 if (cname != null) { 189 try { 190 Class<?> clz = ClassLoader.getSystemClassLoader() 191 .loadClass(cname); 192 mgr = (LogManager) clz.newInstance(); 193 } catch (ClassNotFoundException ex) { 194 Class<?> clz = Thread.currentThread() 195 .getContextClassLoader().loadClass(cname); 196 mgr = (LogManager) clz.newInstance(); 197 } 198 } 199 } catch (Exception ex) { 200 System.err.println("Could not load Logmanager \"" + cname + "\""); 201 ex.printStackTrace(); 202 } 203 if (mgr == null) { 204 mgr = new LogManager(); 205 } 206 return mgr; 207 208 } 209 }); 210 } 211 212 213 // This private class is used as a shutdown hook. 214 // It does a "reset" to close all open handlers. 215 private class Cleaner extends Thread { 216 Cleaner()217 private Cleaner() { 218 /* Set context class loader to null in order to avoid 219 * keeping a strong reference to an application classloader. 220 */ 221 this.setContextClassLoader(null); 222 } 223 224 @Override run()225 public void run() { 226 // This is to ensure the LogManager.<clinit> is completed 227 // before synchronized block. Otherwise deadlocks are possible. 228 LogManager mgr = manager; 229 230 // If the global handlers haven't been initialized yet, we 231 // don't want to initialize them just so we can close them! 232 synchronized (LogManager.this) { 233 // Note that death is imminent. 234 deathImminent = true; 235 initializedGlobalHandlers = true; 236 } 237 238 // Do a reset to close all active handlers. 239 reset(); 240 } 241 } 242 243 244 /** 245 * Protected constructor. This is protected so that container applications 246 * (such as J2EE containers) can subclass the object. It is non-public as 247 * it is intended that there only be one LogManager object, whose value is 248 * retrieved by calling LogManager.getLogManager. 249 */ LogManager()250 protected LogManager() { 251 this(checkSubclassPermissions()); 252 } 253 LogManager(Void checked)254 private LogManager(Void checked) { 255 256 // Add a shutdown hook to close the global handlers. 257 try { 258 Runtime.getRuntime().addShutdownHook(new Cleaner()); 259 } catch (IllegalStateException e) { 260 // If the VM is already shutting down, 261 // We do not need to register shutdownHook. 262 } 263 } 264 checkSubclassPermissions()265 private static Void checkSubclassPermissions() { 266 final SecurityManager sm = System.getSecurityManager(); 267 if (sm != null) { 268 // These permission will be checked in the LogManager constructor, 269 // in order to register the Cleaner() thread as a shutdown hook. 270 // Check them here to avoid the penalty of constructing the object 271 // etc... 272 sm.checkPermission(new RuntimePermission("shutdownHooks")); 273 sm.checkPermission(new RuntimePermission("setContextClassLoader")); 274 } 275 return null; 276 } 277 278 /** 279 * Lazy initialization: if this instance of manager is the global 280 * manager then this method will read the initial configuration and 281 * add the root logger and global logger by calling addLogger(). 282 * 283 * Note that it is subtly different from what we do in LoggerContext. 284 * In LoggerContext we're patching up the logger context tree in order to add 285 * the root and global logger *to the context tree*. 286 * 287 * For this to work, addLogger() must have already have been called 288 * once on the LogManager instance for the default logger being 289 * added. 290 * 291 * This is why ensureLogManagerInitialized() needs to be called before 292 * any logger is added to any logger context. 293 * 294 */ 295 private boolean initializedCalled = false; 296 private volatile boolean initializationDone = false; ensureLogManagerInitialized()297 final void ensureLogManagerInitialized() { 298 final LogManager owner = this; 299 if (initializationDone || owner != manager) { 300 // we don't want to do this twice, and we don't want to do 301 // this on private manager instances. 302 return; 303 } 304 305 // Maybe another thread has called ensureLogManagerInitialized() 306 // before us and is still executing it. If so we will block until 307 // the log manager has finished initialized, then acquire the monitor, 308 // notice that initializationDone is now true and return. 309 // Otherwise - we have come here first! We will acquire the monitor, 310 // see that initializationDone is still false, and perform the 311 // initialization. 312 // 313 synchronized(this) { 314 // If initializedCalled is true it means that we're already in 315 // the process of initializing the LogManager in this thread. 316 // There has been a recursive call to ensureLogManagerInitialized(). 317 final boolean isRecursiveInitialization = (initializedCalled == true); 318 319 assert initializedCalled || !initializationDone 320 : "Initialization can't be done if initialized has not been called!"; 321 322 if (isRecursiveInitialization || initializationDone) { 323 // If isRecursiveInitialization is true it means that we're 324 // already in the process of initializing the LogManager in 325 // this thread. There has been a recursive call to 326 // ensureLogManagerInitialized(). We should not proceed as 327 // it would lead to infinite recursion. 328 // 329 // If initializationDone is true then it means the manager 330 // has finished initializing; just return: we're done. 331 return; 332 } 333 // Calling addLogger below will in turn call requiresDefaultLogger() 334 // which will call ensureLogManagerInitialized(). 335 // We use initializedCalled to break the recursion. 336 initializedCalled = true; 337 try { 338 AccessController.doPrivileged(new PrivilegedAction<Object>() { 339 @Override 340 public Object run() { 341 assert rootLogger == null; 342 assert initializedCalled && !initializationDone; 343 344 // Read configuration. 345 owner.readPrimordialConfiguration(); 346 347 // Create and retain Logger for the root of the namespace. 348 owner.rootLogger = owner.new RootLogger(); 349 owner.addLogger(owner.rootLogger); 350 if (!owner.rootLogger.isLevelInitialized()) { 351 owner.rootLogger.setLevel(defaultLevel); 352 } 353 354 // Adding the global Logger. 355 // Do not call Logger.getGlobal() here as this might trigger 356 // subtle inter-dependency issues. 357 @SuppressWarnings("deprecation") 358 final Logger global = Logger.global; 359 360 // Make sure the global logger will be registered in the 361 // global manager 362 owner.addLogger(global); 363 return null; 364 } 365 }); 366 } finally { 367 initializationDone = true; 368 } 369 } 370 } 371 372 /** 373 * Returns the global LogManager object. 374 * @return the global LogManager object 375 */ getLogManager()376 public static LogManager getLogManager() { 377 if (manager != null) { 378 manager.ensureLogManagerInitialized(); 379 } 380 return manager; 381 } 382 readPrimordialConfiguration()383 private void readPrimordialConfiguration() { 384 if (!readPrimordialConfiguration) { 385 synchronized (this) { 386 if (!readPrimordialConfiguration) { 387 // If System.in/out/err are null, it's a good 388 // indication that we're still in the 389 // bootstrapping phase 390 if (System.out == null) { 391 return; 392 } 393 readPrimordialConfiguration = true; 394 395 try { 396 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 397 @Override 398 public Void run() throws Exception { 399 readConfiguration(); 400 401 // Platform loggers begin to delegate to java.util.logging.Logger 402 sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 403 return null; 404 } 405 }); 406 } catch (Exception ex) { 407 assert false : "Exception raised while reading logging configuration: " + ex; 408 } 409 } 410 } 411 } 412 } 413 414 /** 415 * Adds an event listener to be invoked when the logging 416 * properties are re-read. Adding multiple instances of 417 * the same event Listener results in multiple entries 418 * in the property event listener table. 419 * 420 * <p><b>WARNING:</b> This method is omitted from this class in all subset 421 * Profiles of Java SE that do not include the {@code java.beans} package. 422 * </p> 423 * 424 * @param l event listener 425 * @exception SecurityException if a security manager exists and if 426 * the caller does not have LoggingPermission("control"). 427 * @exception NullPointerException if the PropertyChangeListener is null. 428 * @deprecated The dependency on {@code PropertyChangeListener} creates a 429 * significant impediment to future modularization of the Java 430 * platform. This method will be removed in a future release. 431 * The global {@code LogManager} can detect changes to the 432 * logging configuration by overridding the {@link 433 * #readConfiguration readConfiguration} method. 434 */ 435 @Deprecated addPropertyChangeListener(PropertyChangeListener l)436 public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException { 437 PropertyChangeListener listener = Objects.requireNonNull(l); 438 checkPermission(); 439 synchronized (listenerMap) { 440 // increment the registration count if already registered 441 Integer value = listenerMap.get(listener); 442 value = (value == null) ? 1 : (value + 1); 443 listenerMap.put(listener, value); 444 } 445 } 446 447 /** 448 * Removes an event listener for property change events. 449 * If the same listener instance has been added to the listener table 450 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>, 451 * then an equivalent number of 452 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove 453 * all instances of that listener from the listener table. 454 * <P> 455 * Returns silently if the given listener is not found. 456 * 457 * <p><b>WARNING:</b> This method is omitted from this class in all subset 458 * Profiles of Java SE that do not include the {@code java.beans} package. 459 * </p> 460 * 461 * @param l event listener (can be null) 462 * @exception SecurityException if a security manager exists and if 463 * the caller does not have LoggingPermission("control"). 464 * @deprecated The dependency on {@code PropertyChangeListener} creates a 465 * significant impediment to future modularization of the Java 466 * platform. This method will be removed in a future release. 467 * The global {@code LogManager} can detect changes to the 468 * logging configuration by overridding the {@link 469 * #readConfiguration readConfiguration} method. 470 */ 471 @Deprecated removePropertyChangeListener(PropertyChangeListener l)472 public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException { 473 checkPermission(); 474 if (l != null) { 475 PropertyChangeListener listener = l; 476 synchronized (listenerMap) { 477 Integer value = listenerMap.get(listener); 478 if (value != null) { 479 // remove from map if registration count is 1, otherwise 480 // just decrement its count 481 int i = value.intValue(); 482 if (i == 1) { 483 listenerMap.remove(listener); 484 } else { 485 assert i > 1; 486 listenerMap.put(listener, i - 1); 487 } 488 } 489 } 490 } 491 } 492 493 // LoggerContext maps from AppContext 494 private WeakHashMap<Object, LoggerContext> contextsMap = null; 495 496 // Returns the LoggerContext for the user code (i.e. application or AppContext). 497 // Loggers are isolated from each AppContext. getUserContext()498 private LoggerContext getUserContext() { 499 LoggerContext context = null; 500 501 SecurityManager sm = System.getSecurityManager(); 502 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess(); 503 if (sm != null && javaAwtAccess != null) { 504 // for each applet, it has its own LoggerContext isolated from others 505 final Object ecx = javaAwtAccess.getAppletContext(); 506 if (ecx != null) { 507 synchronized (javaAwtAccess) { 508 // find the AppContext of the applet code 509 // will be null if we are in the main app context. 510 if (contextsMap == null) { 511 contextsMap = new WeakHashMap<>(); 512 } 513 context = contextsMap.get(ecx); 514 if (context == null) { 515 // Create a new LoggerContext for the applet. 516 context = new LoggerContext(); 517 contextsMap.put(ecx, context); 518 } 519 } 520 } 521 } 522 // for standalone app, return userContext 523 return context != null ? context : userContext; 524 } 525 526 // The system context. getSystemContext()527 final LoggerContext getSystemContext() { 528 return systemContext; 529 } 530 contexts()531 private List<LoggerContext> contexts() { 532 List<LoggerContext> cxs = new ArrayList<>(); 533 cxs.add(getSystemContext()); 534 cxs.add(getUserContext()); 535 return cxs; 536 } 537 538 // Find or create a specified logger instance. If a logger has 539 // already been created with the given name it is returned. 540 // Otherwise a new logger instance is created and registered 541 // in the LogManager global namespace. 542 // This method will always return a non-null Logger object. 543 // Synchronization is not required here. All synchronization for 544 // adding a new Logger object is handled by addLogger(). 545 // 546 // This method must delegate to the LogManager implementation to 547 // add a new Logger or return the one that has been added previously 548 // as a LogManager subclass may override the addLogger, getLogger, 549 // readConfiguration, and other methods. demandLogger(String name, String resourceBundleName, Class<?> caller)550 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 551 Logger result = getLogger(name); 552 if (result == null) { 553 // only allocate the new logger once 554 Logger newLogger = new Logger(name, resourceBundleName, caller, this, false); 555 do { 556 if (addLogger(newLogger)) { 557 // We successfully added the new Logger that we 558 // created above so return it without refetching. 559 return newLogger; 560 } 561 562 // We didn't add the new Logger that we created above 563 // because another thread added a Logger with the same 564 // name after our null check above and before our call 565 // to addLogger(). We have to refetch the Logger because 566 // addLogger() returns a boolean instead of the Logger 567 // reference itself. However, if the thread that created 568 // the other Logger is not holding a strong reference to 569 // the other Logger, then it is possible for the other 570 // Logger to be GC'ed after we saw it in addLogger() and 571 // before we can refetch it. If it has been GC'ed then 572 // we'll just loop around and try again. 573 result = getLogger(name); 574 } while (result == null); 575 } 576 return result; 577 } 578 demandSystemLogger(String name, String resourceBundleName)579 Logger demandSystemLogger(String name, String resourceBundleName) { 580 // Add a system logger in the system context's namespace 581 final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName); 582 583 // Add the system logger to the LogManager's namespace if not exist 584 // so that there is only one single logger of the given name. 585 // System loggers are visible to applications unless a logger of 586 // the same name has been added. 587 Logger logger; 588 do { 589 // First attempt to call addLogger instead of getLogger 590 // This would avoid potential bug in custom LogManager.getLogger 591 // implementation that adds a logger if does not exist 592 if (addLogger(sysLogger)) { 593 // successfully added the new system logger 594 logger = sysLogger; 595 } else { 596 logger = getLogger(name); 597 } 598 } while (logger == null); 599 600 // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 601 if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) { 602 // if logger already exists but handlers not set 603 final Logger l = logger; 604 AccessController.doPrivileged(new PrivilegedAction<Void>() { 605 @Override 606 public Void run() { 607 for (Handler hdl : l.accessCheckedHandlers()) { 608 sysLogger.addHandler(hdl); 609 } 610 return null; 611 } 612 }); 613 } 614 return sysLogger; 615 } 616 617 // LoggerContext maintains the logger namespace per context. 618 // The default LogManager implementation has one system context and user 619 // context. The system context is used to maintain the namespace for 620 // all system loggers and is queried by the system code. If a system logger 621 // doesn't exist in the user context, it'll also be added to the user context. 622 // The user context is queried by the user code and all other loggers are 623 // added in the user context. 624 class LoggerContext { 625 // Table of named Loggers that maps names to Loggers. 626 private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); 627 // Tree of named Loggers 628 private final LogNode root; LoggerContext()629 private LoggerContext() { 630 this.root = new LogNode(null, this); 631 } 632 633 634 // Tells whether default loggers are required in this context. 635 // If true, the default loggers will be lazily added. requiresDefaultLoggers()636 final boolean requiresDefaultLoggers() { 637 final boolean requiresDefaultLoggers = (getOwner() == manager); 638 if (requiresDefaultLoggers) { 639 getOwner().ensureLogManagerInitialized(); 640 } 641 return requiresDefaultLoggers; 642 } 643 644 // This context's LogManager. getOwner()645 final LogManager getOwner() { 646 return LogManager.this; 647 } 648 649 // This context owner's root logger, which if not null, and if 650 // the context requires default loggers, will be added to the context 651 // logger's tree. getRootLogger()652 final Logger getRootLogger() { 653 return getOwner().rootLogger; 654 } 655 656 // The global logger, which if not null, and if 657 // the context requires default loggers, will be added to the context 658 // logger's tree. getGlobalLogger()659 final Logger getGlobalLogger() { 660 @SuppressWarnings("deprecated") // avoids initialization cycles. 661 final Logger global = Logger.global; 662 return global; 663 } 664 demandLogger(String name, String resourceBundleName)665 Logger demandLogger(String name, String resourceBundleName) { 666 // a LogManager subclass may have its own implementation to add and 667 // get a Logger. So delegate to the LogManager to do the work. 668 final LogManager owner = getOwner(); 669 return owner.demandLogger(name, resourceBundleName, null); 670 } 671 672 673 // Due to subtle deadlock issues getUserContext() no longer 674 // calls addLocalLogger(rootLogger); 675 // Therefore - we need to add the default loggers later on. 676 // Checks that the context is properly initialized 677 // This is necessary before calling e.g. find(name) 678 // or getLoggerNames() 679 // ensureInitialized()680 private void ensureInitialized() { 681 if (requiresDefaultLoggers()) { 682 // Ensure that the root and global loggers are set. 683 ensureDefaultLogger(getRootLogger()); 684 ensureDefaultLogger(getGlobalLogger()); 685 } 686 } 687 688 findLogger(String name)689 synchronized Logger findLogger(String name) { 690 // ensure that this context is properly initialized before 691 // looking for loggers. 692 ensureInitialized(); 693 LoggerWeakRef ref = namedLoggers.get(name); 694 if (ref == null) { 695 return null; 696 } 697 Logger logger = ref.get(); 698 if (logger == null) { 699 // Hashtable holds stale weak reference 700 // to a logger which has been GC-ed. 701 ref.dispose(); 702 } 703 return logger; 704 } 705 706 // This method is called before adding a logger to the 707 // context. 708 // 'logger' is the context that will be added. 709 // This method will ensure that the defaults loggers are added 710 // before adding 'logger'. 711 // ensureAllDefaultLoggers(Logger logger)712 private void ensureAllDefaultLoggers(Logger logger) { 713 if (requiresDefaultLoggers()) { 714 final String name = logger.getName(); 715 if (!name.isEmpty()) { 716 ensureDefaultLogger(getRootLogger()); 717 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { 718 ensureDefaultLogger(getGlobalLogger()); 719 } 720 } 721 } 722 } 723 ensureDefaultLogger(Logger logger)724 private void ensureDefaultLogger(Logger logger) { 725 // Used for lazy addition of root logger and global logger 726 // to a LoggerContext. 727 728 // This check is simple sanity: we do not want that this 729 // method be called for anything else than Logger.global 730 // or owner.rootLogger. 731 if (!requiresDefaultLoggers() || logger == null 732 || logger != Logger.global && logger != LogManager.this.rootLogger) { 733 734 // the case where we have a non null logger which is neither 735 // Logger.global nor manager.rootLogger indicates a serious 736 // issue - as ensureDefaultLogger should never be called 737 // with any other loggers than one of these two (or null - if 738 // e.g manager.rootLogger is not yet initialized)... 739 assert logger == null; 740 741 return; 742 } 743 744 // Adds the logger if it's not already there. 745 if (!namedLoggers.containsKey(logger.getName())) { 746 // It is important to prevent addLocalLogger to 747 // call ensureAllDefaultLoggers when we're in the process 748 // off adding one of those default loggers - as this would 749 // immediately cause a stack overflow. 750 // Therefore we must pass addDefaultLoggersIfNeeded=false, 751 // even if requiresDefaultLoggers is true. 752 addLocalLogger(logger, false); 753 } 754 } 755 addLocalLogger(Logger logger)756 boolean addLocalLogger(Logger logger) { 757 // no need to add default loggers if it's not required 758 return addLocalLogger(logger, requiresDefaultLoggers()); 759 } 760 761 // Add a logger to this context. This method will only set its level 762 // and process parent loggers. It doesn't set its handlers. addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded)763 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { 764 // addDefaultLoggersIfNeeded serves to break recursion when adding 765 // default loggers. If we're adding one of the default loggers 766 // (we're being called from ensureDefaultLogger()) then 767 // addDefaultLoggersIfNeeded will be false: we don't want to 768 // call ensureAllDefaultLoggers again. 769 // 770 // Note: addDefaultLoggersIfNeeded can also be false when 771 // requiresDefaultLoggers is false - since calling 772 // ensureAllDefaultLoggers would have no effect in this case. 773 if (addDefaultLoggersIfNeeded) { 774 ensureAllDefaultLoggers(logger); 775 } 776 777 final String name = logger.getName(); 778 if (name == null) { 779 throw new NullPointerException(); 780 } 781 LoggerWeakRef ref = namedLoggers.get(name); 782 if (ref != null) { 783 if (ref.get() == null) { 784 // It's possible that the Logger was GC'ed after a 785 // drainLoggerRefQueueBounded() call above so allow 786 // a new one to be registered. 787 ref.dispose(); 788 } else { 789 // We already have a registered logger with the given name. 790 return false; 791 } 792 } 793 794 // We're adding a new logger. 795 // Note that we are creating a weak reference here. 796 final LogManager owner = getOwner(); 797 logger.setLogManager(owner); 798 ref = owner.new LoggerWeakRef(logger); 799 namedLoggers.put(name, ref); 800 801 // Apply any initial level defined for the new logger, unless 802 // the logger's level is already initialized 803 Level level = owner.getLevelProperty(name + ".level", null); 804 if (level != null && !logger.isLevelInitialized()) { 805 doSetLevel(logger, level); 806 } 807 808 // instantiation of the handler is done in the LogManager.addLogger 809 // implementation as a handler class may be only visible to LogManager 810 // subclass for the custom log manager case 811 processParentHandlers(logger, name); 812 813 // Find the new node and its parent. 814 LogNode node = getNode(name); 815 node.loggerRef = ref; 816 Logger parent = null; 817 LogNode nodep = node.parent; 818 while (nodep != null) { 819 LoggerWeakRef nodeRef = nodep.loggerRef; 820 if (nodeRef != null) { 821 parent = nodeRef.get(); 822 if (parent != null) { 823 break; 824 } 825 } 826 nodep = nodep.parent; 827 } 828 829 if (parent != null) { 830 doSetParent(logger, parent); 831 } 832 // Walk over the children and tell them we are their new parent. 833 node.walkAndSetParent(logger); 834 // new LogNode is ready so tell the LoggerWeakRef about it 835 ref.setNode(node); 836 return true; 837 } 838 removeLoggerRef(String name, LoggerWeakRef ref)839 synchronized void removeLoggerRef(String name, LoggerWeakRef ref) { 840 namedLoggers.remove(name, ref); 841 } 842 getLoggerNames()843 synchronized Enumeration<String> getLoggerNames() { 844 // ensure that this context is properly initialized before 845 // returning logger names. 846 ensureInitialized(); 847 return namedLoggers.keys(); 848 } 849 850 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 851 // parents have levels or handlers defined, make sure they are instantiated. processParentHandlers(final Logger logger, final String name)852 private void processParentHandlers(final Logger logger, final String name) { 853 final LogManager owner = getOwner(); 854 AccessController.doPrivileged(new PrivilegedAction<Void>() { 855 @Override 856 public Void run() { 857 if (logger != owner.rootLogger) { 858 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); 859 if (!useParent) { 860 logger.setUseParentHandlers(false); 861 } 862 } 863 return null; 864 } 865 }); 866 867 int ix = 1; 868 for (;;) { 869 int ix2 = name.indexOf(".", ix); 870 if (ix2 < 0) { 871 break; 872 } 873 String pname = name.substring(0, ix2); 874 if (owner.getProperty(pname + ".level") != null || 875 owner.getProperty(pname + ".handlers") != null) { 876 // This pname has a level/handlers definition. 877 // Make sure it exists. 878 demandLogger(pname, null); 879 } 880 ix = ix2+1; 881 } 882 } 883 884 // Gets a node in our tree of logger nodes. 885 // If necessary, create it. getNode(String name)886 LogNode getNode(String name) { 887 if (name == null || name.equals("")) { 888 return root; 889 } 890 LogNode node = root; 891 while (name.length() > 0) { 892 int ix = name.indexOf("."); 893 String head; 894 if (ix > 0) { 895 head = name.substring(0, ix); 896 name = name.substring(ix + 1); 897 } else { 898 head = name; 899 name = ""; 900 } 901 if (node.children == null) { 902 node.children = new HashMap<>(); 903 } 904 LogNode child = node.children.get(head); 905 if (child == null) { 906 child = new LogNode(node, this); 907 node.children.put(head, child); 908 } 909 node = child; 910 } 911 return node; 912 } 913 } 914 915 final class SystemLoggerContext extends LoggerContext { 916 // Add a system logger in the system context's namespace as well as 917 // in the LogManager's namespace if not exist so that there is only 918 // one single logger of the given name. System loggers are visible 919 // to applications unless a logger of the same name has been added. 920 @Override demandLogger(String name, String resourceBundleName)921 Logger demandLogger(String name, String resourceBundleName) { 922 Logger result = findLogger(name); 923 if (result == null) { 924 // only allocate the new system logger once 925 Logger newLogger = new Logger(name, resourceBundleName, null, getOwner(), true); 926 do { 927 if (addLocalLogger(newLogger)) { 928 // We successfully added the new Logger that we 929 // created above so return it without refetching. 930 result = newLogger; 931 } else { 932 // We didn't add the new Logger that we created above 933 // because another thread added a Logger with the same 934 // name after our null check above and before our call 935 // to addLogger(). We have to refetch the Logger because 936 // addLogger() returns a boolean instead of the Logger 937 // reference itself. However, if the thread that created 938 // the other Logger is not holding a strong reference to 939 // the other Logger, then it is possible for the other 940 // Logger to be GC'ed after we saw it in addLogger() and 941 // before we can refetch it. If it has been GC'ed then 942 // we'll just loop around and try again. 943 result = findLogger(name); 944 } 945 } while (result == null); 946 } 947 return result; 948 } 949 } 950 951 // Add new per logger handlers. 952 // We need to raise privilege here. All our decisions will 953 // be made based on the logging configuration, which can 954 // only be modified by trusted code. loadLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName)955 private void loadLoggerHandlers(final Logger logger, final String name, 956 final String handlersPropertyName) 957 { 958 AccessController.doPrivileged(new PrivilegedAction<Object>() { 959 @Override 960 public Object run() { 961 String names[] = parseClassNames(handlersPropertyName); 962 for (int i = 0; i < names.length; i++) { 963 String word = names[i]; 964 try { 965 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 966 Handler hdl = (Handler) clz.newInstance(); 967 // Check if there is a property defining the 968 // this handler's level. 969 String levs = getProperty(word + ".level"); 970 if (levs != null) { 971 Level l = Level.findLevel(levs); 972 if (l != null) { 973 hdl.setLevel(l); 974 } else { 975 // Probably a bad level. Drop through. 976 System.err.println("Can't set level for " + word); 977 } 978 } 979 // Add this Handler to the logger 980 logger.addHandler(hdl); 981 } catch (Exception ex) { 982 System.err.println("Can't load log handler \"" + word + "\""); 983 System.err.println("" + ex); 984 ex.printStackTrace(); 985 } 986 } 987 return null; 988 } 989 }); 990 } 991 992 993 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 994 // that have been GC'ed. 995 private final ReferenceQueue<Logger> loggerRefQueue 996 = new ReferenceQueue<>(); 997 998 // Package-level inner class. 999 // Helper class for managing WeakReferences to Logger objects. 1000 // 1001 // LogManager.namedLoggers 1002 // - has weak references to all named Loggers 1003 // - namedLoggers keeps the LoggerWeakRef objects for the named 1004 // Loggers around until we can deal with the book keeping for 1005 // the named Logger that is being GC'ed. 1006 // LogManager.LogNode.loggerRef 1007 // - has a weak reference to a named Logger 1008 // - the LogNode will also keep the LoggerWeakRef objects for 1009 // the named Loggers around; currently LogNodes never go away. 1010 // Logger.kids 1011 // - has a weak reference to each direct child Logger; this 1012 // includes anonymous and named Loggers 1013 // - anonymous Loggers are always children of the rootLogger 1014 // which is a strong reference; rootLogger.kids keeps the 1015 // LoggerWeakRef objects for the anonymous Loggers around 1016 // until we can deal with the book keeping. 1017 // 1018 final class LoggerWeakRef extends WeakReference<Logger> { 1019 private String name; // for namedLoggers cleanup 1020 private LogNode node; // for loggerRef cleanup 1021 private WeakReference<Logger> parentRef; // for kids cleanup 1022 private boolean disposed = false; // avoid calling dispose twice 1023 LoggerWeakRef(Logger logger)1024 LoggerWeakRef(Logger logger) { 1025 super(logger, loggerRefQueue); 1026 1027 name = logger.getName(); // save for namedLoggers cleanup 1028 } 1029 1030 // dispose of this LoggerWeakRef object dispose()1031 void dispose() { 1032 // Avoid calling dispose twice. When a Logger is gc'ed, its 1033 // LoggerWeakRef will be enqueued. 1034 // However, a new logger of the same name may be added (or looked 1035 // up) before the queue is drained. When that happens, dispose() 1036 // will be called by addLocalLogger() or findLogger(). 1037 // Later when the queue is drained, dispose() will be called again 1038 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed 1039 // avoids processing the data twice (even though the code should 1040 // now be reentrant). 1041 synchronized(this) { 1042 // Note to maintainers: 1043 // Be careful not to call any method that tries to acquire 1044 // another lock from within this block - as this would surely 1045 // lead to deadlocks, given that dispose() can be called by 1046 // multiple threads, and from within different synchronized 1047 // methods/blocks. 1048 if (disposed) return; 1049 disposed = true; 1050 } 1051 1052 final LogNode n = node; 1053 if (n != null) { 1054 // n.loggerRef can only be safely modified from within 1055 // a lock on LoggerContext. removeLoggerRef is already 1056 // synchronized on LoggerContext so calling 1057 // n.context.removeLoggerRef from within this lock is safe. 1058 synchronized (n.context) { 1059 // if we have a LogNode, then we were a named Logger 1060 // so clear namedLoggers weak ref to us 1061 n.context.removeLoggerRef(name, this); 1062 name = null; // clear our ref to the Logger's name 1063 1064 // LogNode may have been reused - so only clear 1065 // LogNode.loggerRef if LogNode.loggerRef == this 1066 if (n.loggerRef == this) { 1067 n.loggerRef = null; // clear LogNode's weak ref to us 1068 } 1069 node = null; // clear our ref to LogNode 1070 } 1071 } 1072 1073 if (parentRef != null) { 1074 // this LoggerWeakRef has or had a parent Logger 1075 Logger parent = parentRef.get(); 1076 if (parent != null) { 1077 // the parent Logger is still there so clear the 1078 // parent Logger's weak ref to us 1079 parent.removeChildLogger(this); 1080 } 1081 parentRef = null; // clear our weak ref to the parent Logger 1082 } 1083 } 1084 1085 // set the node field to the specified value setNode(LogNode node)1086 void setNode(LogNode node) { 1087 this.node = node; 1088 } 1089 1090 // set the parentRef field to the specified value setParentRef(WeakReference<Logger> parentRef)1091 void setParentRef(WeakReference<Logger> parentRef) { 1092 this.parentRef = parentRef; 1093 } 1094 } 1095 1096 // Package-level method. 1097 // Drain some Logger objects that have been GC'ed. 1098 // 1099 // drainLoggerRefQueueBounded() is called by addLogger() below 1100 // and by Logger.getAnonymousLogger(String) so we'll drain up to 1101 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 1102 // 1103 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 1104 // us about a 50/50 mix in increased weak ref counts versus 1105 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 1106 // Here are stats for cleaning up sets of 400 anonymous Loggers: 1107 // - test duration 1 minute 1108 // - sample size of 125 sets of 400 1109 // - average: 1.99 ms 1110 // - minimum: 0.57 ms 1111 // - maximum: 25.3 ms 1112 // 1113 // The same config gives us a better decreased weak ref count 1114 // than increased weak ref count in the LoggerWeakRefLeak test. 1115 // Here are stats for cleaning up sets of 400 named Loggers: 1116 // - test duration 2 minutes 1117 // - sample size of 506 sets of 400 1118 // - average: 0.57 ms 1119 // - minimum: 0.02 ms 1120 // - maximum: 10.9 ms 1121 // 1122 private final static int MAX_ITERATIONS = 400; drainLoggerRefQueueBounded()1123 final void drainLoggerRefQueueBounded() { 1124 for (int i = 0; i < MAX_ITERATIONS; i++) { 1125 if (loggerRefQueue == null) { 1126 // haven't finished loading LogManager yet 1127 break; 1128 } 1129 1130 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 1131 if (ref == null) { 1132 break; 1133 } 1134 // a Logger object has been GC'ed so clean it up 1135 ref.dispose(); 1136 } 1137 } 1138 1139 /** 1140 * Add a named logger. This does nothing and returns false if a logger 1141 * with the same name is already registered. 1142 * <p> 1143 * The Logger factory methods call this method to register each 1144 * newly created Logger. 1145 * <p> 1146 * The application should retain its own reference to the Logger 1147 * object to avoid it being garbage collected. The LogManager 1148 * may only retain a weak reference. 1149 * 1150 * @param logger the new logger. 1151 * @return true if the argument logger was registered successfully, 1152 * false if a logger of that name already exists. 1153 * @exception NullPointerException if the logger name is null. 1154 */ addLogger(Logger logger)1155 public boolean addLogger(Logger logger) { 1156 final String name = logger.getName(); 1157 if (name == null) { 1158 throw new NullPointerException(); 1159 } 1160 drainLoggerRefQueueBounded(); 1161 LoggerContext cx = getUserContext(); 1162 if (cx.addLocalLogger(logger)) { 1163 // Do we have a per logger handler too? 1164 // Note: this will add a 200ms penalty 1165 loadLoggerHandlers(logger, name, name + ".handlers"); 1166 return true; 1167 } else { 1168 return false; 1169 } 1170 } 1171 1172 // Private method to set a level on a logger. 1173 // If necessary, we raise privilege before doing the call. doSetLevel(final Logger logger, final Level level)1174 private static void doSetLevel(final Logger logger, final Level level) { 1175 SecurityManager sm = System.getSecurityManager(); 1176 if (sm == null) { 1177 // There is no security manager, so things are easy. 1178 logger.setLevel(level); 1179 return; 1180 } 1181 // There is a security manager. Raise privilege before 1182 // calling setLevel. 1183 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1184 @Override 1185 public Object run() { 1186 logger.setLevel(level); 1187 return null; 1188 }}); 1189 } 1190 1191 // Private method to set a parent on a logger. 1192 // If necessary, we raise privilege before doing the setParent call. doSetParent(final Logger logger, final Logger parent)1193 private static void doSetParent(final Logger logger, final Logger parent) { 1194 SecurityManager sm = System.getSecurityManager(); 1195 if (sm == null) { 1196 // There is no security manager, so things are easy. 1197 logger.setParent(parent); 1198 return; 1199 } 1200 // There is a security manager. Raise privilege before 1201 // calling setParent. 1202 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1203 @Override 1204 public Object run() { 1205 logger.setParent(parent); 1206 return null; 1207 }}); 1208 } 1209 1210 /** 1211 * Method to find a named logger. 1212 * <p> 1213 * Note that since untrusted code may create loggers with 1214 * arbitrary names this method should not be relied on to 1215 * find Loggers for security sensitive logging. 1216 * It is also important to note that the Logger associated with the 1217 * String {@code name} may be garbage collected at any time if there 1218 * is no strong reference to the Logger. The caller of this method 1219 * must check the return value for null in order to properly handle 1220 * the case where the Logger has been garbage collected. 1221 * <p> 1222 * @param name name of the logger 1223 * @return matching logger or null if none is found 1224 */ getLogger(String name)1225 public Logger getLogger(String name) { 1226 return getUserContext().findLogger(name); 1227 } 1228 1229 /** 1230 * Get an enumeration of known logger names. 1231 * <p> 1232 * Note: Loggers may be added dynamically as new classes are loaded. 1233 * This method only reports on the loggers that are currently registered. 1234 * It is also important to note that this method only returns the name 1235 * of a Logger, not a strong reference to the Logger itself. 1236 * The returned String does nothing to prevent the Logger from being 1237 * garbage collected. In particular, if the returned name is passed 1238 * to {@code LogManager.getLogger()}, then the caller must check the 1239 * return value from {@code LogManager.getLogger()} for null to properly 1240 * handle the case where the Logger has been garbage collected in the 1241 * time since its name was returned by this method. 1242 * <p> 1243 * @return enumeration of logger name strings 1244 */ getLoggerNames()1245 public Enumeration<String> getLoggerNames() { 1246 return getUserContext().getLoggerNames(); 1247 } 1248 1249 /** 1250 * Reinitialize the logging properties and reread the logging configuration. 1251 * <p> 1252 * The same rules are used for locating the configuration properties 1253 * as are used at startup. So normally the logging properties will 1254 * be re-read from the same file that was used at startup. 1255 * <P> 1256 * Any log level definitions in the new configuration file will be 1257 * applied using Logger.setLevel(), if the target Logger exists. 1258 * <p> 1259 * A PropertyChangeEvent will be fired after the properties are read. 1260 * 1261 * @exception SecurityException if a security manager exists and if 1262 * the caller does not have LoggingPermission("control"). 1263 * @exception IOException if there are IO problems reading the configuration. 1264 */ readConfiguration()1265 public void readConfiguration() throws IOException, SecurityException { 1266 checkPermission(); 1267 1268 // if a configuration class is specified, load it and use it. 1269 String cname = System.getProperty("java.util.logging.config.class"); 1270 if (cname != null) { 1271 try { 1272 // Instantiate the named class. It is its constructor's 1273 // responsibility to initialize the logging configuration, by 1274 // calling readConfiguration(InputStream) with a suitable stream. 1275 try { 1276 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 1277 clz.newInstance(); 1278 return; 1279 } catch (ClassNotFoundException ex) { 1280 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 1281 clz.newInstance(); 1282 return; 1283 } 1284 } catch (Exception ex) { 1285 System.err.println("Logging configuration class \"" + cname + "\" failed"); 1286 System.err.println("" + ex); 1287 // keep going and useful config file. 1288 } 1289 } 1290 1291 String fname = System.getProperty("java.util.logging.config.file"); 1292 if (fname == null) { 1293 fname = System.getProperty("java.home"); 1294 if (fname == null) { 1295 throw new Error("Can't find java.home ??"); 1296 } 1297 File f = new File(fname, "lib"); 1298 f = new File(f, "logging.properties"); 1299 fname = f.getCanonicalPath(); 1300 } 1301 try (final InputStream in = new FileInputStream(fname)) { 1302 final BufferedInputStream bin = new BufferedInputStream(in); 1303 readConfiguration(bin); 1304 } 1305 } 1306 1307 /** 1308 * Reset the logging configuration. 1309 * <p> 1310 * For all named loggers, the reset operation removes and closes 1311 * all Handlers and (except for the root logger) sets the level 1312 * to null. The root logger's level is set to Level.INFO. 1313 * 1314 * @exception SecurityException if a security manager exists and if 1315 * the caller does not have LoggingPermission("control"). 1316 */ 1317 reset()1318 public void reset() throws SecurityException { 1319 checkPermission(); 1320 synchronized (this) { 1321 props = new Properties(); 1322 // Since we are doing a reset we no longer want to initialize 1323 // the global handlers, if they haven't been initialized yet. 1324 initializedGlobalHandlers = true; 1325 } 1326 for (LoggerContext cx : contexts()) { 1327 Enumeration<String> enum_ = cx.getLoggerNames(); 1328 while (enum_.hasMoreElements()) { 1329 String name = enum_.nextElement(); 1330 Logger logger = cx.findLogger(name); 1331 if (logger != null) { 1332 resetLogger(logger); 1333 } 1334 } 1335 } 1336 } 1337 1338 // Private method to reset an individual target logger. resetLogger(Logger logger)1339 private void resetLogger(Logger logger) { 1340 // Close all the Logger's handlers. 1341 Handler[] targets = logger.getHandlers(); 1342 for (int i = 0; i < targets.length; i++) { 1343 Handler h = targets[i]; 1344 logger.removeHandler(h); 1345 try { 1346 h.close(); 1347 } catch (Exception ex) { 1348 // Problems closing a handler? Keep going... 1349 } 1350 } 1351 String name = logger.getName(); 1352 if (name != null && name.equals("")) { 1353 // This is the root logger. 1354 logger.setLevel(defaultLevel); 1355 } else { 1356 logger.setLevel(null); 1357 } 1358 } 1359 1360 // get a list of whitespace separated classnames from a property. parseClassNames(String propertyName)1361 private String[] parseClassNames(String propertyName) { 1362 String hands = getProperty(propertyName); 1363 if (hands == null) { 1364 return new String[0]; 1365 } 1366 hands = hands.trim(); 1367 int ix = 0; 1368 final List<String> result = new ArrayList<>(); 1369 while (ix < hands.length()) { 1370 int end = ix; 1371 while (end < hands.length()) { 1372 if (Character.isWhitespace(hands.charAt(end))) { 1373 break; 1374 } 1375 if (hands.charAt(end) == ',') { 1376 break; 1377 } 1378 end++; 1379 } 1380 String word = hands.substring(ix, end); 1381 ix = end+1; 1382 word = word.trim(); 1383 if (word.length() == 0) { 1384 continue; 1385 } 1386 result.add(word); 1387 } 1388 return result.toArray(new String[result.size()]); 1389 } 1390 1391 /** 1392 * Reinitialize the logging properties and reread the logging configuration 1393 * from the given stream, which should be in java.util.Properties format. 1394 * A PropertyChangeEvent will be fired after the properties are read. 1395 * <p> 1396 * Any log level definitions in the new configuration file will be 1397 * applied using Logger.setLevel(), if the target Logger exists. 1398 * 1399 * @param ins stream to read properties from 1400 * @exception SecurityException if a security manager exists and if 1401 * the caller does not have LoggingPermission("control"). 1402 * @exception IOException if there are problems reading from the stream. 1403 */ readConfiguration(InputStream ins)1404 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 1405 checkPermission(); 1406 reset(); 1407 1408 // Load the properties 1409 props.load(ins); 1410 // Instantiate new configuration objects. 1411 String names[] = parseClassNames("config"); 1412 1413 for (int i = 0; i < names.length; i++) { 1414 String word = names[i]; 1415 try { 1416 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 1417 clz.newInstance(); 1418 } catch (Exception ex) { 1419 System.err.println("Can't load config class \"" + word + "\""); 1420 System.err.println("" + ex); 1421 // ex.printStackTrace(); 1422 } 1423 } 1424 1425 // Set levels on any pre-existing loggers, based on the new properties. 1426 setLevelsOnExistingLoggers(); 1427 1428 // Notify any interested parties that our properties have changed. 1429 // We first take a copy of the listener map so that we aren't holding any 1430 // locks when calling the listeners. 1431 Map<Object,Integer> listeners = null; 1432 synchronized (listenerMap) { 1433 if (!listenerMap.isEmpty()) 1434 listeners = new HashMap<>(listenerMap); 1435 } 1436 if (listeners != null) { 1437 assert Beans.isBeansPresent(); 1438 Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null); 1439 for (Map.Entry<Object,Integer> entry : listeners.entrySet()) { 1440 Object listener = entry.getKey(); 1441 int count = entry.getValue().intValue(); 1442 for (int i = 0; i < count; i++) { 1443 Beans.invokePropertyChange(listener, ev); 1444 } 1445 } 1446 } 1447 1448 1449 // Note that we need to reinitialize global handles when 1450 // they are first referenced. 1451 synchronized (this) { 1452 initializedGlobalHandlers = false; 1453 } 1454 } 1455 1456 /** 1457 * Get the value of a logging property. 1458 * The method returns null if the property is not found. 1459 * @param name property name 1460 * @return property value 1461 */ getProperty(String name)1462 public String getProperty(String name) { 1463 return props.getProperty(name); 1464 } 1465 1466 // Package private method to get a String property. 1467 // If the property is not defined we return the given 1468 // default value. getStringProperty(String name, String defaultValue)1469 String getStringProperty(String name, String defaultValue) { 1470 String val = getProperty(name); 1471 if (val == null) { 1472 return defaultValue; 1473 } 1474 return val.trim(); 1475 } 1476 1477 // Package private method to get an integer property. 1478 // If the property is not defined or cannot be parsed 1479 // we return the given default value. getIntProperty(String name, int defaultValue)1480 int getIntProperty(String name, int defaultValue) { 1481 String val = getProperty(name); 1482 if (val == null) { 1483 return defaultValue; 1484 } 1485 try { 1486 return Integer.parseInt(val.trim()); 1487 } catch (Exception ex) { 1488 return defaultValue; 1489 } 1490 } 1491 1492 // Package private method to get a boolean property. 1493 // If the property is not defined or cannot be parsed 1494 // we return the given default value. getBooleanProperty(String name, boolean defaultValue)1495 boolean getBooleanProperty(String name, boolean defaultValue) { 1496 String val = getProperty(name); 1497 if (val == null) { 1498 return defaultValue; 1499 } 1500 val = val.toLowerCase(); 1501 if (val.equals("true") || val.equals("1")) { 1502 return true; 1503 } else if (val.equals("false") || val.equals("0")) { 1504 return false; 1505 } 1506 return defaultValue; 1507 } 1508 1509 // Package private method to get a Level property. 1510 // If the property is not defined or cannot be parsed 1511 // we return the given default value. getLevelProperty(String name, Level defaultValue)1512 Level getLevelProperty(String name, Level defaultValue) { 1513 String val = getProperty(name); 1514 if (val == null) { 1515 return defaultValue; 1516 } 1517 Level l = Level.findLevel(val.trim()); 1518 return l != null ? l : defaultValue; 1519 } 1520 1521 // Package private method to get a filter property. 1522 // We return an instance of the class named by the "name" 1523 // property. If the property is not defined or has problems 1524 // we return the defaultValue. getFilterProperty(String name, Filter defaultValue)1525 Filter getFilterProperty(String name, Filter defaultValue) { 1526 String val = getProperty(name); 1527 try { 1528 if (val != null) { 1529 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1530 return (Filter) clz.newInstance(); 1531 } 1532 } catch (Exception ex) { 1533 // We got one of a variety of exceptions in creating the 1534 // class or creating an instance. 1535 // Drop through. 1536 } 1537 // We got an exception. Return the defaultValue. 1538 return defaultValue; 1539 } 1540 1541 1542 // Package private method to get a formatter property. 1543 // We return an instance of the class named by the "name" 1544 // property. If the property is not defined or has problems 1545 // we return the defaultValue. getFormatterProperty(String name, Formatter defaultValue)1546 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1547 String val = getProperty(name); 1548 try { 1549 if (val != null) { 1550 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1551 return (Formatter) clz.newInstance(); 1552 } 1553 } catch (Exception ex) { 1554 // We got one of a variety of exceptions in creating the 1555 // class or creating an instance. 1556 // Drop through. 1557 } 1558 // We got an exception. Return the defaultValue. 1559 return defaultValue; 1560 } 1561 1562 // Private method to load the global handlers. 1563 // We do the real work lazily, when the global handlers 1564 // are first used. initializeGlobalHandlers()1565 private synchronized void initializeGlobalHandlers() { 1566 if (initializedGlobalHandlers) { 1567 return; 1568 } 1569 1570 initializedGlobalHandlers = true; 1571 1572 if (deathImminent) { 1573 // Aaargh... 1574 // The VM is shutting down and our exit hook has been called. 1575 // Avoid allocating global handlers. 1576 return; 1577 } 1578 loadLoggerHandlers(rootLogger, null, "handlers"); 1579 } 1580 1581 private final Permission controlPermission = new LoggingPermission("control", null); 1582 checkPermission()1583 void checkPermission() { 1584 SecurityManager sm = System.getSecurityManager(); 1585 if (sm != null) 1586 sm.checkPermission(controlPermission); 1587 } 1588 1589 /** 1590 * Check that the current context is trusted to modify the logging 1591 * configuration. This requires LoggingPermission("control"). 1592 * <p> 1593 * If the check fails we throw a SecurityException, otherwise 1594 * we return normally. 1595 * 1596 * @exception SecurityException if a security manager exists and if 1597 * the caller does not have LoggingPermission("control"). 1598 */ checkAccess()1599 public void checkAccess() throws SecurityException { 1600 checkPermission(); 1601 } 1602 1603 // Nested class to represent a node in our tree of named loggers. 1604 private static class LogNode { 1605 HashMap<String,LogNode> children; 1606 LoggerWeakRef loggerRef; 1607 LogNode parent; 1608 final LoggerContext context; 1609 LogNode(LogNode parent, LoggerContext context)1610 LogNode(LogNode parent, LoggerContext context) { 1611 this.parent = parent; 1612 this.context = context; 1613 } 1614 1615 // Recursive method to walk the tree below a node and set 1616 // a new parent logger. walkAndSetParent(Logger parent)1617 void walkAndSetParent(Logger parent) { 1618 if (children == null) { 1619 return; 1620 } 1621 Iterator<LogNode> values = children.values().iterator(); 1622 while (values.hasNext()) { 1623 LogNode node = values.next(); 1624 LoggerWeakRef ref = node.loggerRef; 1625 Logger logger = (ref == null) ? null : ref.get(); 1626 if (logger == null) { 1627 node.walkAndSetParent(parent); 1628 } else { 1629 doSetParent(logger, parent); 1630 } 1631 } 1632 } 1633 } 1634 1635 // We use a subclass of Logger for the root logger, so 1636 // that we only instantiate the global handlers when they 1637 // are first needed. 1638 private final class RootLogger extends Logger { RootLogger()1639 private RootLogger() { 1640 // We do not call the protected Logger two args constructor here, 1641 // to avoid calling LogManager.getLogManager() from within the 1642 // RootLogger constructor. 1643 super("", null, null, LogManager.this, true); 1644 } 1645 1646 @Override log(LogRecord record)1647 public void log(LogRecord record) { 1648 // Make sure that the global handlers have been instantiated. 1649 initializeGlobalHandlers(); 1650 super.log(record); 1651 } 1652 1653 @Override addHandler(Handler h)1654 public void addHandler(Handler h) { 1655 initializeGlobalHandlers(); 1656 super.addHandler(h); 1657 } 1658 1659 @Override removeHandler(Handler h)1660 public void removeHandler(Handler h) { 1661 initializeGlobalHandlers(); 1662 super.removeHandler(h); 1663 } 1664 1665 @Override accessCheckedHandlers()1666 Handler[] accessCheckedHandlers() { 1667 initializeGlobalHandlers(); 1668 return super.accessCheckedHandlers(); 1669 } 1670 } 1671 1672 1673 // Private method to be called when the configuration has 1674 // changed to apply any level settings to any pre-existing loggers. setLevelsOnExistingLoggers()1675 synchronized private void setLevelsOnExistingLoggers() { 1676 Enumeration<?> enum_ = props.propertyNames(); 1677 while (enum_.hasMoreElements()) { 1678 String key = (String)enum_.nextElement(); 1679 if (!key.endsWith(".level")) { 1680 // Not a level definition. 1681 continue; 1682 } 1683 int ix = key.length() - 6; 1684 String name = key.substring(0, ix); 1685 Level level = getLevelProperty(key, null); 1686 if (level == null) { 1687 System.err.println("Bad level value for property: " + key); 1688 continue; 1689 } 1690 for (LoggerContext cx : contexts()) { 1691 Logger l = cx.findLogger(name); 1692 if (l == null) { 1693 continue; 1694 } 1695 l.setLevel(level); 1696 } 1697 } 1698 } 1699 1700 // Management Support 1701 private static LoggingMXBean loggingMXBean = null; 1702 /** 1703 * String representation of the 1704 * {@link javax.management.ObjectName} for the management interface 1705 * for the logging facility. 1706 * 1707 * @see java.lang.management.PlatformLoggingMXBean 1708 * @see java.util.logging.LoggingMXBean 1709 * 1710 * @since 1.5 1711 */ 1712 public final static String LOGGING_MXBEAN_NAME 1713 = "java.util.logging:type=Logging"; 1714 1715 /** 1716 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1717 * An alternative way to manage loggers is through the 1718 * {@link java.lang.management.PlatformLoggingMXBean} interface 1719 * that can be obtained by calling: 1720 * <pre> 1721 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 1722 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 1723 * </pre> 1724 * 1725 * @return a {@link LoggingMXBean} object. 1726 * 1727 * @see java.lang.management.PlatformLoggingMXBean 1728 * @since 1.5 1729 */ getLoggingMXBean()1730 public static synchronized LoggingMXBean getLoggingMXBean() { 1731 if (loggingMXBean == null) { 1732 loggingMXBean = new Logging(); 1733 } 1734 return loggingMXBean; 1735 } 1736 1737 /** 1738 * A class that provides access to the java.beans.PropertyChangeListener 1739 * and java.beans.PropertyChangeEvent without creating a static dependency 1740 * on java.beans. This class can be removed once the addPropertyChangeListener 1741 * and removePropertyChangeListener methods are removed. 1742 */ 1743 private static class Beans { 1744 private static final Class<?> propertyChangeListenerClass = 1745 getClass("java.beans.PropertyChangeListener"); 1746 1747 private static final Class<?> propertyChangeEventClass = 1748 getClass("java.beans.PropertyChangeEvent"); 1749 1750 private static final Method propertyChangeMethod = 1751 getMethod(propertyChangeListenerClass, 1752 "propertyChange", 1753 propertyChangeEventClass); 1754 1755 private static final Constructor<?> propertyEventCtor = 1756 getConstructor(propertyChangeEventClass, 1757 Object.class, 1758 String.class, 1759 Object.class, 1760 Object.class); 1761 getClass(String name)1762 private static Class<?> getClass(String name) { 1763 try { 1764 return Class.forName(name, true, Beans.class.getClassLoader()); 1765 } catch (ClassNotFoundException e) { 1766 return null; 1767 } 1768 } getConstructor(Class<?> c, Class<?>... types)1769 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { 1770 try { 1771 return (c == null) ? null : c.getDeclaredConstructor(types); 1772 } catch (NoSuchMethodException x) { 1773 throw new AssertionError(x); 1774 } 1775 } 1776 getMethod(Class<?> c, String name, Class<?>... types)1777 private static Method getMethod(Class<?> c, String name, Class<?>... types) { 1778 try { 1779 return (c == null) ? null : c.getMethod(name, types); 1780 } catch (NoSuchMethodException e) { 1781 throw new AssertionError(e); 1782 } 1783 } 1784 1785 /** 1786 * Returns {@code true} if java.beans is present. 1787 */ isBeansPresent()1788 static boolean isBeansPresent() { 1789 return propertyChangeListenerClass != null && 1790 propertyChangeEventClass != null; 1791 } 1792 1793 /** 1794 * Returns a new PropertyChangeEvent with the given source, property 1795 * name, old and new values. 1796 */ newPropertyChangeEvent(Object source, String prop, Object oldValue, Object newValue)1797 static Object newPropertyChangeEvent(Object source, String prop, 1798 Object oldValue, Object newValue) 1799 { 1800 try { 1801 return propertyEventCtor.newInstance(source, prop, oldValue, newValue); 1802 } catch (InstantiationException | IllegalAccessException x) { 1803 throw new AssertionError(x); 1804 } catch (InvocationTargetException x) { 1805 Throwable cause = x.getCause(); 1806 if (cause instanceof Error) 1807 throw (Error)cause; 1808 if (cause instanceof RuntimeException) 1809 throw (RuntimeException)cause; 1810 throw new AssertionError(x); 1811 } 1812 } 1813 1814 /** 1815 * Invokes the given PropertyChangeListener's propertyChange method 1816 * with the given event. 1817 */ invokePropertyChange(Object listener, Object ev)1818 static void invokePropertyChange(Object listener, Object ev) { 1819 try { 1820 propertyChangeMethod.invoke(listener, ev); 1821 } catch (IllegalAccessException x) { 1822 throw new AssertionError(x); 1823 } catch (InvocationTargetException x) { 1824 Throwable cause = x.getCause(); 1825 if (cause instanceof Error) 1826 throw (Error)cause; 1827 if (cause instanceof RuntimeException) 1828 throw (RuntimeException)cause; 1829 throw new AssertionError(x); 1830 } 1831 } 1832 } 1833 } 1834