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