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