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