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