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