1 /* 2 * Copyright (c) 2000, 2021, 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 package java.util.logging; 27 28 import java.io.*; 29 import java.util.*; 30 import java.security.*; 31 import java.lang.ref.ReferenceQueue; 32 import java.lang.ref.WeakReference; 33 import java.util.concurrent.ConcurrentHashMap; 34 import java.nio.file.Paths; 35 import java.util.concurrent.CopyOnWriteArrayList; 36 import java.util.concurrent.locks.ReentrantLock; 37 import java.util.function.BiFunction; 38 import java.util.function.Function; 39 import java.util.function.Predicate; 40 import java.util.stream.Collectors; 41 import java.util.stream.Stream; 42 import jdk.internal.access.JavaAWTAccess; 43 import jdk.internal.access.SharedSecrets; 44 import sun.util.logging.internal.LoggingProviderImpl; 45 import static jdk.internal.logger.DefaultLoggerFinder.isSystem; 46 47 /** 48 * There is a single global LogManager object that is used to 49 * maintain a set of shared state about Loggers and log services. 50 * <p> 51 * This LogManager object: 52 * <ul> 53 * <li> Manages a hierarchical namespace of Logger objects. All 54 * named Loggers are stored in this namespace. 55 * <li> Manages a set of logging control properties. These are 56 * simple key-value pairs that can be used by Handlers and 57 * other logging objects to configure themselves. 58 * </ul> 59 * <p> 60 * The global LogManager object can be retrieved using LogManager.getLogManager(). 61 * The LogManager object is created during class initialization and 62 * cannot subsequently be changed. 63 * <p> 64 * At startup the LogManager class is located using the 65 * java.util.logging.manager system property. 66 * 67 * <h2>LogManager Configuration</h2> 68 * 69 * A LogManager initializes the logging configuration via 70 * the {@link #readConfiguration()} method during LogManager initialization. 71 * By default, LogManager default configuration is used. 72 * The logging configuration read by LogManager must be in the 73 * {@linkplain Properties properties file} format. 74 * <p> 75 * The LogManager defines two optional system properties that allow control over 76 * the initial configuration, as specified in the {@link #readConfiguration()} 77 * method: 78 * <ul> 79 * <li>{@systemProperty java.util.logging.config.class} 80 * <li>{@systemProperty java.util.logging.config.file} 81 * </ul> 82 * <p> 83 * These two system properties may be specified on the command line to the "java" 84 * command, or as system property definitions passed to JNI_CreateJavaVM. 85 * <p> 86 * The {@linkplain Properties properties} for loggers and Handlers will have 87 * names starting with the dot-separated name for the handler or logger.<br> 88 * The global logging properties may include: 89 * <ul> 90 * <li>A property "handlers". This defines a whitespace or comma separated 91 * list of class names for handler classes to load and register as 92 * handlers on the root Logger (the Logger named ""). Each class 93 * name must be for a Handler class which has a default constructor. 94 * Note that these Handlers may be created lazily, when they are 95 * first used. 96 * 97 * <li>A property "<logger>.handlers". This defines a whitespace or 98 * comma separated list of class names for handlers classes to 99 * load and register as handlers to the specified logger. Each class 100 * name must be for a Handler class which has a default constructor. 101 * Note that these Handlers may be created lazily, when they are 102 * first used. 103 * 104 * <li>A property "<logger>.handlers.ensureCloseOnReset". This defines a 105 * a boolean value. If "<logger>.handlers" is not defined or is empty, 106 * this property is ignored. Otherwise it defaults to {@code true}. When the 107 * value is {@code true}, the handlers associated with the logger are guaranteed 108 * to be closed on {@linkplain #reset} and shutdown. This can be turned off 109 * by explicitly setting "<logger>.handlers.ensureCloseOnReset=false" in 110 * the configuration. Note that turning this property off causes the risk of 111 * introducing a resource leak, as the logger may get garbage collected before 112 * {@code reset()} is called, thus preventing its handlers from being closed 113 * on {@code reset()}. In that case it is the responsibility of the application 114 * to ensure that the handlers are closed before the logger is garbage 115 * collected. 116 * 117 * <li>A property "<logger>.useParentHandlers". This defines a boolean 118 * value. By default every logger calls its parent in addition to 119 * handling the logging message itself, this often result in messages 120 * being handled by the root logger as well. When setting this property 121 * to false a Handler needs to be configured for this logger otherwise 122 * no logging messages are delivered. 123 * 124 * <li>A property "config". This property is intended to allow 125 * arbitrary configuration code to be run. The property defines a 126 * whitespace or comma separated list of class names. A new instance will be 127 * created for each named class. The default constructor of each class 128 * may execute arbitrary code to update the logging configuration, such as 129 * setting logger levels, adding handlers, adding filters, etc. 130 * </ul> 131 * <p> 132 * Note that all classes loaded during LogManager configuration are 133 * first searched on the system class path before any user class path. 134 * That includes the LogManager class, any config classes, and any 135 * handler classes. 136 * <p> 137 * Loggers are organized into a naming hierarchy based on their 138 * dot separated names. Thus "a.b.c" is a child of "a.b", but 139 * "a.b1" and a.b2" are peers. 140 * <p> 141 * All properties whose names end with ".level" are assumed to define 142 * log levels for Loggers. Thus "foo.level" defines a log level for 143 * the logger called "foo" and (recursively) for any of its children 144 * in the naming hierarchy. Log Levels are applied in the order they 145 * are defined in the properties file. Thus level settings for child 146 * nodes in the tree should come after settings for their parents. 147 * The property name ".level" can be used to set the level for the 148 * root of the tree. 149 * <p> 150 * All methods on the LogManager object are multi-thread safe. 151 * 152 * @since 1.4 153 */ 154 155 public class LogManager { 156 157 // 'props' is assigned within a lock but accessed without it. 158 // Declaring it volatile makes sure that another thread will not 159 // be able to see a partially constructed 'props' object. 160 // (seeing a partially constructed 'props' object can result in 161 // NPE being thrown in Hashtable.get(), because it leaves the door 162 // open for props.getProperties() to be called before the construcor 163 // of Hashtable is actually completed). 164 private volatile Properties props = new Properties(); 165 private static final Level defaultLevel = Level.INFO; 166 167 // LoggerContext for system loggers and user loggers 168 private final LoggerContext systemContext = new SystemLoggerContext(); 169 private final LoggerContext userContext = new LoggerContext(); 170 // non final field - make it volatile to make sure that other threads 171 // will see the new value once ensureLogManagerInitialized() has finished 172 // executing. 173 private volatile Logger rootLogger; 174 // Have we done the primordial reading of the configuration file? 175 // (Must be done after a suitable amount of java.lang.System 176 // initialization has been done) 177 private volatile boolean readPrimordialConfiguration; 178 // Have we initialized global (root) handlers yet? 179 // This gets set to STATE_UNINITIALIZED in readConfiguration 180 private static final int 181 STATE_INITIALIZED = 0, // initial state 182 STATE_INITIALIZING = 1, 183 STATE_READING_CONFIG = 2, 184 STATE_UNINITIALIZED = 3, 185 STATE_SHUTDOWN = 4; // terminal state 186 private volatile int globalHandlersState; // = STATE_INITIALIZED; 187 // A concurrency lock for reset(), readConfiguration() and Cleaner. 188 private final ReentrantLock configurationLock = new ReentrantLock(); 189 190 // This list contains the loggers for which some handlers have been 191 // explicitly configured in the configuration file. 192 // It prevents these loggers from being arbitrarily garbage collected. 193 private static final class CloseOnReset { 194 private final Logger logger; CloseOnReset(Logger ref)195 private CloseOnReset(Logger ref) { 196 this.logger = Objects.requireNonNull(ref); 197 } 198 @Override equals(Object other)199 public boolean equals(Object other) { 200 return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger; 201 } 202 @Override hashCode()203 public int hashCode() { 204 return System.identityHashCode(logger); 205 } get()206 public Logger get() { 207 return logger; 208 } create(Logger logger)209 public static CloseOnReset create(Logger logger) { 210 return new CloseOnReset(logger); 211 } 212 } 213 private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers = 214 new CopyOnWriteArrayList<>(); 215 216 217 private final Map<Object, Runnable> listeners = 218 Collections.synchronizedMap(new IdentityHashMap<>()); 219 220 // The global LogManager object 221 @SuppressWarnings("removal") 222 private static final LogManager manager = AccessController.doPrivileged( 223 new PrivilegedAction<LogManager>() { 224 @Override 225 public LogManager run() { 226 LogManager mgr = null; 227 String cname = null; 228 try { 229 cname = System.getProperty("java.util.logging.manager"); 230 if (cname != null) { 231 try { 232 @SuppressWarnings("deprecation") 233 Object tmp = ClassLoader.getSystemClassLoader() 234 .loadClass(cname).newInstance(); 235 mgr = (LogManager) tmp; 236 } catch (ClassNotFoundException ex) { 237 @SuppressWarnings("deprecation") 238 Object tmp = Thread.currentThread() 239 .getContextClassLoader().loadClass(cname).newInstance(); 240 mgr = (LogManager) tmp; 241 } 242 } 243 } catch (Exception ex) { 244 System.err.println("Could not load Logmanager \"" + cname + "\""); 245 ex.printStackTrace(); 246 } 247 if (mgr == null) { 248 mgr = new LogManager(); 249 } 250 return mgr; 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 Cleaner()259 private Cleaner() { 260 super(null, null, "Logging-Cleaner", 0, false); 261 /* Set context class loader to null in order to avoid 262 * keeping a strong reference to an application classloader. 263 */ 264 this.setContextClassLoader(null); 265 } 266 267 @Override run()268 public void run() { 269 // This is to ensure the LogManager.<clinit> is completed 270 // before synchronized block. Otherwise deadlocks are possible. 271 LogManager mgr = manager; 272 273 // set globalHandlersState to STATE_SHUTDOWN atomically so that 274 // no attempts are made to (re)initialize the handlers or (re)read 275 // the configuration again. This is terminal state. 276 configurationLock.lock(); 277 globalHandlersState = STATE_SHUTDOWN; 278 configurationLock.unlock(); 279 280 // Do a reset to close all active handlers. 281 reset(); 282 } 283 } 284 285 286 /** 287 * Protected constructor. This is protected so that container applications 288 * (such as J2EE containers) can subclass the object. It is non-public as 289 * it is intended that there only be one LogManager object, whose value is 290 * retrieved by calling LogManager.getLogManager. 291 */ LogManager()292 protected LogManager() { 293 this(checkSubclassPermissions()); 294 } 295 LogManager(Void checked)296 private LogManager(Void checked) { 297 298 // Add a shutdown hook to close the global handlers. 299 try { 300 Runtime.getRuntime().addShutdownHook(new Cleaner()); 301 } catch (IllegalStateException e) { 302 // If the VM is already shutting down, 303 // We do not need to register shutdownHook. 304 } 305 } 306 checkSubclassPermissions()307 private static Void checkSubclassPermissions() { 308 @SuppressWarnings("removal") 309 final SecurityManager sm = System.getSecurityManager(); 310 if (sm != null) { 311 // These permission will be checked in the LogManager constructor, 312 // in order to register the Cleaner() thread as a shutdown hook. 313 // Check them here to avoid the penalty of constructing the object 314 // etc... 315 sm.checkPermission(new RuntimePermission("shutdownHooks")); 316 sm.checkPermission(new RuntimePermission("setContextClassLoader")); 317 } 318 return null; 319 } 320 321 /** 322 * Lazy initialization: if this instance of manager is the global 323 * manager then this method will read the initial configuration and 324 * add the root logger and global logger by calling addLogger(). 325 * 326 * Note that it is subtly different from what we do in LoggerContext. 327 * In LoggerContext we're patching up the logger context tree in order to add 328 * the root and global logger *to the context tree*. 329 * 330 * For this to work, addLogger() must have already have been called 331 * once on the LogManager instance for the default logger being 332 * added. 333 * 334 * This is why ensureLogManagerInitialized() needs to be called before 335 * any logger is added to any logger context. 336 * 337 */ 338 private boolean initializedCalled = false; 339 private volatile boolean initializationDone = false; 340 @SuppressWarnings("removal") ensureLogManagerInitialized()341 final void ensureLogManagerInitialized() { 342 final LogManager owner = this; 343 if (initializationDone || owner != manager) { 344 // we don't want to do this twice, and we don't want to do 345 // this on private manager instances. 346 return; 347 } 348 349 // Maybe another thread has called ensureLogManagerInitialized() 350 // before us and is still executing it. If so we will block until 351 // the log manager has finished initialized, then acquire the monitor, 352 // notice that initializationDone is now true and return. 353 // Otherwise - we have come here first! We will acquire the monitor, 354 // see that initializationDone is still false, and perform the 355 // initialization. 356 // 357 configurationLock.lock(); 358 try { 359 // If initializedCalled is true it means that we're already in 360 // the process of initializing the LogManager in this thread. 361 // There has been a recursive call to ensureLogManagerInitialized(). 362 final boolean isRecursiveInitialization = (initializedCalled == true); 363 364 assert initializedCalled || !initializationDone 365 : "Initialization can't be done if initialized has not been called!"; 366 367 if (isRecursiveInitialization || initializationDone) { 368 // If isRecursiveInitialization is true it means that we're 369 // already in the process of initializing the LogManager in 370 // this thread. There has been a recursive call to 371 // ensureLogManagerInitialized(). We should not proceed as 372 // it would lead to infinite recursion. 373 // 374 // If initializationDone is true then it means the manager 375 // has finished initializing; just return: we're done. 376 return; 377 } 378 // Calling addLogger below will in turn call requiresDefaultLogger() 379 // which will call ensureLogManagerInitialized(). 380 // We use initializedCalled to break the recursion. 381 initializedCalled = true; 382 try { 383 AccessController.doPrivileged(new PrivilegedAction<Object>() { 384 @Override 385 public Object run() { 386 assert rootLogger == null; 387 assert initializedCalled && !initializationDone; 388 389 // create root logger before reading primordial 390 // configuration - to ensure that it will be added 391 // before the global logger, and not after. 392 final Logger root = owner.rootLogger = owner.new RootLogger(); 393 394 // Read configuration. 395 owner.readPrimordialConfiguration(); 396 397 // Create and retain Logger for the root of the namespace. 398 owner.addLogger(root); 399 400 // Initialize level if not yet initialized 401 if (!root.isLevelInitialized()) { 402 root.setLevel(defaultLevel); 403 } 404 405 // Adding the global Logger. 406 // Do not call Logger.getGlobal() here as this might trigger 407 // subtle inter-dependency issues. 408 @SuppressWarnings("deprecation") 409 final Logger global = Logger.global; 410 411 // Make sure the global logger will be registered in the 412 // global manager 413 owner.addLogger(global); 414 return null; 415 } 416 }); 417 } finally { 418 initializationDone = true; 419 } 420 } finally { 421 configurationLock.unlock(); 422 } 423 } 424 425 /** 426 * Returns the global LogManager object. 427 * @return the global LogManager object 428 */ getLogManager()429 public static LogManager getLogManager() { 430 if (manager != null) { 431 manager.ensureLogManagerInitialized(); 432 } 433 return manager; 434 } 435 readPrimordialConfiguration()436 private void readPrimordialConfiguration() { // must be called while holding configurationLock 437 if (!readPrimordialConfiguration) { 438 // If System.in/out/err are null, it's a good 439 // indication that we're still in the 440 // bootstrapping phase 441 if (System.out == null) { 442 return; 443 } 444 readPrimordialConfiguration = true; 445 try { 446 readConfiguration(); 447 448 // Platform loggers begin to delegate to java.util.logging.Logger 449 jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers(); 450 451 } catch (Exception ex) { 452 assert false : "Exception raised while reading logging configuration: " + ex; 453 } 454 } 455 } 456 457 // LoggerContext maps from AppContext 458 private WeakHashMap<Object, LoggerContext> contextsMap = null; 459 460 // Returns the LoggerContext for the user code (i.e. application or AppContext). 461 // Loggers are isolated from each AppContext. getUserContext()462 private LoggerContext getUserContext() { 463 LoggerContext context = null; 464 465 @SuppressWarnings("removal") 466 SecurityManager sm = System.getSecurityManager(); 467 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess(); 468 if (sm != null && javaAwtAccess != null) { 469 // for each applet, it has its own LoggerContext isolated from others 470 final Object ecx = javaAwtAccess.getAppletContext(); 471 if (ecx != null) { 472 synchronized (javaAwtAccess) { 473 // find the AppContext of the applet code 474 // will be null if we are in the main app context. 475 if (contextsMap == null) { 476 contextsMap = new WeakHashMap<>(); 477 } 478 context = contextsMap.get(ecx); 479 if (context == null) { 480 // Create a new LoggerContext for the applet. 481 context = new LoggerContext(); 482 contextsMap.put(ecx, context); 483 } 484 } 485 } 486 } 487 // for standalone app, return userContext 488 return context != null ? context : userContext; 489 } 490 491 // The system context. getSystemContext()492 final LoggerContext getSystemContext() { 493 return systemContext; 494 } 495 contexts()496 private List<LoggerContext> contexts() { 497 List<LoggerContext> cxs = new ArrayList<>(); 498 cxs.add(getSystemContext()); 499 cxs.add(getUserContext()); 500 return cxs; 501 } 502 503 // Find or create a specified logger instance. If a logger has 504 // already been created with the given name it is returned. 505 // Otherwise a new logger instance is created and registered 506 // in the LogManager global namespace. 507 // This method will always return a non-null Logger object. 508 // Synchronization is not required here. All synchronization for 509 // adding a new Logger object is handled by addLogger(). 510 // 511 // This method must delegate to the LogManager implementation to 512 // add a new Logger or return the one that has been added previously 513 // as a LogManager subclass may override the addLogger, getLogger, 514 // readConfiguration, and other methods. demandLogger(String name, String resourceBundleName, Class<?> caller)515 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 516 final Module module = caller == null ? null : caller.getModule(); 517 return demandLogger(name, resourceBundleName, module); 518 } 519 demandLogger(String name, String resourceBundleName, Module module)520 Logger demandLogger(String name, String resourceBundleName, Module module) { 521 Logger result = getLogger(name); 522 if (result == null) { 523 // only allocate the new logger once 524 Logger newLogger = new Logger(name, resourceBundleName, 525 module, this, false); 526 do { 527 if (addLogger(newLogger)) { 528 // We successfully added the new Logger that we 529 // created above so return it without refetching. 530 return newLogger; 531 } 532 533 // We didn't add the new Logger that we created above 534 // because another thread added a Logger with the same 535 // name after our null check above and before our call 536 // to addLogger(). We have to refetch the Logger because 537 // addLogger() returns a boolean instead of the Logger 538 // reference itself. However, if the thread that created 539 // the other Logger is not holding a strong reference to 540 // the other Logger, then it is possible for the other 541 // Logger to be GC'ed after we saw it in addLogger() and 542 // before we can refetch it. If it has been GC'ed then 543 // we'll just loop around and try again. 544 result = getLogger(name); 545 } while (result == null); 546 } 547 return result; 548 } 549 demandSystemLogger(String name, String resourceBundleName, Class<?> caller)550 Logger demandSystemLogger(String name, String resourceBundleName, Class<?> caller) { 551 final Module module = caller == null ? null : caller.getModule(); 552 return demandSystemLogger(name, resourceBundleName, module); 553 } 554 555 @SuppressWarnings("removal") demandSystemLogger(String name, String resourceBundleName, Module module)556 Logger demandSystemLogger(String name, String resourceBundleName, Module module) { 557 // Add a system logger in the system context's namespace 558 final Logger sysLogger = getSystemContext() 559 .demandLogger(name, resourceBundleName, module); 560 561 // Add the system logger to the LogManager's namespace if not exist 562 // so that there is only one single logger of the given name. 563 // System loggers are visible to applications unless a logger of 564 // the same name has been added. 565 Logger logger; 566 do { 567 // First attempt to call addLogger instead of getLogger 568 // This would avoid potential bug in custom LogManager.getLogger 569 // implementation that adds a logger if does not exist 570 if (addLogger(sysLogger)) { 571 // successfully added the new system logger 572 logger = sysLogger; 573 } else { 574 logger = getLogger(name); 575 } 576 } while (logger == null); 577 578 // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 579 if (logger != sysLogger) { 580 // if logger already exists we merge the two logger configurations. 581 final Logger l = logger; 582 AccessController.doPrivileged(new PrivilegedAction<Void>() { 583 @Override 584 public Void run() { 585 l.mergeWithSystemLogger(sysLogger); 586 return null; 587 } 588 }); 589 } 590 return sysLogger; 591 } 592 593 // LoggerContext maintains the logger namespace per context. 594 // The default LogManager implementation has one system context and user 595 // context. The system context is used to maintain the namespace for 596 // all system loggers and is queried by the system code. If a system logger 597 // doesn't exist in the user context, it'll also be added to the user context. 598 // The user context is queried by the user code and all other loggers are 599 // added in the user context. 600 class LoggerContext { 601 // Table of named Loggers that maps names to Loggers. 602 private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers = 603 new ConcurrentHashMap<>(); 604 // Tree of named Loggers 605 private final LogNode root; LoggerContext()606 private LoggerContext() { 607 this.root = new LogNode(null, this); 608 } 609 610 611 // Tells whether default loggers are required in this context. 612 // If true, the default loggers will be lazily added. requiresDefaultLoggers()613 final boolean requiresDefaultLoggers() { 614 final boolean requiresDefaultLoggers = (getOwner() == manager); 615 if (requiresDefaultLoggers) { 616 getOwner().ensureLogManagerInitialized(); 617 } 618 return requiresDefaultLoggers; 619 } 620 621 // This context's LogManager. getOwner()622 final LogManager getOwner() { 623 return LogManager.this; 624 } 625 626 // This context owner's root logger, which if not null, and if 627 // the context requires default loggers, will be added to the context 628 // logger's tree. getRootLogger()629 final Logger getRootLogger() { 630 return getOwner().rootLogger; 631 } 632 633 // The global logger, which if not null, and if 634 // the context requires default loggers, will be added to the context 635 // logger's tree. getGlobalLogger()636 final Logger getGlobalLogger() { 637 @SuppressWarnings("deprecation") // avoids initialization cycles. 638 final Logger global = Logger.global; 639 return global; 640 } 641 demandLogger(String name, String resourceBundleName, Module module)642 Logger demandLogger(String name, String resourceBundleName, Module module) { 643 // a LogManager subclass may have its own implementation to add and 644 // get a Logger. So delegate to the LogManager to do the work. 645 final LogManager owner = getOwner(); 646 return owner.demandLogger(name, resourceBundleName, module); 647 } 648 649 650 // Due to subtle deadlock issues getUserContext() no longer 651 // calls addLocalLogger(rootLogger); 652 // Therefore - we need to add the default loggers later on. 653 // Checks that the context is properly initialized 654 // This is necessary before calling e.g. find(name) 655 // or getLoggerNames() 656 // ensureInitialized()657 private void ensureInitialized() { 658 if (requiresDefaultLoggers()) { 659 // Ensure that the root and global loggers are set. 660 ensureDefaultLogger(getRootLogger()); 661 ensureDefaultLogger(getGlobalLogger()); 662 } 663 } 664 665 findLogger(String name)666 Logger findLogger(String name) { 667 // Attempt to find logger without locking. 668 LoggerWeakRef ref = namedLoggers.get(name); 669 Logger logger = ref == null ? null : ref.get(); 670 671 // if logger is not null, then we can return it right away. 672 // if name is "" or "global" and logger is null 673 // we need to fall through and check that this context is 674 // initialized. 675 // if ref is not null and logger is null we also need to 676 // fall through. 677 if (logger != null || (ref == null && !name.isEmpty() 678 && !name.equals(Logger.GLOBAL_LOGGER_NAME))) { 679 return logger; 680 } 681 682 // We either found a stale reference, or we were looking for 683 // "" or "global" and didn't find them. 684 // Make sure context is initialized (has the default loggers), 685 // and look up again, cleaning the stale reference if it hasn't 686 // been cleaned up in between. All this needs to be done inside 687 // a synchronized block. 688 synchronized(this) { 689 // ensure that this context is properly initialized before 690 // looking for loggers. 691 ensureInitialized(); 692 ref = namedLoggers.get(name); 693 if (ref == null) { 694 return null; 695 } 696 logger = ref.get(); 697 if (logger == null) { 698 // The namedLoggers map holds stale weak reference 699 // to a logger which has been GC-ed. 700 ref.dispose(); 701 } 702 return logger; 703 } 704 } 705 706 // This method is called before adding a logger to the 707 // context. 708 // 'logger' is the context that will be added. 709 // This method will ensure that the defaults loggers are added 710 // before adding 'logger'. 711 // ensureAllDefaultLoggers(Logger logger)712 private void ensureAllDefaultLoggers(Logger logger) { 713 if (requiresDefaultLoggers()) { 714 final String name = logger.getName(); 715 if (!name.isEmpty()) { 716 ensureDefaultLogger(getRootLogger()); 717 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { 718 ensureDefaultLogger(getGlobalLogger()); 719 } 720 } 721 } 722 } 723 ensureDefaultLogger(Logger logger)724 private void ensureDefaultLogger(Logger logger) { 725 // Used for lazy addition of root logger and global logger 726 // to a LoggerContext. 727 728 // This check is simple sanity: we do not want that this 729 // method be called for anything else than Logger.global 730 // or owner.rootLogger. 731 if (!requiresDefaultLoggers() || logger == null 732 || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) { 733 734 // the case where we have a non null logger which is neither 735 // Logger.global nor manager.rootLogger indicates a serious 736 // issue - as ensureDefaultLogger should never be called 737 // with any other loggers than one of these two (or null - if 738 // e.g manager.rootLogger is not yet initialized)... 739 assert logger == null; 740 741 return; 742 } 743 744 // Adds the logger if it's not already there. 745 if (!namedLoggers.containsKey(logger.getName())) { 746 // It is important to prevent addLocalLogger to 747 // call ensureAllDefaultLoggers when we're in the process 748 // off adding one of those default loggers - as this would 749 // immediately cause a stack overflow. 750 // Therefore we must pass addDefaultLoggersIfNeeded=false, 751 // even if requiresDefaultLoggers is true. 752 addLocalLogger(logger, false); 753 } 754 } 755 addLocalLogger(Logger logger)756 boolean addLocalLogger(Logger logger) { 757 // no need to add default loggers if it's not required 758 return addLocalLogger(logger, requiresDefaultLoggers()); 759 } 760 761 // Add a logger to this context. This method will only set its level 762 // and process parent loggers. It doesn't set its handlers. addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded)763 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { 764 // addDefaultLoggersIfNeeded serves to break recursion when adding 765 // default loggers. If we're adding one of the default loggers 766 // (we're being called from ensureDefaultLogger()) then 767 // addDefaultLoggersIfNeeded will be false: we don't want to 768 // call ensureAllDefaultLoggers again. 769 // 770 // Note: addDefaultLoggersIfNeeded can also be false when 771 // requiresDefaultLoggers is false - since calling 772 // ensureAllDefaultLoggers would have no effect in this case. 773 if (addDefaultLoggersIfNeeded) { 774 ensureAllDefaultLoggers(logger); 775 } 776 777 final String name = logger.getName(); 778 if (name == null) { 779 throw new NullPointerException(); 780 } 781 LoggerWeakRef ref = namedLoggers.get(name); 782 if (ref != null) { 783 if (ref.refersTo(null)) { 784 // It's possible that the Logger was GC'ed after a 785 // drainLoggerRefQueueBounded() call above so allow 786 // a new one to be registered. 787 ref.dispose(); 788 } else { 789 // We already have a registered logger with the given name. 790 return false; 791 } 792 } 793 794 // We're adding a new logger. 795 // Note that we are creating a weak reference here. 796 final LogManager owner = getOwner(); 797 logger.setLogManager(owner); 798 ref = owner.new LoggerWeakRef(logger); 799 800 // Apply any initial level defined for the new logger, unless 801 // the logger's level is already initialized 802 Level level = owner.getLevelProperty(name + ".level", null); 803 if (level != null && !logger.isLevelInitialized()) { 804 doSetLevel(logger, level); 805 } 806 807 // instantiation of the handler is done in the LogManager.addLogger 808 // implementation as a handler class may be only visible to LogManager 809 // subclass for the custom log manager case 810 processParentHandlers(logger, name, VisitedLoggers.NEVER); 811 812 // Find the new node and its parent. 813 LogNode node = getNode(name); 814 node.loggerRef = ref; 815 Logger parent = null; 816 LogNode nodep = node.parent; 817 while (nodep != null) { 818 LoggerWeakRef nodeRef = nodep.loggerRef; 819 if (nodeRef != null) { 820 parent = nodeRef.get(); 821 if (parent != null) { 822 break; 823 } 824 } 825 nodep = nodep.parent; 826 } 827 828 if (parent != null) { 829 doSetParent(logger, parent); 830 } 831 // Walk over the children and tell them we are their new parent. 832 node.walkAndSetParent(logger); 833 // new LogNode is ready so tell the LoggerWeakRef about it 834 ref.setNode(node); 835 836 // Do not publish 'ref' in namedLoggers before the logger tree 837 // is fully updated - because the named logger will be visible as 838 // soon as it is published in namedLoggers (findLogger takes 839 // benefit of the ConcurrentHashMap implementation of namedLoggers 840 // to avoid synchronizing on retrieval when that is possible). 841 namedLoggers.put(name, ref); 842 return true; 843 } 844 removeLoggerRef(String name, LoggerWeakRef ref)845 void removeLoggerRef(String name, LoggerWeakRef ref) { 846 namedLoggers.remove(name, ref); 847 } 848 getLoggerNames()849 synchronized Enumeration<String> getLoggerNames() { 850 // ensure that this context is properly initialized before 851 // returning logger names. 852 ensureInitialized(); 853 return Collections.enumeration(namedLoggers.keySet()); 854 } 855 856 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 857 // parents have levels or handlers defined, make sure they are instantiated. 858 @SuppressWarnings("removal") processParentHandlers(final Logger logger, final String name, Predicate<Logger> visited)859 private void processParentHandlers(final Logger logger, final String name, 860 Predicate<Logger> visited) { 861 final LogManager owner = getOwner(); 862 AccessController.doPrivileged(new PrivilegedAction<Void>() { 863 @Override 864 public Void run() { 865 if (logger != owner.rootLogger) { 866 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); 867 if (!useParent) { 868 logger.setUseParentHandlers(false); 869 } 870 } 871 return null; 872 } 873 }); 874 875 int ix = 1; 876 for (;;) { 877 int ix2 = name.indexOf('.', ix); 878 if (ix2 < 0) { 879 break; 880 } 881 String pname = name.substring(0, ix2); 882 if (owner.getProperty(pname + ".level") != null || 883 owner.getProperty(pname + ".handlers") != null) { 884 // This pname has a level/handlers definition. 885 // Make sure it exists. 886 if (visited.test(demandLogger(pname, null, null))) { 887 break; 888 } 889 } 890 ix = ix2+1; 891 } 892 } 893 894 // Gets a node in our tree of logger nodes. 895 // If necessary, create it. getNode(String name)896 LogNode getNode(String name) { 897 if (name == null || name.isEmpty()) { 898 return root; 899 } 900 LogNode node = root; 901 while (name.length() > 0) { 902 int ix = name.indexOf('.'); 903 String head; 904 if (ix > 0) { 905 head = name.substring(0, ix); 906 name = name.substring(ix + 1); 907 } else { 908 head = name; 909 name = ""; 910 } 911 if (node.children == null) { 912 node.children = new HashMap<>(); 913 } 914 LogNode child = node.children.get(head); 915 if (child == null) { 916 child = new LogNode(node, this); 917 node.children.put(head, child); 918 } 919 node = child; 920 } 921 return node; 922 } 923 } 924 925 final class SystemLoggerContext extends LoggerContext { 926 // Add a system logger in the system context's namespace as well as 927 // in the LogManager's namespace if not exist so that there is only 928 // one single logger of the given name. System loggers are visible 929 // to applications unless a logger of the same name has been added. 930 @Override demandLogger(String name, String resourceBundleName, Module module)931 Logger demandLogger(String name, String resourceBundleName, 932 Module module) { 933 Logger result = findLogger(name); 934 if (result == null) { 935 // only allocate the new system logger once 936 Logger newLogger = new Logger(name, resourceBundleName, 937 module, getOwner(), true); 938 do { 939 if (addLocalLogger(newLogger)) { 940 // We successfully added the new Logger that we 941 // created above so return it without refetching. 942 result = newLogger; 943 } else { 944 // We didn't add the new Logger that we created above 945 // because another thread added a Logger with the same 946 // name after our null check above and before our call 947 // to addLogger(). We have to refetch the Logger because 948 // addLogger() returns a boolean instead of the Logger 949 // reference itself. However, if the thread that created 950 // the other Logger is not holding a strong reference to 951 // the other Logger, then it is possible for the other 952 // Logger to be GC'ed after we saw it in addLogger() and 953 // before we can refetch it. If it has been GC'ed then 954 // we'll just loop around and try again. 955 result = findLogger(name); 956 } 957 } while (result == null); 958 } 959 return result; 960 } 961 } 962 963 // Add new per logger handlers. 964 // We need to raise privilege here. All our decisions will 965 // be made based on the logging configuration, which can 966 // only be modified by trusted code. 967 @SuppressWarnings("removal") loadLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName)968 private void loadLoggerHandlers(final Logger logger, final String name, 969 final String handlersPropertyName) 970 { 971 AccessController.doPrivileged(new PrivilegedAction<Void>() { 972 @Override 973 public Void run() { 974 setLoggerHandlers(logger, name, handlersPropertyName, 975 createLoggerHandlers(name, handlersPropertyName)); 976 return null; 977 } 978 }); 979 } 980 setLoggerHandlers(final Logger logger, final String name, final String handlersPropertyName, List<Handler> handlers)981 private void setLoggerHandlers(final Logger logger, final String name, 982 final String handlersPropertyName, 983 List<Handler> handlers) 984 { 985 final boolean ensureCloseOnReset = ! handlers.isEmpty() 986 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true); 987 int count = 0; 988 for (Handler hdl : handlers) { 989 logger.addHandler(hdl); 990 if (++count == 1 && ensureCloseOnReset) { 991 // add this logger to the closeOnResetLoggers list. 992 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger)); 993 } 994 } 995 } 996 createLoggerHandlers(final String name, final String handlersPropertyName)997 private List<Handler> createLoggerHandlers(final String name, 998 final String handlersPropertyName) 999 { 1000 String names[] = parseClassNames(handlersPropertyName); 1001 List<Handler> handlers = new ArrayList<>(names.length); 1002 for (String type : names) { 1003 try { 1004 @SuppressWarnings("deprecation") 1005 Object o = ClassLoader.getSystemClassLoader().loadClass(type).newInstance(); 1006 Handler hdl = (Handler) o; 1007 // Check if there is a property defining the 1008 // this handler's level. 1009 String levs = getProperty(type + ".level"); 1010 if (levs != null) { 1011 Level l = Level.findLevel(levs); 1012 if (l != null) { 1013 hdl.setLevel(l); 1014 } else { 1015 // Probably a bad level. Drop through. 1016 System.err.println("Can't set level for " + type); 1017 } 1018 } 1019 // Add this Handler to the logger 1020 handlers.add(hdl); 1021 } catch (Exception ex) { 1022 System.err.println("Can't load log handler \"" + type + "\""); 1023 System.err.println("" + ex); 1024 ex.printStackTrace(); 1025 } 1026 } 1027 1028 return handlers; 1029 } 1030 1031 1032 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 1033 // that have been GC'ed. 1034 private final ReferenceQueue<Logger> loggerRefQueue 1035 = new ReferenceQueue<>(); 1036 1037 // Package-level inner class. 1038 // Helper class for managing WeakReferences to Logger objects. 1039 // 1040 // LogManager.namedLoggers 1041 // - has weak references to all named Loggers 1042 // - namedLoggers keeps the LoggerWeakRef objects for the named 1043 // Loggers around until we can deal with the book keeping for 1044 // the named Logger that is being GC'ed. 1045 // LogManager.LogNode.loggerRef 1046 // - has a weak reference to a named Logger 1047 // - the LogNode will also keep the LoggerWeakRef objects for 1048 // the named Loggers around; currently LogNodes never go away. 1049 // Logger.kids 1050 // - has a weak reference to each direct child Logger; this 1051 // includes anonymous and named Loggers 1052 // - anonymous Loggers are always children of the rootLogger 1053 // which is a strong reference; rootLogger.kids keeps the 1054 // LoggerWeakRef objects for the anonymous Loggers around 1055 // until we can deal with the book keeping. 1056 // 1057 final class LoggerWeakRef extends WeakReference<Logger> { 1058 private String name; // for namedLoggers cleanup 1059 private LogNode node; // for loggerRef cleanup 1060 private WeakReference<Logger> parentRef; // for kids cleanup 1061 private boolean disposed = false; // avoid calling dispose twice 1062 LoggerWeakRef(Logger logger)1063 LoggerWeakRef(Logger logger) { 1064 super(logger, loggerRefQueue); 1065 1066 name = logger.getName(); // save for namedLoggers cleanup 1067 } 1068 1069 // dispose of this LoggerWeakRef object dispose()1070 void dispose() { 1071 // Avoid calling dispose twice. When a Logger is gc'ed, its 1072 // LoggerWeakRef will be enqueued. 1073 // However, a new logger of the same name may be added (or looked 1074 // up) before the queue is drained. When that happens, dispose() 1075 // will be called by addLocalLogger() or findLogger(). 1076 // Later when the queue is drained, dispose() will be called again 1077 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed 1078 // avoids processing the data twice (even though the code should 1079 // now be reentrant). 1080 synchronized(this) { 1081 // Note to maintainers: 1082 // Be careful not to call any method that tries to acquire 1083 // another lock from within this block - as this would surely 1084 // lead to deadlocks, given that dispose() can be called by 1085 // multiple threads, and from within different synchronized 1086 // methods/blocks. 1087 if (disposed) return; 1088 disposed = true; 1089 } 1090 1091 final LogNode n = node; 1092 if (n != null) { 1093 // n.loggerRef can only be safely modified from within 1094 // a lock on LoggerContext. removeLoggerRef is already 1095 // synchronized on LoggerContext so calling 1096 // n.context.removeLoggerRef from within this lock is safe. 1097 synchronized (n.context) { 1098 // if we have a LogNode, then we were a named Logger 1099 // so clear namedLoggers weak ref to us 1100 n.context.removeLoggerRef(name, this); 1101 name = null; // clear our ref to the Logger's name 1102 1103 // LogNode may have been reused - so only clear 1104 // LogNode.loggerRef if LogNode.loggerRef == this 1105 if (n.loggerRef == this) { 1106 n.loggerRef = null; // clear LogNode's weak ref to us 1107 } 1108 node = null; // clear our ref to LogNode 1109 } 1110 } 1111 1112 if (parentRef != null) { 1113 // this LoggerWeakRef has or had a parent Logger 1114 Logger parent = parentRef.get(); 1115 if (parent != null) { 1116 // the parent Logger is still there so clear the 1117 // parent Logger's weak ref to us 1118 parent.removeChildLogger(this); 1119 } 1120 parentRef = null; // clear our weak ref to the parent Logger 1121 } 1122 } 1123 1124 // set the node field to the specified value setNode(LogNode node)1125 void setNode(LogNode node) { 1126 this.node = node; 1127 } 1128 1129 // set the parentRef field to the specified value setParentRef(WeakReference<Logger> parentRef)1130 void setParentRef(WeakReference<Logger> parentRef) { 1131 this.parentRef = parentRef; 1132 } 1133 } 1134 1135 // Package-level method. 1136 // Drain some Logger objects that have been GC'ed. 1137 // 1138 // drainLoggerRefQueueBounded() is called by addLogger() below 1139 // and by Logger.getAnonymousLogger(String) so we'll drain up to 1140 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 1141 // 1142 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 1143 // us about a 50/50 mix in increased weak ref counts versus 1144 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 1145 // Here are stats for cleaning up sets of 400 anonymous Loggers: 1146 // - test duration 1 minute 1147 // - sample size of 125 sets of 400 1148 // - average: 1.99 ms 1149 // - minimum: 0.57 ms 1150 // - maximum: 25.3 ms 1151 // 1152 // The same config gives us a better decreased weak ref count 1153 // than increased weak ref count in the LoggerWeakRefLeak test. 1154 // Here are stats for cleaning up sets of 400 named Loggers: 1155 // - test duration 2 minutes 1156 // - sample size of 506 sets of 400 1157 // - average: 0.57 ms 1158 // - minimum: 0.02 ms 1159 // - maximum: 10.9 ms 1160 // 1161 private static final int MAX_ITERATIONS = 400; drainLoggerRefQueueBounded()1162 final void drainLoggerRefQueueBounded() { 1163 for (int i = 0; i < MAX_ITERATIONS; i++) { 1164 if (loggerRefQueue == null) { 1165 // haven't finished loading LogManager yet 1166 break; 1167 } 1168 1169 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 1170 if (ref == null) { 1171 break; 1172 } 1173 // a Logger object has been GC'ed so clean it up 1174 ref.dispose(); 1175 } 1176 } 1177 1178 /** 1179 * Add a named logger. This does nothing and returns false if a logger 1180 * with the same name is already registered. 1181 * <p> 1182 * The Logger factory methods call this method to register each 1183 * newly created Logger. 1184 * <p> 1185 * The application should retain its own reference to the Logger 1186 * object to avoid it being garbage collected. The LogManager 1187 * may only retain a weak reference. 1188 * 1189 * @param logger the new logger. 1190 * @return true if the argument logger was registered successfully, 1191 * false if a logger of that name already exists. 1192 * @throws NullPointerException if the logger name is null. 1193 */ addLogger(Logger logger)1194 public boolean addLogger(Logger logger) { 1195 final String name = logger.getName(); 1196 if (name == null) { 1197 throw new NullPointerException(); 1198 } 1199 drainLoggerRefQueueBounded(); 1200 LoggerContext cx = getUserContext(); 1201 if (cx.addLocalLogger(logger) || forceLoadHandlers(logger)) { 1202 // Do we have a per logger handler too? 1203 // Note: this will add a 200ms penalty 1204 loadLoggerHandlers(logger, name, name + ".handlers"); 1205 return true; 1206 } else { 1207 return false; 1208 } 1209 } 1210 1211 1212 // Checks whether the given logger is a special logger 1213 // that still requires handler initialization. 1214 // This method will only return true for the root and 1215 // global loggers and only if called by the thread that 1216 // performs initialization of the LogManager, during that 1217 // initialization. Must only be called by addLogger. 1218 @SuppressWarnings("deprecation") forceLoadHandlers(Logger logger)1219 private boolean forceLoadHandlers(Logger logger) { 1220 // Called just after reading the primordial configuration, in 1221 // the same thread that reads it. 1222 // The root and global logger would already be present in the context 1223 // by this point, but we would not have called loadLoggerHandlers 1224 // yet. 1225 return (logger == rootLogger || logger == Logger.global) 1226 && !initializationDone 1227 && initializedCalled 1228 && configurationLock.isHeldByCurrentThread(); 1229 } 1230 1231 // Private method to set a level on a logger. 1232 // If necessary, we raise privilege before doing the call. 1233 @SuppressWarnings("removal") doSetLevel(final Logger logger, final Level level)1234 private static void doSetLevel(final Logger logger, final Level level) { 1235 SecurityManager sm = System.getSecurityManager(); 1236 if (sm == null) { 1237 // There is no security manager, so things are easy. 1238 logger.setLevel(level); 1239 return; 1240 } 1241 // There is a security manager. Raise privilege before 1242 // calling setLevel. 1243 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1244 @Override 1245 public Object run() { 1246 logger.setLevel(level); 1247 return null; 1248 }}); 1249 } 1250 1251 // Private method to set a parent on a logger. 1252 // If necessary, we raise privilege before doing the setParent call. 1253 @SuppressWarnings("removal") doSetParent(final Logger logger, final Logger parent)1254 private static void doSetParent(final Logger logger, final Logger parent) { 1255 SecurityManager sm = System.getSecurityManager(); 1256 if (sm == null) { 1257 // There is no security manager, so things are easy. 1258 logger.setParent(parent); 1259 return; 1260 } 1261 // There is a security manager. Raise privilege before 1262 // calling setParent. 1263 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1264 @Override 1265 public Object run() { 1266 logger.setParent(parent); 1267 return null; 1268 }}); 1269 } 1270 1271 /** 1272 * Method to find a named logger. 1273 * <p> 1274 * Note that since untrusted code may create loggers with 1275 * arbitrary names this method should not be relied on to 1276 * find Loggers for security sensitive logging. 1277 * It is also important to note that the Logger associated with the 1278 * String {@code name} may be garbage collected at any time if there 1279 * is no strong reference to the Logger. The caller of this method 1280 * must check the return value for null in order to properly handle 1281 * the case where the Logger has been garbage collected. 1282 * 1283 * @param name name of the logger 1284 * @return matching logger or null if none is found 1285 */ getLogger(String name)1286 public Logger getLogger(String name) { 1287 return getUserContext().findLogger(name); 1288 } 1289 1290 /** 1291 * Get an enumeration of known logger names. 1292 * <p> 1293 * Note: Loggers may be added dynamically as new classes are loaded. 1294 * This method only reports on the loggers that are currently registered. 1295 * It is also important to note that this method only returns the name 1296 * of a Logger, not a strong reference to the Logger itself. 1297 * The returned String does nothing to prevent the Logger from being 1298 * garbage collected. In particular, if the returned name is passed 1299 * to {@code LogManager.getLogger()}, then the caller must check the 1300 * return value from {@code LogManager.getLogger()} for null to properly 1301 * handle the case where the Logger has been garbage collected in the 1302 * time since its name was returned by this method. 1303 * 1304 * @return enumeration of logger name strings 1305 */ getLoggerNames()1306 public Enumeration<String> getLoggerNames() { 1307 return getUserContext().getLoggerNames(); 1308 } 1309 1310 /** 1311 * Reads and initializes the logging configuration. 1312 * <p> 1313 * If the "java.util.logging.config.class" system property is set, then the 1314 * property value is treated as a class name. The given class will be 1315 * loaded, an object will be instantiated, and that object's constructor 1316 * is responsible for reading in the initial configuration. (That object 1317 * may use other system properties to control its configuration.) The 1318 * alternate configuration class can use {@code readConfiguration(InputStream)} 1319 * to define properties in the LogManager. 1320 * <p> 1321 * If "java.util.logging.config.class" system property is <b>not</b> set, 1322 * then this method will read the initial configuration from a properties 1323 * file and calls the {@link #readConfiguration(InputStream)} method to initialize 1324 * the configuration. The "java.util.logging.config.file" system property can be used 1325 * to specify the properties file that will be read as the initial configuration; 1326 * if not set, then the LogManager default configuration is used. 1327 * The default configuration is typically loaded from the 1328 * properties file "{@code conf/logging.properties}" in the Java installation 1329 * directory. 1330 * 1331 * <p> 1332 * Any {@linkplain #addConfigurationListener registered configuration 1333 * listener} will be invoked after the properties are read. 1334 * 1335 * @apiNote This {@code readConfiguration} method should only be used for 1336 * initializing the configuration during LogManager initialization or 1337 * used with the "java.util.logging.config.class" property. 1338 * When this method is called after loggers have been created, and 1339 * the "java.util.logging.config.class" system property is not set, all 1340 * existing loggers will be {@linkplain #reset() reset}. Then any 1341 * existing loggers that have a level property specified in the new 1342 * configuration stream will be {@linkplain 1343 * Logger#setLevel(java.util.logging.Level) set} to the specified log level. 1344 * <p> 1345 * To properly update the logging configuration, use the 1346 * {@link #updateConfiguration(java.util.function.Function)} or 1347 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)} 1348 * methods instead. 1349 * 1350 * @throws SecurityException if a security manager exists and if 1351 * the caller does not have LoggingPermission("control"). 1352 * @throws IOException if there are IO problems reading the configuration. 1353 */ readConfiguration()1354 public void readConfiguration() throws IOException, SecurityException { 1355 checkPermission(); 1356 1357 // if a configuration class is specified, load it and use it. 1358 String cname = System.getProperty("java.util.logging.config.class"); 1359 if (cname != null) { 1360 try { 1361 // Instantiate the named class. It is its constructor's 1362 // responsibility to initialize the logging configuration, by 1363 // calling readConfiguration(InputStream) with a suitable stream. 1364 try { 1365 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 1366 @SuppressWarnings("deprecation") 1367 Object witness = clz.newInstance(); 1368 return; 1369 } catch (ClassNotFoundException ex) { 1370 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 1371 @SuppressWarnings("deprecation") 1372 Object witness = clz.newInstance(); 1373 return; 1374 } 1375 } catch (Exception ex) { 1376 System.err.println("Logging configuration class \"" + cname + "\" failed"); 1377 System.err.println("" + ex); 1378 // keep going and useful config file. 1379 } 1380 } 1381 1382 String fname = getConfigurationFileName(); 1383 try (final InputStream in = new FileInputStream(fname)) { 1384 final BufferedInputStream bin = new BufferedInputStream(in); 1385 readConfiguration(bin); 1386 } 1387 } 1388 getConfigurationFileName()1389 String getConfigurationFileName() throws IOException { 1390 String fname = System.getProperty("java.util.logging.config.file"); 1391 if (fname == null) { 1392 fname = System.getProperty("java.home"); 1393 if (fname == null) { 1394 throw new Error("Can't find java.home ??"); 1395 } 1396 fname = Paths.get(fname, "conf", "logging.properties") 1397 .toAbsolutePath().normalize().toString(); 1398 } 1399 return fname; 1400 } 1401 1402 /** 1403 * Reset the logging configuration. 1404 * <p> 1405 * For all named loggers, the reset operation removes and closes 1406 * all Handlers and (except for the root logger) sets the level 1407 * to {@code null}. The root logger's level is set to {@code Level.INFO}. 1408 * 1409 * @apiNote Calling this method also clears the LogManager {@linkplain 1410 * #getProperty(java.lang.String) properties}. The {@link 1411 * #updateConfiguration(java.util.function.Function) 1412 * updateConfiguration(Function)} or 1413 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function) 1414 * updateConfiguration(InputStream, Function)} method can be used to 1415 * properly update to a new configuration. 1416 * 1417 * @throws SecurityException if a security manager exists and if 1418 * the caller does not have LoggingPermission("control"). 1419 */ 1420 reset()1421 public void reset() throws SecurityException { 1422 checkPermission(); 1423 1424 List<CloseOnReset> persistent; 1425 1426 // We don't want reset() and readConfiguration() 1427 // to run in parallel 1428 configurationLock.lock(); 1429 try { 1430 // install new empty properties 1431 props = new Properties(); 1432 // make sure we keep the loggers persistent until reset is done. 1433 // Those are the loggers for which we previously created a 1434 // handler from the configuration, and we need to prevent them 1435 // from being gc'ed until those handlers are closed. 1436 persistent = new ArrayList<>(closeOnResetLoggers); 1437 closeOnResetLoggers.clear(); 1438 1439 // if reset has been called from shutdown-hook (Cleaner), 1440 // or if reset has been called from readConfiguration() which 1441 // already holds the lock and will change the state itself, 1442 // then do not change state here... 1443 if (globalHandlersState != STATE_SHUTDOWN && 1444 globalHandlersState != STATE_READING_CONFIG) { 1445 // ...else user called reset()... 1446 // Since we are doing a reset we no longer want to initialize 1447 // the global handlers, if they haven't been initialized yet. 1448 globalHandlersState = STATE_INITIALIZED; 1449 } 1450 1451 for (LoggerContext cx : contexts()) { 1452 resetLoggerContext(cx); 1453 } 1454 1455 persistent.clear(); 1456 } finally { 1457 configurationLock.unlock(); 1458 } 1459 } 1460 resetLoggerContext(LoggerContext cx)1461 private void resetLoggerContext(LoggerContext cx) { 1462 Enumeration<String> enum_ = cx.getLoggerNames(); 1463 while (enum_.hasMoreElements()) { 1464 String name = enum_.nextElement(); 1465 Logger logger = cx.findLogger(name); 1466 if (logger != null) { 1467 resetLogger(logger); 1468 } 1469 } 1470 } 1471 closeHandlers(Logger logger)1472 private void closeHandlers(Logger logger) { 1473 Handler[] targets = logger.getHandlers(); 1474 for (Handler h : targets) { 1475 logger.removeHandler(h); 1476 try { 1477 h.close(); 1478 } catch (Exception ex) { 1479 // Problems closing a handler? Keep going... 1480 } catch (Error e) { 1481 // ignore Errors while shutting down 1482 if (globalHandlersState != STATE_SHUTDOWN) { 1483 throw e; 1484 } 1485 } 1486 } 1487 } 1488 1489 // Private method to reset an individual target logger. resetLogger(Logger logger)1490 private void resetLogger(Logger logger) { 1491 // Close all the Logger handlers. 1492 closeHandlers(logger); 1493 1494 // Reset Logger level 1495 String name = logger.getName(); 1496 if (name != null && name.isEmpty()) { 1497 // This is the root logger. 1498 logger.setLevel(defaultLevel); 1499 } else { 1500 logger.setLevel(null); 1501 } 1502 } 1503 1504 // get a list of whitespace separated classnames from a property. parseClassNames(String propertyName)1505 private String[] parseClassNames(String propertyName) { 1506 String hands = getProperty(propertyName); 1507 if (hands == null) { 1508 return new String[0]; 1509 } 1510 hands = hands.trim(); 1511 int ix = 0; 1512 final List<String> result = new ArrayList<>(); 1513 while (ix < hands.length()) { 1514 int end = ix; 1515 while (end < hands.length()) { 1516 if (Character.isWhitespace(hands.charAt(end))) { 1517 break; 1518 } 1519 if (hands.charAt(end) == ',') { 1520 break; 1521 } 1522 end++; 1523 } 1524 String word = hands.substring(ix, end); 1525 ix = end+1; 1526 word = word.trim(); 1527 if (word.length() == 0) { 1528 continue; 1529 } 1530 result.add(word); 1531 } 1532 return result.toArray(new String[result.size()]); 1533 } 1534 1535 /** 1536 * Reads and initializes the logging configuration from the given input stream. 1537 * 1538 * <p> 1539 * Any {@linkplain #addConfigurationListener registered configuration 1540 * listener} will be invoked after the properties are read. 1541 * 1542 * @apiNote This {@code readConfiguration} method should only be used for 1543 * initializing the configuration during LogManager initialization or 1544 * used with the "java.util.logging.config.class" property. 1545 * When this method is called after loggers have been created, all 1546 * existing loggers will be {@linkplain #reset() reset}. Then any 1547 * existing loggers that have a level property specified in the 1548 * given input stream will be {@linkplain 1549 * Logger#setLevel(java.util.logging.Level) set} to the specified log level. 1550 * <p> 1551 * To properly update the logging configuration, use the 1552 * {@link #updateConfiguration(java.util.function.Function)} or 1553 * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)} 1554 * method instead. 1555 * 1556 * @param ins stream to read properties from 1557 * @throws SecurityException if a security manager exists and if 1558 * the caller does not have LoggingPermission("control"). 1559 * @throws IOException if there are problems reading from the stream, 1560 * or the given stream is not in the 1561 * {@linkplain java.util.Properties properties file} format. 1562 */ readConfiguration(InputStream ins)1563 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 1564 checkPermission(); 1565 1566 // We don't want reset() and readConfiguration() to run 1567 // in parallel. 1568 configurationLock.lock(); 1569 try { 1570 if (globalHandlersState == STATE_SHUTDOWN) { 1571 // already in terminal state: don't even bother 1572 // to read the configuration 1573 return; 1574 } 1575 1576 // change state to STATE_READING_CONFIG to signal reset() to not change it 1577 globalHandlersState = STATE_READING_CONFIG; 1578 try { 1579 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG 1580 // so that while reading configuration, any ongoing logging requests block and 1581 // wait for the outcome (see the end of this try statement) 1582 reset(); 1583 1584 try { 1585 // Load the properties 1586 props.load(ins); 1587 } catch (IllegalArgumentException x) { 1588 // props.load may throw an IllegalArgumentException if the stream 1589 // contains malformed Unicode escape sequences. 1590 // We wrap that in an IOException as readConfiguration is 1591 // specified to throw IOException if there are problems reading 1592 // from the stream. 1593 // Note: new IOException(x.getMessage(), x) allow us to get a more 1594 // concise error message than new IOException(x); 1595 throw new IOException(x.getMessage(), x); 1596 } 1597 1598 // Instantiate new configuration objects. 1599 String names[] = parseClassNames("config"); 1600 1601 for (String word : names) { 1602 try { 1603 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 1604 @SuppressWarnings("deprecation") 1605 Object witness = clz.newInstance(); 1606 } catch (Exception ex) { 1607 System.err.println("Can't load config class \"" + word + "\""); 1608 System.err.println("" + ex); 1609 // ex.printStackTrace(); 1610 } 1611 } 1612 1613 // Set levels on any pre-existing loggers, based on the new properties. 1614 setLevelsOnExistingLoggers(); 1615 1616 // Note that we need to reinitialize global handles when 1617 // they are first referenced. 1618 globalHandlersState = STATE_UNINITIALIZED; 1619 } catch (Throwable t) { 1620 // If there were any trouble, then set state to STATE_INITIALIZED 1621 // so that no global handlers reinitialization is performed on not fully 1622 // initialized configuration. 1623 globalHandlersState = STATE_INITIALIZED; 1624 // re-throw 1625 throw t; 1626 } 1627 } finally { 1628 configurationLock.unlock(); 1629 } 1630 1631 // should be called out of lock to avoid dead-lock situations 1632 // when user code is involved 1633 invokeConfigurationListeners(); 1634 } 1635 1636 // This enum enumerate the configuration properties that will be 1637 // updated on existing loggers when the configuration is updated 1638 // with LogManager.updateConfiguration(). 1639 // 1640 // Note that this works properly only for the global LogManager - as 1641 // Handler and its subclasses get their configuration from 1642 // LogManager.getLogManager(). 1643 // 1644 static enum ConfigProperty { 1645 LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers"); 1646 final String suffix; 1647 final int length; ConfigProperty(String suffix)1648 private ConfigProperty(String suffix) { 1649 this.suffix = Objects.requireNonNull(suffix); 1650 length = suffix.length(); 1651 } 1652 handleKey(String key)1653 public boolean handleKey(String key) { 1654 if (this == HANDLERS && suffix.substring(1).equals(key)) return true; 1655 if (this == HANDLERS && suffix.equals(key)) return false; 1656 return key.endsWith(suffix); 1657 } key(String loggerName)1658 String key(String loggerName) { 1659 if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) { 1660 return suffix.substring(1); 1661 } 1662 return loggerName + suffix; 1663 } loggerName(String key)1664 String loggerName(String key) { 1665 assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix); 1666 if (this == HANDLERS && suffix.substring(1).equals(key)) return ""; 1667 return key.substring(0, key.length() - length); 1668 } 1669 1670 /** 1671 * If the property is one that should be updated on existing loggers by 1672 * updateConfiguration, returns the name of the logger for which the 1673 * property is configured. Otherwise, returns null. 1674 * @param property a property key in 'props' 1675 * @return the name of the logger on which the property is to be set, 1676 * if the property is one that should be updated on existing 1677 * loggers, {@code null} otherwise. 1678 */ getLoggerName(String property)1679 static String getLoggerName(String property) { 1680 for (ConfigProperty p : ConfigProperty.ALL) { 1681 if (p.handleKey(property)) { 1682 return p.loggerName(property); 1683 } 1684 } 1685 return null; // Not a property that should be updated. 1686 } 1687 1688 /** 1689 * Find the ConfigProperty corresponding to the given 1690 * property key (may find none). 1691 * @param property a property key in 'props' 1692 * @return An optional containing a ConfigProperty object, 1693 * if the property is one that should be updated on existing 1694 * loggers, empty otherwise. 1695 */ find(String property)1696 static Optional<ConfigProperty> find(String property) { 1697 return ConfigProperty.ALL.stream() 1698 .filter(p -> p.handleKey(property)) 1699 .findFirst(); 1700 } 1701 1702 /** 1703 * Returns true if the given property is one that should be updated 1704 * on existing loggers. 1705 * Used to filter property name streams. 1706 * @param property a property key from the configuration. 1707 * @return true if this property is of interest for updateConfiguration. 1708 */ matches(String property)1709 static boolean matches(String property) { 1710 return find(property).isPresent(); 1711 } 1712 1713 /** 1714 * Returns true if the new property value is different from the old, 1715 * and therefore needs to be updated on existing loggers. 1716 * @param k a property key in the configuration 1717 * @param previous the old configuration 1718 * @param next the new configuration 1719 * @return true if the property is changing value between the two 1720 * configurations. 1721 */ needsUpdating(String k, Properties previous, Properties next)1722 static boolean needsUpdating(String k, Properties previous, Properties next) { 1723 final String p = trim(previous.getProperty(k, null)); 1724 final String n = trim(next.getProperty(k, null)); 1725 return ! Objects.equals(p,n); 1726 } 1727 1728 /** 1729 * Applies the mapping function for the given key to the next 1730 * configuration. 1731 * If the mapping function is null then this method does nothing. 1732 * Otherwise, it calls the mapping function to compute the value 1733 * that should be associated with {@code key} in the resulting 1734 * configuration, and applies it to {@code next}. 1735 * If the mapping function returns {@code null} the key is removed 1736 * from {@code next}. 1737 * 1738 * @param k a property key in the configuration 1739 * @param previous the old configuration 1740 * @param next the new configuration (modified by this function) 1741 * @param mappingFunction the mapping function. 1742 */ merge(String k, Properties previous, Properties next, BiFunction<String, String, String> mappingFunction)1743 static void merge(String k, Properties previous, Properties next, 1744 BiFunction<String, String, String> mappingFunction) { 1745 String p = trim(previous.getProperty(k, null)); 1746 String n = trim(next.getProperty(k, null)); 1747 String mapped = trim(mappingFunction.apply(p,n)); 1748 if (!Objects.equals(n, mapped)) { 1749 if (mapped == null) { 1750 next.remove(k); 1751 } else { 1752 next.setProperty(k, mapped); 1753 } 1754 } 1755 } 1756 1757 private static final EnumSet<ConfigProperty> ALL = 1758 EnumSet.allOf(ConfigProperty.class); 1759 } 1760 1761 // trim the value if not null. trim(String value)1762 private static String trim(String value) { 1763 return value == null ? null : value.trim(); 1764 } 1765 1766 /** 1767 * An object that keep track of loggers we have already visited. 1768 * Used when updating configuration, to avoid processing the same logger 1769 * twice. 1770 */ 1771 static final class VisitedLoggers implements Predicate<Logger> { 1772 final IdentityHashMap<Logger,Boolean> visited; VisitedLoggers(IdentityHashMap<Logger,Boolean> visited)1773 private VisitedLoggers(IdentityHashMap<Logger,Boolean> visited) { 1774 this.visited = visited; 1775 } VisitedLoggers()1776 VisitedLoggers() { 1777 this(new IdentityHashMap<>()); 1778 } 1779 @Override test(Logger logger)1780 public boolean test(Logger logger) { 1781 return visited != null && visited.put(logger, Boolean.TRUE) != null; 1782 } clear()1783 public void clear() { 1784 if (visited != null) visited.clear(); 1785 } 1786 1787 // An object that considers that no logger has ever been visited. 1788 // This is used when processParentHandlers is called from 1789 // LoggerContext.addLocalLogger 1790 static final VisitedLoggers NEVER = new VisitedLoggers(null); 1791 } 1792 1793 1794 /** 1795 * Type of the modification for a given property. One of SAME, ADDED, CHANGED, 1796 * or REMOVED. 1797 */ 1798 static enum ModType { 1799 SAME, // property had no value in the old and new conf, or had the 1800 // same value in both. 1801 ADDED, // property had no value in the old conf, but has one in the new. 1802 CHANGED, // property has a different value in the old conf and the new conf. 1803 REMOVED; // property has no value in the new conf, but had one in the old. of(String previous, String next)1804 static ModType of(String previous, String next) { 1805 if (previous == null && next != null) { 1806 return ADDED; 1807 } 1808 if (next == null && previous != null) { 1809 return REMOVED; 1810 } 1811 if (!Objects.equals(trim(previous), trim(next))) { 1812 return CHANGED; 1813 } 1814 return SAME; 1815 } 1816 } 1817 1818 /** 1819 * Updates the logging configuration. 1820 * <p> 1821 * If the "java.util.logging.config.file" system property is set, 1822 * then the property value specifies the properties file to be read 1823 * as the new configuration. Otherwise, the LogManager default 1824 * configuration is used. 1825 * <br>The default configuration is typically loaded from the 1826 * properties file "{@code conf/logging.properties}" in the 1827 * Java installation directory. 1828 * <p> 1829 * This method reads the new configuration and calls the {@link 1830 * #updateConfiguration(java.io.InputStream, java.util.function.Function) 1831 * updateConfiguration(ins, mapper)} method to 1832 * update the configuration. 1833 * 1834 * @apiNote 1835 * This method updates the logging configuration from reading 1836 * a properties file and ignores the "java.util.logging.config.class" 1837 * system property. The "java.util.logging.config.class" property is 1838 * only used by the {@link #readConfiguration()} method to load a custom 1839 * configuration class as an initial configuration. 1840 * 1841 * @param mapper a functional interface that takes a configuration 1842 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned 1843 * value will be applied to the resulting configuration. The 1844 * function <i>f</i> may return {@code null} to indicate that the property 1845 * <i>k</i> will not be added to the resulting configuration. 1846 * <br> 1847 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is 1848 * assumed. 1849 * <br> 1850 * For each <i>k</i>, the mapped function <i>f</i> will 1851 * be invoked with the value associated with <i>k</i> in the old 1852 * configuration (i.e <i>o</i>) and the value associated with 1853 * <i>k</i> in the new configuration (i.e. <i>n</i>). 1854 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no 1855 * value was present for <i>k</i> in the corresponding configuration. 1856 * 1857 * @throws SecurityException if a security manager exists and if 1858 * the caller does not have LoggingPermission("control"), or 1859 * does not have the permissions required to set up the 1860 * configuration (e.g. open file specified for FileHandlers 1861 * etc...) 1862 * 1863 * @throws NullPointerException if {@code mapper} returns a {@code null} 1864 * function when invoked. 1865 * 1866 * @throws IOException if there are problems reading from the 1867 * logging configuration file. 1868 * 1869 * @see #updateConfiguration(java.io.InputStream, java.util.function.Function) 1870 * @since 9 1871 */ updateConfiguration(Function<String, BiFunction<String,String,String>> mapper)1872 public void updateConfiguration(Function<String, BiFunction<String,String,String>> mapper) 1873 throws IOException { 1874 checkPermission(); 1875 ensureLogManagerInitialized(); 1876 drainLoggerRefQueueBounded(); 1877 1878 String fname = getConfigurationFileName(); 1879 try (final InputStream in = new FileInputStream(fname)) { 1880 final BufferedInputStream bin = new BufferedInputStream(in); 1881 updateConfiguration(bin, mapper); 1882 } 1883 } 1884 1885 /** 1886 * Updates the logging configuration. 1887 * <p> 1888 * For each configuration key in the {@linkplain 1889 * #getProperty(java.lang.String) existing configuration} and 1890 * the given input stream configuration, the given {@code mapper} function 1891 * is invoked to map from the configuration key to a function, 1892 * <i>f(o,n)</i>, that takes the old value and new value and returns 1893 * the resulting value to be applied in the resulting configuration, 1894 * as specified in the table below. 1895 * <p>Let <i>k</i> be a configuration key in the old or new configuration, 1896 * <i>o</i> be the old value (i.e. the value associated 1897 * with <i>k</i> in the old configuration), <i>n</i> be the 1898 * new value (i.e. the value associated with <i>k</i> in the new 1899 * configuration), and <i>f</i> be the function returned 1900 * by {@code mapper.apply(}<i>k</i>{@code )}: then <i>v = f(o,n)</i> is the 1901 * resulting value. If <i>v</i> is not {@code null}, then a property 1902 * <i>k</i> with value <i>v</i> will be added to the resulting configuration. 1903 * Otherwise, it will be omitted. 1904 * <br>A {@code null} value may be passed to function 1905 * <i>f</i> to indicate that the corresponding configuration has no 1906 * configuration key <i>k</i>. 1907 * The function <i>f</i> may return {@code null} to indicate that 1908 * there will be no value associated with <i>k</i> in the resulting 1909 * configuration. 1910 * <p> 1911 * If {@code mapper} is {@code null}, then <i>v</i> will be set to 1912 * <i>n</i>. 1913 * <p> 1914 * LogManager {@linkplain #getProperty(java.lang.String) properties} are 1915 * updated with the resulting value in the resulting configuration. 1916 * <p> 1917 * The registered {@linkplain #addConfigurationListener configuration 1918 * listeners} will be invoked after the configuration is successfully updated. 1919 * <br><br> 1920 * <table class="striped"> 1921 * <caption style="display:none">Updating configuration properties</caption> 1922 * <thead> 1923 * <tr> 1924 * <th scope="col">Property</th> 1925 * <th scope="col">Resulting Behavior</th> 1926 * </tr> 1927 * </thead> 1928 * <tbody> 1929 * <tr> 1930 * <th scope="row" style="vertical-align:top">{@code <logger>.level}</th> 1931 * <td> 1932 * <ul> 1933 * <li>If the resulting configuration defines a level for a logger and 1934 * if the resulting level is different than the level specified in the 1935 * the old configuration, or not specified in 1936 * the old configuration, then if the logger exists or if children for 1937 * that logger exist, the level for that logger will be updated, 1938 * and the change propagated to any existing logger children. 1939 * This may cause the logger to be created, if necessary. 1940 * </li> 1941 * <li>If the old configuration defined a level for a logger, and the 1942 * resulting configuration doesn't, then this change will not be 1943 * propagated to existing loggers, if any. 1944 * To completely replace a configuration - the caller should therefore 1945 * call {@link #reset() reset} to empty the current configuration, 1946 * before calling {@code updateConfiguration}. 1947 * </li> 1948 * </ul> 1949 * </td> 1950 * <tr> 1951 * <th scope="row" style="vertical-align:top">{@code <logger>.useParentHandlers}</th> 1952 * <td> 1953 * <ul> 1954 * <li>If either the resulting or the old value for the useParentHandlers 1955 * property is not null, then if the logger exists or if children for 1956 * that logger exist, that logger will be updated to the resulting 1957 * value. 1958 * The value of the useParentHandlers property is the value specified 1959 * in the configuration; if not specified, the default is true. 1960 * </li> 1961 * </ul> 1962 * </td> 1963 * </tr> 1964 * <tr> 1965 * <th scope="row" style="vertical-align:top">{@code <logger>.handlers}</th> 1966 * <td> 1967 * <ul> 1968 * <li>If the resulting configuration defines a list of handlers for a 1969 * logger, and if the resulting list is different than the list 1970 * specified in the old configuration for that logger (that could be 1971 * empty), then if the logger exists or its children exist, the 1972 * handlers associated with that logger are closed and removed and 1973 * the new handlers will be created per the resulting configuration 1974 * and added to that logger, creating that logger if necessary. 1975 * </li> 1976 * <li>If the old configuration defined some handlers for a logger, and 1977 * the resulting configuration doesn't, if that logger exists, 1978 * its handlers will be removed and closed. 1979 * </li> 1980 * <li>Changing the list of handlers on an existing logger will cause all 1981 * its previous handlers to be removed and closed, regardless of whether 1982 * they had been created from the configuration or programmatically. 1983 * The old handlers will be replaced by new handlers, if any. 1984 * </li> 1985 * </ul> 1986 * </td> 1987 * </tr> 1988 * <tr> 1989 * <th scope="row" style="vertical-align:top">{@code <handler-name>.*}</th> 1990 * <td> 1991 * <ul> 1992 * <li>Properties configured/changed on handler classes will only affect 1993 * newly created handlers. If a node is configured with the same list 1994 * of handlers in the old and the resulting configuration, then these 1995 * handlers will remain unchanged. 1996 * </li> 1997 * </ul> 1998 * </td> 1999 * </tr> 2000 * <tr> 2001 * <th scope="row" style="vertical-align:top">{@code config} and any other property</th> 2002 * <td> 2003 * <ul> 2004 * <li>The resulting value for these property will be stored in the 2005 * LogManager properties, but {@code updateConfiguration} will not parse 2006 * or process their values. 2007 * </li> 2008 * </ul> 2009 * </td> 2010 * </tr> 2011 * </tbody> 2012 * </table> 2013 * <p> 2014 * <em>Example mapper functions:</em> 2015 * <br><br> 2016 * <ul> 2017 * <li>Replace all logging properties with the new configuration: 2018 * <br><br>{@code (k) -> ((o, n) -> n)}: 2019 * <br><br>this is equivalent to passing a null {@code mapper} parameter. 2020 * </li> 2021 * <li>Merge the new configuration and old configuration and use the 2022 * new value if <i>k</i> exists in the new configuration: 2023 * <br><br>{@code (k) -> ((o, n) -> n == null ? o : n)}: 2024 * <br><br>as if merging two collections as follows: 2025 * {@code result.putAll(oldc); result.putAll(newc)}.<br></li> 2026 * <li>Merge the new configuration and old configuration and use the old 2027 * value if <i>k</i> exists in the old configuration: 2028 * <br><br>{@code (k) -> ((o, n) -> o == null ? n : o)}: 2029 * <br><br>as if merging two collections as follows: 2030 * {@code result.putAll(newc); result.putAll(oldc)}.<br></li> 2031 * <li>Replace all properties with the new configuration except the handler 2032 * property to configure Logger's handler that is not root logger: 2033 * <br> 2034 * <pre>{@code (k) -> k.endsWith(".handlers")} 2035 * {@code ? ((o, n) -> (o == null ? n : o))} 2036 * {@code : ((o, n) -> n)}</pre> 2037 * </li> 2038 * </ul> 2039 * <p> 2040 * To completely reinitialize a configuration, an application can first call 2041 * {@link #reset() reset} to fully remove the old configuration, followed by 2042 * {@code updateConfiguration} to initialize the new configuration. 2043 * 2044 * @param ins a stream to read properties from 2045 * @param mapper a functional interface that takes a configuration 2046 * key <i>k</i> and returns a function <i>f(o,n)</i> whose returned 2047 * value will be applied to the resulting configuration. The 2048 * function <i>f</i> may return {@code null} to indicate that the property 2049 * <i>k</i> will not be added to the resulting configuration. 2050 * <br> 2051 * If {@code mapper} is {@code null} then {@code (k) -> ((o, n) -> n)} is 2052 * assumed. 2053 * <br> 2054 * For each <i>k</i>, the mapped function <i>f</i> will 2055 * be invoked with the value associated with <i>k</i> in the old 2056 * configuration (i.e <i>o</i>) and the value associated with 2057 * <i>k</i> in the new configuration (i.e. <i>n</i>). 2058 * <br>A {@code null} value for <i>o</i> or <i>n</i> indicates that no 2059 * value was present for <i>k</i> in the corresponding configuration. 2060 * 2061 * @throws SecurityException if a security manager exists and if 2062 * the caller does not have LoggingPermission("control"), or 2063 * does not have the permissions required to set up the 2064 * configuration (e.g. open files specified for FileHandlers) 2065 * 2066 * @throws NullPointerException if {@code ins} is null or if 2067 * {@code mapper} returns a null function when invoked. 2068 * 2069 * @throws IOException if there are problems reading from the stream, 2070 * or the given stream is not in the 2071 * {@linkplain java.util.Properties properties file} format. 2072 * @since 9 2073 */ updateConfiguration(InputStream ins, Function<String, BiFunction<String,String,String>> mapper)2074 public void updateConfiguration(InputStream ins, 2075 Function<String, BiFunction<String,String,String>> mapper) 2076 throws IOException { 2077 checkPermission(); 2078 ensureLogManagerInitialized(); 2079 drainLoggerRefQueueBounded(); 2080 2081 final Properties previous; 2082 final Set<String> updatePropertyNames; 2083 List<LoggerContext> cxs = Collections.emptyList(); 2084 final VisitedLoggers visited = new VisitedLoggers(); 2085 final Properties next = new Properties(); 2086 2087 try { 2088 // Load the properties 2089 next.load(ins); 2090 } catch (IllegalArgumentException x) { 2091 // props.load may throw an IllegalArgumentException if the stream 2092 // contains malformed Unicode escape sequences. 2093 // We wrap that in an IOException as updateConfiguration is 2094 // specified to throw IOException if there are problems reading 2095 // from the stream. 2096 // Note: new IOException(x.getMessage(), x) allow us to get a more 2097 // concise error message than new IOException(x); 2098 throw new IOException(x.getMessage(), x); 2099 } 2100 2101 if (globalHandlersState == STATE_SHUTDOWN) return; 2102 2103 // exclusive lock: readConfiguration/reset/updateConfiguration can't 2104 // run concurrently. 2105 // configurationLock.writeLock().lock(); 2106 configurationLock.lock(); 2107 try { 2108 if (globalHandlersState == STATE_SHUTDOWN) return; 2109 previous = props; 2110 2111 // Builds a TreeSet of all (old and new) property names. 2112 updatePropertyNames = 2113 Stream.concat(previous.stringPropertyNames().stream(), 2114 next.stringPropertyNames().stream()) 2115 .collect(Collectors.toCollection(TreeSet::new)); 2116 2117 if (mapper != null) { 2118 // mapper will potentially modify the content of 2119 // 'next', so we need to call it before affecting props=next. 2120 // give a chance to the mapper to control all 2121 // properties - not just those we will reset. 2122 updatePropertyNames.stream() 2123 .forEachOrdered(k -> ConfigProperty 2124 .merge(k, previous, next, 2125 Objects.requireNonNull(mapper.apply(k)))); 2126 } 2127 2128 props = next; 2129 2130 // allKeys will contain all keys: 2131 // - which correspond to a configuration property we are interested in 2132 // (first filter) 2133 // - whose value needs to be updated (because it's new, removed, or 2134 // different) in the resulting configuration (second filter) 2135 final Stream<String> allKeys = updatePropertyNames.stream() 2136 .filter(ConfigProperty::matches) 2137 .filter(k -> ConfigProperty.needsUpdating(k, previous, next)); 2138 2139 // Group configuration properties by logger name 2140 // We use a TreeMap so that parent loggers will be visited before 2141 // child loggers. 2142 final Map<String, TreeSet<String>> loggerConfigs = 2143 allKeys.collect(Collectors.groupingBy(ConfigProperty::getLoggerName, 2144 TreeMap::new, 2145 Collectors.toCollection(TreeSet::new))); 2146 2147 if (!loggerConfigs.isEmpty()) { 2148 cxs = contexts(); 2149 } 2150 final List<Logger> loggers = cxs.isEmpty() 2151 ? Collections.emptyList() : new ArrayList<>(cxs.size()); 2152 for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) { 2153 // This can be a logger name, or something else... 2154 // The only thing we know is that we found a property 2155 // we are interested in. 2156 // For instance, if we found x.y.z.level, then x.y.z could be 2157 // a logger, but it could also be a handler class... 2158 // Anyway... 2159 final String name = e.getKey(); 2160 final Set<String> properties = e.getValue(); 2161 loggers.clear(); 2162 for (LoggerContext cx : cxs) { 2163 Logger l = cx.findLogger(name); 2164 if (l != null && !visited.test(l)) { 2165 loggers.add(l); 2166 } 2167 } 2168 if (loggers.isEmpty()) continue; 2169 for (String pk : properties) { 2170 ConfigProperty cp = ConfigProperty.find(pk).get(); 2171 String p = previous.getProperty(pk, null); 2172 String n = next.getProperty(pk, null); 2173 2174 // Determines the type of modification. 2175 ModType mod = ModType.of(p, n); 2176 2177 // mod == SAME means that the two values are equals, there 2178 // is nothing to do. Usually, this should not happen as such 2179 // properties should have been filtered above. 2180 // It could happen however if the properties had 2181 // trailing/leading whitespaces. 2182 if (mod == ModType.SAME) continue; 2183 2184 switch (cp) { 2185 case LEVEL: 2186 if (mod == ModType.REMOVED) continue; 2187 Level level = Level.findLevel(trim(n)); 2188 if (level != null) { 2189 if (name.isEmpty()) { 2190 rootLogger.setLevel(level); 2191 } 2192 for (Logger l : loggers) { 2193 if (!name.isEmpty() || l != rootLogger) { 2194 l.setLevel(level); 2195 } 2196 } 2197 } 2198 break; 2199 case USEPARENT: 2200 if (!name.isEmpty()) { 2201 boolean useParent = getBooleanProperty(pk, true); 2202 if (n != null || p != null) { 2203 // reset the flag only if the previous value 2204 // or the new value are not null. 2205 for (Logger l : loggers) { 2206 l.setUseParentHandlers(useParent); 2207 } 2208 } 2209 } 2210 break; 2211 case HANDLERS: 2212 List<Handler> hdls = null; 2213 if (name.isEmpty()) { 2214 // special handling for the root logger. 2215 globalHandlersState = STATE_READING_CONFIG; 2216 try { 2217 closeHandlers(rootLogger); 2218 globalHandlersState = STATE_UNINITIALIZED; 2219 } catch (Throwable t) { 2220 globalHandlersState = STATE_INITIALIZED; 2221 throw t; 2222 } 2223 } 2224 for (Logger l : loggers) { 2225 if (l == rootLogger) continue; 2226 closeHandlers(l); 2227 if (mod == ModType.REMOVED) { 2228 closeOnResetLoggers.removeIf(c -> c.logger == l); 2229 continue; 2230 } 2231 if (hdls == null) { 2232 hdls = name.isEmpty() 2233 ? Arrays.asList(rootLogger.getHandlers()) 2234 : createLoggerHandlers(name, pk); 2235 } 2236 setLoggerHandlers(l, name, pk, hdls); 2237 } 2238 break; 2239 default: break; 2240 } 2241 } 2242 } 2243 } finally { 2244 configurationLock.unlock(); 2245 visited.clear(); 2246 } 2247 2248 // Now ensure that if an existing logger has acquired a new parent 2249 // in the configuration, this new parent will be created - if needed, 2250 // and added to the context of the existing child. 2251 // 2252 drainLoggerRefQueueBounded(); 2253 for (LoggerContext cx : cxs) { 2254 for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) { 2255 String name = names.nextElement(); 2256 if (name.isEmpty()) continue; // don't need to process parents on root. 2257 Logger l = cx.findLogger(name); 2258 if (l != null && !visited.test(l)) { 2259 // should pass visited here to cut the processing when 2260 // reaching a logger already visited. 2261 cx.processParentHandlers(l, name, visited); 2262 } 2263 } 2264 } 2265 2266 // We changed the configuration: invoke configuration listeners 2267 invokeConfigurationListeners(); 2268 } 2269 2270 /** 2271 * Get the value of a logging property. 2272 * The method returns null if the property is not found. 2273 * @param name property name 2274 * @return property value 2275 */ getProperty(String name)2276 public String getProperty(String name) { 2277 return props.getProperty(name); 2278 } 2279 2280 // Package private method to get a String property. 2281 // If the property is not defined we return the given 2282 // default value. getStringProperty(String name, String defaultValue)2283 String getStringProperty(String name, String defaultValue) { 2284 String val = getProperty(name); 2285 if (val == null) { 2286 return defaultValue; 2287 } 2288 return val.trim(); 2289 } 2290 2291 // Package private method to get an integer property. 2292 // If the property is not defined or cannot be parsed 2293 // we return the given default value. getIntProperty(String name, int defaultValue)2294 int getIntProperty(String name, int defaultValue) { 2295 String val = getProperty(name); 2296 if (val == null) { 2297 return defaultValue; 2298 } 2299 try { 2300 return Integer.parseInt(val.trim()); 2301 } catch (Exception ex) { 2302 return defaultValue; 2303 } 2304 } 2305 2306 // Package private method to get a long property. 2307 // If the property is not defined or cannot be parsed 2308 // we return the given default value. getLongProperty(String name, long defaultValue)2309 long getLongProperty(String name, long defaultValue) { 2310 String val = getProperty(name); 2311 if (val == null) { 2312 return defaultValue; 2313 } 2314 try { 2315 return Long.parseLong(val.trim()); 2316 } catch (Exception ex) { 2317 return defaultValue; 2318 } 2319 } 2320 2321 // Package private method to get a boolean property. 2322 // If the property is not defined or cannot be parsed 2323 // we return the given default value. getBooleanProperty(String name, boolean defaultValue)2324 boolean getBooleanProperty(String name, boolean defaultValue) { 2325 String val = getProperty(name); 2326 if (val == null) { 2327 return defaultValue; 2328 } 2329 val = val.toLowerCase(); 2330 if (val.equals("true") || val.equals("1")) { 2331 return true; 2332 } else if (val.equals("false") || val.equals("0")) { 2333 return false; 2334 } 2335 return defaultValue; 2336 } 2337 2338 // Package private method to get a Level property. 2339 // If the property is not defined or cannot be parsed 2340 // we return the given default value. getLevelProperty(String name, Level defaultValue)2341 Level getLevelProperty(String name, Level defaultValue) { 2342 String val = getProperty(name); 2343 if (val == null) { 2344 return defaultValue; 2345 } 2346 Level l = Level.findLevel(val.trim()); 2347 return l != null ? l : defaultValue; 2348 } 2349 2350 // Package private method to get a filter property. 2351 // We return an instance of the class named by the "name" 2352 // property. If the property is not defined or has problems 2353 // we return the defaultValue. getFilterProperty(String name, Filter defaultValue)2354 Filter getFilterProperty(String name, Filter defaultValue) { 2355 String val = getProperty(name); 2356 try { 2357 if (val != null) { 2358 @SuppressWarnings("deprecation") 2359 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance(); 2360 return (Filter) o; 2361 } 2362 } catch (Exception ex) { 2363 // We got one of a variety of exceptions in creating the 2364 // class or creating an instance. 2365 // Drop through. 2366 } 2367 // We got an exception. Return the defaultValue. 2368 return defaultValue; 2369 } 2370 2371 2372 // Package private method to get a formatter property. 2373 // We return an instance of the class named by the "name" 2374 // property. If the property is not defined or has problems 2375 // we return the defaultValue. getFormatterProperty(String name, Formatter defaultValue)2376 Formatter getFormatterProperty(String name, Formatter defaultValue) { 2377 String val = getProperty(name); 2378 try { 2379 if (val != null) { 2380 @SuppressWarnings("deprecation") 2381 Object o = ClassLoader.getSystemClassLoader().loadClass(val).newInstance(); 2382 return (Formatter) o; 2383 } 2384 } catch (Exception ex) { 2385 // We got one of a variety of exceptions in creating the 2386 // class or creating an instance. 2387 // Drop through. 2388 } 2389 // We got an exception. Return the defaultValue. 2390 return defaultValue; 2391 } 2392 2393 // Private method to load the global handlers. 2394 // We do the real work lazily, when the global handlers 2395 // are first used. initializeGlobalHandlers()2396 private void initializeGlobalHandlers() { 2397 int state = globalHandlersState; 2398 if (state == STATE_INITIALIZED || 2399 state == STATE_SHUTDOWN) { 2400 // Nothing to do: return. 2401 return; 2402 } 2403 2404 // If we have not initialized global handlers yet (or need to 2405 // reinitialize them), lets do it now (this case is indicated by 2406 // globalHandlersState == STATE_UNINITIALIZED). 2407 // If we are in the process of initializing global handlers we 2408 // also need to lock & wait (this case is indicated by 2409 // globalHandlersState == STATE_INITIALIZING). 2410 // If we are in the process of reading configuration we also need to 2411 // wait to see what the outcome will be (this case 2412 // is indicated by globalHandlersState == STATE_READING_CONFIG) 2413 // So in either case we need to wait for the lock. 2414 configurationLock.lock(); 2415 try { 2416 if (globalHandlersState != STATE_UNINITIALIZED) { 2417 return; // recursive call or nothing to do 2418 } 2419 // set globalHandlersState to STATE_INITIALIZING first to avoid 2420 // getting an infinite recursion when loadLoggerHandlers(...) 2421 // is going to call addHandler(...) 2422 globalHandlersState = STATE_INITIALIZING; 2423 try { 2424 loadLoggerHandlers(rootLogger, null, "handlers"); 2425 } finally { 2426 globalHandlersState = STATE_INITIALIZED; 2427 } 2428 } finally { 2429 configurationLock.unlock(); 2430 } 2431 } 2432 2433 static final Permission controlPermission = 2434 new LoggingPermission("control", null); 2435 checkPermission()2436 void checkPermission() { 2437 @SuppressWarnings("removal") 2438 SecurityManager sm = System.getSecurityManager(); 2439 if (sm != null) 2440 sm.checkPermission(controlPermission); 2441 } 2442 2443 /** 2444 * Check that the current context is trusted to modify the logging 2445 * configuration. This requires LoggingPermission("control"). 2446 * <p> 2447 * If the check fails we throw a SecurityException, otherwise 2448 * we return normally. 2449 * 2450 * @throws SecurityException if a security manager exists and if 2451 * the caller does not have LoggingPermission("control"). 2452 * @deprecated This method is only useful in conjunction with 2453 * {@linkplain SecurityManager the Security Manager}, which is 2454 * deprecated and subject to removal in a future release. 2455 * Consequently, this method is also deprecated and subject to 2456 * removal. There is no replacement for the Security Manager or this 2457 * method. 2458 */ 2459 @Deprecated(since="17", forRemoval=true) checkAccess()2460 public void checkAccess() throws SecurityException { 2461 checkPermission(); 2462 } 2463 2464 // Nested class to represent a node in our tree of named loggers. 2465 private static class LogNode { 2466 HashMap<String,LogNode> children; 2467 LoggerWeakRef loggerRef; 2468 LogNode parent; 2469 final LoggerContext context; 2470 LogNode(LogNode parent, LoggerContext context)2471 LogNode(LogNode parent, LoggerContext context) { 2472 this.parent = parent; 2473 this.context = context; 2474 } 2475 2476 // Recursive method to walk the tree below a node and set 2477 // a new parent logger. walkAndSetParent(Logger parent)2478 void walkAndSetParent(Logger parent) { 2479 if (children == null) { 2480 return; 2481 } 2482 for (LogNode node : children.values()) { 2483 LoggerWeakRef ref = node.loggerRef; 2484 Logger logger = (ref == null) ? null : ref.get(); 2485 if (logger == null) { 2486 node.walkAndSetParent(parent); 2487 } else { 2488 doSetParent(logger, parent); 2489 } 2490 } 2491 } 2492 } 2493 2494 // We use a subclass of Logger for the root logger, so 2495 // that we only instantiate the global handlers when they 2496 // are first needed. 2497 private final class RootLogger extends Logger { RootLogger()2498 private RootLogger() { 2499 // We do not call the protected Logger two args constructor here, 2500 // to avoid calling LogManager.getLogManager() from within the 2501 // RootLogger constructor. 2502 super("", null, null, LogManager.this, true); 2503 } 2504 2505 @Override log(LogRecord record)2506 public void log(LogRecord record) { 2507 // Make sure that the global handlers have been instantiated. 2508 initializeGlobalHandlers(); 2509 super.log(record); 2510 } 2511 2512 @Override addHandler(Handler h)2513 public void addHandler(Handler h) { 2514 initializeGlobalHandlers(); 2515 super.addHandler(h); 2516 } 2517 2518 @Override removeHandler(Handler h)2519 public void removeHandler(Handler h) { 2520 initializeGlobalHandlers(); 2521 super.removeHandler(h); 2522 } 2523 2524 @Override accessCheckedHandlers()2525 Handler[] accessCheckedHandlers() { 2526 initializeGlobalHandlers(); 2527 return super.accessCheckedHandlers(); 2528 } 2529 } 2530 2531 2532 // Private method to be called when the configuration has 2533 // changed to apply any level settings to any pre-existing loggers. setLevelsOnExistingLoggers()2534 private void setLevelsOnExistingLoggers() { 2535 Enumeration<?> enum_ = props.propertyNames(); 2536 while (enum_.hasMoreElements()) { 2537 String key = (String)enum_.nextElement(); 2538 if (!key.endsWith(".level")) { 2539 // Not a level definition. 2540 continue; 2541 } 2542 int ix = key.length() - 6; 2543 String name = key.substring(0, ix); 2544 Level level = getLevelProperty(key, null); 2545 if (level == null) { 2546 System.err.println("Bad level value for property: " + key); 2547 continue; 2548 } 2549 for (LoggerContext cx : contexts()) { 2550 Logger l = cx.findLogger(name); 2551 if (l == null) { 2552 continue; 2553 } 2554 l.setLevel(level); 2555 } 2556 } 2557 } 2558 2559 /** 2560 * String representation of the 2561 * {@link javax.management.ObjectName} for the management interface 2562 * for the logging facility. 2563 * 2564 * @see java.lang.management.PlatformLoggingMXBean 2565 * 2566 * @since 1.5 2567 */ 2568 public static final String LOGGING_MXBEAN_NAME 2569 = "java.util.logging:type=Logging"; 2570 2571 /** 2572 * Returns {@code LoggingMXBean} for managing loggers. 2573 * 2574 * @return a {@link LoggingMXBean} object. 2575 * 2576 * @deprecated {@code java.util.logging.LoggingMXBean} is deprecated and 2577 * replaced with {@code java.lang.management.PlatformLoggingMXBean}. Use 2578 * {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 2579 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class) 2580 * instead. 2581 * 2582 * @see java.lang.management.PlatformLoggingMXBean 2583 * @since 1.5 2584 */ 2585 @Deprecated(since="9") getLoggingMXBean()2586 public static synchronized LoggingMXBean getLoggingMXBean() { 2587 return Logging.getInstance(); 2588 } 2589 2590 /** 2591 * Adds a configuration listener to be invoked each time the logging 2592 * configuration is read. 2593 * If the listener is already registered the method does nothing. 2594 * <p> 2595 * The listener is invoked with privileges that are restricted by the 2596 * calling context of this method. 2597 * The order in which the listeners are invoked is unspecified. 2598 * <p> 2599 * It is recommended that listeners do not throw errors or exceptions. 2600 * 2601 * If a listener terminates with an uncaught error or exception then 2602 * the first exception will be propagated to the caller of 2603 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)}) 2604 * after all listeners have been invoked. 2605 * 2606 * @implNote If more than one listener terminates with an uncaught error or 2607 * exception, an implementation may record the additional errors or 2608 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable) 2609 * suppressed exceptions}. 2610 * 2611 * @param listener A configuration listener that will be invoked after the 2612 * configuration changed. 2613 * @return This LogManager. 2614 * @throws SecurityException if a security manager exists and if the 2615 * caller does not have LoggingPermission("control"). 2616 * @throws NullPointerException if the listener is null. 2617 * 2618 * @since 9 2619 */ addConfigurationListener(Runnable listener)2620 public LogManager addConfigurationListener(Runnable listener) { 2621 final Runnable r = Objects.requireNonNull(listener); 2622 checkPermission(); 2623 @SuppressWarnings("removal") 2624 final SecurityManager sm = System.getSecurityManager(); 2625 @SuppressWarnings("removal") 2626 final AccessControlContext acc = 2627 sm == null ? null : AccessController.getContext(); 2628 final PrivilegedAction<Void> pa = 2629 acc == null ? null : () -> { r.run() ; return null; }; 2630 @SuppressWarnings("removal") 2631 final Runnable pr = 2632 acc == null ? r : () -> AccessController.doPrivileged(pa, acc); 2633 // Will do nothing if already registered. 2634 listeners.putIfAbsent(r, pr); 2635 return this; 2636 } 2637 2638 /** 2639 * Removes a previously registered configuration listener. 2640 * 2641 * Returns silently if the listener is not found. 2642 * 2643 * @param listener the configuration listener to remove. 2644 * @throws NullPointerException if the listener is null. 2645 * @throws SecurityException if a security manager exists and if the 2646 * caller does not have LoggingPermission("control"). 2647 * 2648 * @since 9 2649 */ removeConfigurationListener(Runnable listener)2650 public void removeConfigurationListener(Runnable listener) { 2651 final Runnable key = Objects.requireNonNull(listener); 2652 checkPermission(); 2653 listeners.remove(key); 2654 } 2655 invokeConfigurationListeners()2656 private void invokeConfigurationListeners() { 2657 Throwable t = null; 2658 2659 // We're using an IdentityHashMap because we want to compare 2660 // keys using identity (==). 2661 // We don't want to loop within a block synchronized on 'listeners' 2662 // to avoid invoking listeners from yet another synchronized block. 2663 // So we're taking a snapshot of the values list to avoid the risk of 2664 // ConcurrentModificationException while looping. 2665 // 2666 for (Runnable c : listeners.values().toArray(new Runnable[0])) { 2667 try { 2668 c.run(); 2669 } catch (ThreadDeath death) { 2670 throw death; 2671 } catch (Error | RuntimeException x) { 2672 if (t == null) t = x; 2673 else t.addSuppressed(x); 2674 } 2675 } 2676 // Listeners are not supposed to throw exceptions, but if that 2677 // happens, we will rethrow the first error or exception that is raised 2678 // after all listeners have been invoked. 2679 if (t instanceof Error) throw (Error)t; 2680 if (t instanceof RuntimeException) throw (RuntimeException)t; 2681 } 2682 2683 /** 2684 * This class allows the {@link LoggingProviderImpl} to demand loggers on 2685 * behalf of system and application classes. 2686 */ 2687 private static final class LoggingProviderAccess 2688 implements LoggingProviderImpl.LogManagerAccess, 2689 PrivilegedAction<Void> { 2690 LoggingProviderAccess()2691 private LoggingProviderAccess() { 2692 } 2693 2694 /** 2695 * Demands a logger on behalf of the given {@code module}. 2696 * <p> 2697 * If a named logger suitable for the given module is found 2698 * returns it. 2699 * Otherwise, creates a new logger suitable for the given module. 2700 * 2701 * @param name The logger name. 2702 * @param module The module on which behalf the logger is created/retrieved. 2703 * @return A logger for the given {@code module}. 2704 * 2705 * @throws NullPointerException if {@code name} is {@code null} 2706 * or {@code module} is {@code null}. 2707 * @throws IllegalArgumentException if {@code manager} is not the default 2708 * LogManager. 2709 * @throws SecurityException if a security manager is present and the 2710 * calling code doesn't have the 2711 * {@link LoggingPermission LoggingPermission("demandLogger", null)}. 2712 */ 2713 @Override demandLoggerFor(LogManager manager, String name, Module module)2714 public Logger demandLoggerFor(LogManager manager, String name, Module module) { 2715 if (manager != getLogManager()) { 2716 // having LogManager as parameter just ensures that the 2717 // caller will have initialized the LogManager before reaching 2718 // here. 2719 throw new IllegalArgumentException("manager"); 2720 } 2721 Objects.requireNonNull(name); 2722 Objects.requireNonNull(module); 2723 @SuppressWarnings("removal") 2724 SecurityManager sm = System.getSecurityManager(); 2725 if (sm != null) { 2726 sm.checkPermission(controlPermission); 2727 } 2728 if (isSystem(module)) { 2729 return manager.demandSystemLogger(name, 2730 Logger.SYSTEM_LOGGER_RB_NAME, module); 2731 } else { 2732 return manager.demandLogger(name, null, module); 2733 } 2734 } 2735 2736 @Override run()2737 public Void run() { 2738 LoggingProviderImpl.setLogManagerAccess(INSTANCE); 2739 return null; 2740 } 2741 2742 static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess(); 2743 } 2744 2745 static { initStatic()2746 initStatic(); 2747 } 2748 2749 @SuppressWarnings("removal") initStatic()2750 private static void initStatic() { 2751 AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null, 2752 controlPermission); 2753 } 2754 2755 } 2756