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