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