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