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