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