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