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