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