1 /* 2 * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package java.util.logging; 28 29 import java.io.*; 30 import java.util.*; 31 import java.security.*; 32 import java.lang.ref.ReferenceQueue; 33 import java.lang.ref.WeakReference; 34 import java.util.concurrent.ConcurrentHashMap; 35 import java.util.concurrent.CopyOnWriteArrayList; 36 import java.util.concurrent.locks.ReentrantLock; 37 import sun.misc.JavaAWTAccess; 38 import sun.misc.SharedSecrets; 39 40 /** 41 * There is a single global LogManager object that is used to 42 * maintain a set of shared state about Loggers and log services. 43 * <p> 44 * This LogManager object: 45 * <ul> 46 * <li> Manages a hierarchical namespace of Logger objects. All 47 * named Loggers are stored in this namespace. 48 * <li> Manages a set of logging control properties. These are 49 * simple key-value pairs that can be used by Handlers and 50 * other logging objects to configure themselves. 51 * </ul> 52 * <p> 53 * The global LogManager object can be retrieved using LogManager.getLogManager(). 54 * The LogManager object is created during class initialization and 55 * cannot subsequently be changed. 56 * <p> 57 * At startup the LogManager class is located using the 58 * java.util.logging.manager system property. 59 * <p> 60 * The LogManager defines two optional system properties that allow control over 61 * the initial configuration: 62 * <ul> 63 * <li>"java.util.logging.config.class" 64 * <li>"java.util.logging.config.file" 65 * </ul> 66 * These two properties may be specified on the command line to the "java" 67 * command, or as system property definitions passed to JNI_CreateJavaVM. 68 * <p> 69 * If the "java.util.logging.config.class" property is set, then the 70 * property value is treated as a class name. The given class will be 71 * loaded, an object will be instantiated, and that object's constructor 72 * is responsible for reading in the initial configuration. (That object 73 * may use other system properties to control its configuration.) The 74 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 75 * to define properties in the LogManager. 76 * <p> 77 * If "java.util.logging.config.class" property is <b>not</b> set, 78 * then the "java.util.logging.config.file" system property can be used 79 * to specify a properties file (in java.util.Properties format). The 80 * initial logging configuration will be read from this file. 81 * <p> 82 * If neither of these properties is defined then the LogManager uses its 83 * default configuration. The default configuration is typically loaded from the 84 * properties file "{@code conf/logging.properties}" in the Java installation 85 * directory. 86 * <p> 87 * The properties for loggers and Handlers will have names starting 88 * with the dot-separated name for the handler or logger. 89 * <p> 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_UNINITIALIZED = 2, 188 STATE_SHUTDOWN = 3; // terminal state 189 private volatile int globalHandlersState; // = STATE_INITIALIZED; 190 // A concurrency lock for reset(), readConfiguration() and Cleaner. 191 private final ReentrantLock configurationLock = new ReentrantLock(); 192 193 // This list contains the loggers for which some handlers have been 194 // explicitly configured in the configuration file. 195 // It prevents these loggers from being arbitrarily garbage collected. 196 private static final class CloseOnReset { 197 private final Logger logger; 198 private CloseOnReset(Logger ref) { 199 this.logger = Objects.requireNonNull(ref); 200 } 201 @Override 202 public boolean equals(Object other) { 203 return (other instanceof CloseOnReset) && ((CloseOnReset)other).logger == logger; 204 } 205 @Override 206 public int hashCode() { 207 return System.identityHashCode(logger); 208 } 209 public Logger get() { 210 return logger; 211 } 212 public static CloseOnReset create(Logger logger) { 213 return new CloseOnReset(logger); 214 } 215 } 216 private final CopyOnWriteArrayList<CloseOnReset> closeOnResetLoggers = 217 new CopyOnWriteArrayList<>(); 218 219 220 private final Map<Object, Runnable> listeners = 221 Collections.synchronizedMap(new IdentityHashMap<>()); 222 223 static { 224 manager = AccessController.doPrivileged(new PrivilegedAction<LogManager>() { 225 @Override 226 public LogManager run() { 227 LogManager mgr = null; 228 String cname = null; 229 try { 230 cname = System.getProperty("java.util.logging.manager"); 231 if (cname != null) { 232 try { 233 Class<?> clz = ClassLoader.getSystemClassLoader() 234 .loadClass(cname); 235 mgr = (LogManager) clz.newInstance(); 236 } catch (ClassNotFoundException ex) { 237 Class<?> clz = Thread.currentThread() 238 .getContextClassLoader().loadClass(cname); 239 mgr = (LogManager) clz.newInstance(); 240 } 241 } 242 } catch (Exception ex) { 243 System.err.println("Could not load Logmanager \"" + cname + "\""); 244 ex.printStackTrace(); 245 } 246 if (mgr == null) { 247 mgr = new LogManager(); 248 } 249 return mgr; 250 251 } 252 }); 253 } 254 255 // This private class is used as a shutdown hook. 256 // It does a "reset" to close all open handlers. 257 private class Cleaner extends Thread { 258 259 private Cleaner() { 260 /* Set context class loader to null in order to avoid 261 * keeping a strong reference to an application classloader. 262 */ 263 this.setContextClassLoader(null); 264 } 265 266 @Override 267 public void run() { 268 // This is to ensure the LogManager.<clinit> is completed 269 // before synchronized block. Otherwise deadlocks are possible. 270 LogManager mgr = manager; 271 272 // Do a reset to close all active handlers and set 273 // globalHandlersState to a final STATE_SHUTDOWN so 274 // that no attempts will be made to initialize them again. 275 reset(STATE_SHUTDOWN); 276 } 277 } 278 279 280 /** 281 * Protected constructor. This is protected so that container applications 282 * (such as J2EE containers) can subclass the object. It is non-public as 283 * it is intended that there only be one LogManager object, whose value is 284 * retrieved by calling LogManager.getLogManager. 285 */ 286 protected LogManager() { 287 this(checkSubclassPermissions()); 288 } 289 290 private LogManager(Void checked) { 291 292 // Add a shutdown hook to close the global handlers. 293 try { 294 Runtime.getRuntime().addShutdownHook(new Cleaner()); 295 } catch (IllegalStateException e) { 296 // If the VM is already shutting down, 297 // We do not need to register shutdownHook. 298 } 299 } 300 301 private static Void checkSubclassPermissions() { 302 final SecurityManager sm = System.getSecurityManager(); 303 if (sm != null) { 304 // These permission will be checked in the LogManager constructor, 305 // in order to register the Cleaner() thread as a shutdown hook. 306 // Check them here to avoid the penalty of constructing the object 307 // etc... 308 sm.checkPermission(new RuntimePermission("shutdownHooks")); 309 sm.checkPermission(new RuntimePermission("setContextClassLoader")); 310 } 311 return null; 312 } 313 314 /** 315 * Lazy initialization: if this instance of manager is the global 316 * manager then this method will read the initial configuration and 317 * add the root logger and global logger by calling addLogger(). 318 * 319 * Note that it is subtly different from what we do in LoggerContext. 320 * In LoggerContext we're patching up the logger context tree in order to add 321 * the root and global logger *to the context tree*. 322 * 323 * For this to work, addLogger() must have already have been called 324 * once on the LogManager instance for the default logger being 325 * added. 326 * 327 * This is why ensureLogManagerInitialized() needs to be called before 328 * any logger is added to any logger context. 329 * 330 */ 331 private boolean initializedCalled = false; 332 private volatile boolean initializationDone = false; 333 final void ensureLogManagerInitialized() { 334 final LogManager owner = this; 335 if (initializationDone || owner != manager) { 336 // we don't want to do this twice, and we don't want to do 337 // this on private manager instances. 338 return; 339 } 340 341 // Maybe another thread has called ensureLogManagerInitialized() 342 // before us and is still executing it. If so we will block until 343 // the log manager has finished initialized, then acquire the monitor, 344 // notice that initializationDone is now true and return. 345 // Otherwise - we have come here first! We will acquire the monitor, 346 // see that initializationDone is still false, and perform the 347 // initialization. 348 // 349 synchronized(this) { 350 // If initializedCalled is true it means that we're already in 351 // the process of initializing the LogManager in this thread. 352 // There has been a recursive call to ensureLogManagerInitialized(). 353 final boolean isRecursiveInitialization = (initializedCalled == true); 354 355 assert initializedCalled || !initializationDone 356 : "Initialization can't be done if initialized has not been called!"; 357 358 if (isRecursiveInitialization || initializationDone) { 359 // If isRecursiveInitialization is true it means that we're 360 // already in the process of initializing the LogManager in 361 // this thread. There has been a recursive call to 362 // ensureLogManagerInitialized(). We should not proceed as 363 // it would lead to infinite recursion. 364 // 365 // If initializationDone is true then it means the manager 366 // has finished initializing; just return: we're done. 367 return; 368 } 369 // Calling addLogger below will in turn call requiresDefaultLogger() 370 // which will call ensureLogManagerInitialized(). 371 // We use initializedCalled to break the recursion. 372 initializedCalled = true; 373 try { 374 AccessController.doPrivileged(new PrivilegedAction<Object>() { 375 @Override 376 public Object run() { 377 assert rootLogger == null; 378 assert initializedCalled && !initializationDone; 379 380 // Read configuration. 381 owner.readPrimordialConfiguration(); 382 383 // Create and retain Logger for the root of the namespace. 384 owner.rootLogger = owner.new RootLogger(); 385 owner.addLogger(owner.rootLogger); 386 if (!owner.rootLogger.isLevelInitialized()) { 387 owner.rootLogger.setLevel(defaultLevel); 388 } 389 390 // Adding the global Logger. 391 // Do not call Logger.getGlobal() here as this might trigger 392 // subtle inter-dependency issues. 393 @SuppressWarnings("deprecation") 394 final Logger global = Logger.global; 395 396 // Make sure the global logger will be registered in the 397 // global manager 398 owner.addLogger(global); 399 return null; 400 } 401 }); 402 } finally { 403 initializationDone = true; 404 } 405 } 406 } 407 408 /** 409 * Returns the global LogManager object. 410 * @return the global LogManager object 411 */ 412 public static LogManager getLogManager() { 413 if (manager != null) { 414 manager.ensureLogManagerInitialized(); 415 } 416 return manager; 417 } 418 419 private void readPrimordialConfiguration() { 420 if (!readPrimordialConfiguration) { 421 synchronized (this) { 422 if (!readPrimordialConfiguration) { 423 // If System.in/out/err are null, it's a good 424 // indication that we're still in the 425 // bootstrapping phase 426 if (System.out == null) { 427 return; 428 } 429 readPrimordialConfiguration = true; 430 431 try { 432 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 433 @Override 434 public Void run() throws Exception { 435 readConfiguration(); 436 437 // Platform loggers begin to delegate to java.util.logging.Logger 438 sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 439 return null; 440 } 441 }); 442 } catch (Exception ex) { 443 assert false : "Exception raised while reading logging configuration: " + ex; 444 } 445 } 446 } 447 } 448 } 449 450 // LoggerContext maps from AppContext 451 private WeakHashMap<Object, LoggerContext> contextsMap = null; 452 453 // Returns the LoggerContext for the user code (i.e. application or AppContext). 454 // Loggers are isolated from each AppContext. 455 private LoggerContext getUserContext() { 456 LoggerContext context = null; 457 458 SecurityManager sm = System.getSecurityManager(); 459 JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess(); 460 if (sm != null && javaAwtAccess != null) { 461 // for each applet, it has its own LoggerContext isolated from others 462 final Object ecx = javaAwtAccess.getAppletContext(); 463 if (ecx != null) { 464 synchronized (javaAwtAccess) { 465 // find the AppContext of the applet code 466 // will be null if we are in the main app context. 467 if (contextsMap == null) { 468 contextsMap = new WeakHashMap<>(); 469 } 470 context = contextsMap.get(ecx); 471 if (context == null) { 472 // Create a new LoggerContext for the applet. 473 context = new LoggerContext(); 474 contextsMap.put(ecx, context); 475 } 476 } 477 } 478 } 479 // for standalone app, return userContext 480 return context != null ? context : userContext; 481 } 482 483 // The system context. 484 final LoggerContext getSystemContext() { 485 return systemContext; 486 } 487 488 private List<LoggerContext> contexts() { 489 List<LoggerContext> cxs = new ArrayList<>(); 490 cxs.add(getSystemContext()); 491 cxs.add(getUserContext()); 492 return cxs; 493 } 494 495 // Find or create a specified logger instance. If a logger has 496 // already been created with the given name it is returned. 497 // Otherwise a new logger instance is created and registered 498 // in the LogManager global namespace. 499 // This method will always return a non-null Logger object. 500 // Synchronization is not required here. All synchronization for 501 // adding a new Logger object is handled by addLogger(). 502 // 503 // This method must delegate to the LogManager implementation to 504 // add a new Logger or return the one that has been added previously 505 // as a LogManager subclass may override the addLogger, getLogger, 506 // readConfiguration, and other methods. 507 Logger demandLogger(String name, String resourceBundleName, Class<?> caller) { 508 Logger result = getLogger(name); 509 if (result == null) { 510 // only allocate the new logger once 511 Logger newLogger = new Logger(name, resourceBundleName, caller, this, false); 512 do { 513 if (addLogger(newLogger)) { 514 // We successfully added the new Logger that we 515 // created above so return it without refetching. 516 return newLogger; 517 } 518 519 // We didn't add the new Logger that we created above 520 // because another thread added a Logger with the same 521 // name after our null check above and before our call 522 // to addLogger(). We have to refetch the Logger because 523 // addLogger() returns a boolean instead of the Logger 524 // reference itself. However, if the thread that created 525 // the other Logger is not holding a strong reference to 526 // the other Logger, then it is possible for the other 527 // Logger to be GC'ed after we saw it in addLogger() and 528 // before we can refetch it. If it has been GC'ed then 529 // we'll just loop around and try again. 530 result = getLogger(name); 531 } while (result == null); 532 } 533 return result; 534 } 535 536 Logger demandSystemLogger(String name, String resourceBundleName) { 537 // Add a system logger in the system context's namespace 538 final Logger sysLogger = getSystemContext().demandLogger(name, resourceBundleName); 539 540 // Add the system logger to the LogManager's namespace if not exist 541 // so that there is only one single logger of the given name. 542 // System loggers are visible to applications unless a logger of 543 // the same name has been added. 544 Logger logger; 545 do { 546 // First attempt to call addLogger instead of getLogger 547 // This would avoid potential bug in custom LogManager.getLogger 548 // implementation that adds a logger if does not exist 549 if (addLogger(sysLogger)) { 550 // successfully added the new system logger 551 logger = sysLogger; 552 } else { 553 logger = getLogger(name); 554 } 555 } while (logger == null); 556 557 // LogManager will set the sysLogger's handlers via LogManager.addLogger method. 558 if (logger != sysLogger && sysLogger.accessCheckedHandlers().length == 0) { 559 // if logger already exists but handlers not set 560 final Logger l = logger; 561 AccessController.doPrivileged(new PrivilegedAction<Void>() { 562 @Override 563 public Void run() { 564 for (Handler hdl : l.accessCheckedHandlers()) { 565 sysLogger.addHandler(hdl); 566 } 567 return null; 568 } 569 }); 570 } 571 return sysLogger; 572 } 573 574 // LoggerContext maintains the logger namespace per context. 575 // The default LogManager implementation has one system context and user 576 // context. The system context is used to maintain the namespace for 577 // all system loggers and is queried by the system code. If a system logger 578 // doesn't exist in the user context, it'll also be added to the user context. 579 // The user context is queried by the user code and all other loggers are 580 // added in the user context. 581 class LoggerContext { 582 // Table of named Loggers that maps names to Loggers. 583 private final ConcurrentHashMap<String,LoggerWeakRef> namedLoggers = 584 new ConcurrentHashMap<>(); 585 // Tree of named Loggers 586 private final LogNode root; 587 private LoggerContext() { 588 this.root = new LogNode(null, this); 589 } 590 591 592 // Tells whether default loggers are required in this context. 593 // If true, the default loggers will be lazily added. 594 final boolean requiresDefaultLoggers() { 595 final boolean requiresDefaultLoggers = (getOwner() == manager); 596 if (requiresDefaultLoggers) { 597 getOwner().ensureLogManagerInitialized(); 598 } 599 return requiresDefaultLoggers; 600 } 601 602 // This context's LogManager. 603 final LogManager getOwner() { 604 return LogManager.this; 605 } 606 607 // This context owner's root logger, which if not null, and if 608 // the context requires default loggers, will be added to the context 609 // logger's tree. 610 final Logger getRootLogger() { 611 return getOwner().rootLogger; 612 } 613 614 // The global logger, which if not null, and if 615 // the context requires default loggers, will be added to the context 616 // logger's tree. 617 final Logger getGlobalLogger() { 618 @SuppressWarnings("deprecation") // avoids initialization cycles. 619 final Logger global = Logger.global; 620 return global; 621 } 622 623 Logger demandLogger(String name, String resourceBundleName) { 624 // a LogManager subclass may have its own implementation to add and 625 // get a Logger. So delegate to the LogManager to do the work. 626 final LogManager owner = getOwner(); 627 return owner.demandLogger(name, resourceBundleName, null); 628 } 629 630 631 // Due to subtle deadlock issues getUserContext() no longer 632 // calls addLocalLogger(rootLogger); 633 // Therefore - we need to add the default loggers later on. 634 // Checks that the context is properly initialized 635 // This is necessary before calling e.g. find(name) 636 // or getLoggerNames() 637 // 638 private void ensureInitialized() { 639 if (requiresDefaultLoggers()) { 640 // Ensure that the root and global loggers are set. 641 ensureDefaultLogger(getRootLogger()); 642 ensureDefaultLogger(getGlobalLogger()); 643 } 644 } 645 646 647 Logger findLogger(String name) { 648 // Attempt to find logger without locking. 649 LoggerWeakRef ref = namedLoggers.get(name); 650 Logger logger = ref == null ? null : ref.get(); 651 652 // if logger is not null, then we can return it right away. 653 // if name is "" or "global" and logger is null 654 // we need to fall through and check that this context is 655 // initialized. 656 // if ref is not null and logger is null we also need to 657 // fall through. 658 if (logger != null || (ref == null && !name.isEmpty() 659 && !name.equals(Logger.GLOBAL_LOGGER_NAME))) { 660 return logger; 661 } 662 663 // We either found a stale reference, or we were looking for 664 // "" or "global" and didn't find them. 665 // Make sure context is initialized (has the default loggers), 666 // and look up again, cleaning the stale reference if it hasn't 667 // been cleaned up in between. All this needs to be done inside 668 // a synchronized block. 669 synchronized(this) { 670 // ensure that this context is properly initialized before 671 // looking for loggers. 672 ensureInitialized(); 673 ref = namedLoggers.get(name); 674 if (ref == null) { 675 return null; 676 } 677 logger = ref.get(); 678 if (logger == null) { 679 // The namedLoggers map holds stale weak reference 680 // to a logger which has been GC-ed. 681 ref.dispose(); 682 } 683 return logger; 684 } 685 } 686 687 // This method is called before adding a logger to the 688 // context. 689 // 'logger' is the context that will be added. 690 // This method will ensure that the defaults loggers are added 691 // before adding 'logger'. 692 // 693 private void ensureAllDefaultLoggers(Logger logger) { 694 if (requiresDefaultLoggers()) { 695 final String name = logger.getName(); 696 if (!name.isEmpty()) { 697 ensureDefaultLogger(getRootLogger()); 698 if (!Logger.GLOBAL_LOGGER_NAME.equals(name)) { 699 ensureDefaultLogger(getGlobalLogger()); 700 } 701 } 702 } 703 } 704 705 private void ensureDefaultLogger(Logger logger) { 706 // Used for lazy addition of root logger and global logger 707 // to a LoggerContext. 708 709 // This check is simple sanity: we do not want that this 710 // method be called for anything else than Logger.global 711 // or owner.rootLogger. 712 if (!requiresDefaultLoggers() || logger == null 713 || logger != getGlobalLogger() && logger != LogManager.this.rootLogger ) { 714 715 // the case where we have a non null logger which is neither 716 // Logger.global nor manager.rootLogger indicates a serious 717 // issue - as ensureDefaultLogger should never be called 718 // with any other loggers than one of these two (or null - if 719 // e.g manager.rootLogger is not yet initialized)... 720 assert logger == null; 721 722 return; 723 } 724 725 // Adds the logger if it's not already there. 726 if (!namedLoggers.containsKey(logger.getName())) { 727 // It is important to prevent addLocalLogger to 728 // call ensureAllDefaultLoggers when we're in the process 729 // off adding one of those default loggers - as this would 730 // immediately cause a stack overflow. 731 // Therefore we must pass addDefaultLoggersIfNeeded=false, 732 // even if requiresDefaultLoggers is true. 733 addLocalLogger(logger, false); 734 } 735 } 736 737 boolean addLocalLogger(Logger logger) { 738 // no need to add default loggers if it's not required 739 return addLocalLogger(logger, requiresDefaultLoggers()); 740 } 741 742 // Add a logger to this context. This method will only set its level 743 // and process parent loggers. It doesn't set its handlers. 744 synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded) { 745 // addDefaultLoggersIfNeeded serves to break recursion when adding 746 // default loggers. If we're adding one of the default loggers 747 // (we're being called from ensureDefaultLogger()) then 748 // addDefaultLoggersIfNeeded will be false: we don't want to 749 // call ensureAllDefaultLoggers again. 750 // 751 // Note: addDefaultLoggersIfNeeded can also be false when 752 // requiresDefaultLoggers is false - since calling 753 // ensureAllDefaultLoggers would have no effect in this case. 754 if (addDefaultLoggersIfNeeded) { 755 ensureAllDefaultLoggers(logger); 756 } 757 758 final String name = logger.getName(); 759 if (name == null) { 760 throw new NullPointerException(); 761 } 762 LoggerWeakRef ref = namedLoggers.get(name); 763 if (ref != null) { 764 if (ref.get() == null) { 765 // It's possible that the Logger was GC'ed after a 766 // drainLoggerRefQueueBounded() call above so allow 767 // a new one to be registered. 768 ref.dispose(); 769 } else { 770 // We already have a registered logger with the given name. 771 return false; 772 } 773 } 774 775 // We're adding a new logger. 776 // Note that we are creating a weak reference here. 777 final LogManager owner = getOwner(); 778 logger.setLogManager(owner); 779 ref = owner.new LoggerWeakRef(logger); 780 781 // Apply any initial level defined for the new logger, unless 782 // the logger's level is already initialized 783 Level level = owner.getLevelProperty(name + ".level", null); 784 if (level != null && !logger.isLevelInitialized()) { 785 doSetLevel(logger, level); 786 } 787 788 // instantiation of the handler is done in the LogManager.addLogger 789 // implementation as a handler class may be only visible to LogManager 790 // subclass for the custom log manager case 791 processParentHandlers(logger, name); 792 793 // Find the new node and its parent. 794 LogNode node = getNode(name); 795 node.loggerRef = ref; 796 Logger parent = null; 797 LogNode nodep = node.parent; 798 while (nodep != null) { 799 LoggerWeakRef nodeRef = nodep.loggerRef; 800 if (nodeRef != null) { 801 parent = nodeRef.get(); 802 if (parent != null) { 803 break; 804 } 805 } 806 nodep = nodep.parent; 807 } 808 809 if (parent != null) { 810 doSetParent(logger, parent); 811 } 812 // Walk over the children and tell them we are their new parent. 813 node.walkAndSetParent(logger); 814 // new LogNode is ready so tell the LoggerWeakRef about it 815 ref.setNode(node); 816 817 // Do not publish 'ref' in namedLoggers before the logger tree 818 // is fully updated - because the named logger will be visible as 819 // soon as it is published in namedLoggers (findLogger takes 820 // benefit of the ConcurrentHashMap implementation of namedLoggers 821 // to avoid synchronizing on retrieval when that is possible). 822 namedLoggers.put(name, ref); 823 return true; 824 } 825 826 void removeLoggerRef(String name, LoggerWeakRef ref) { 827 namedLoggers.remove(name, ref); 828 } 829 830 synchronized Enumeration<String> getLoggerNames() { 831 // ensure that this context is properly initialized before 832 // returning logger names. 833 ensureInitialized(); 834 return Collections.enumeration(namedLoggers.keySet()); 835 } 836 837 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 838 // parents have levels or handlers defined, make sure they are instantiated. 839 private void processParentHandlers(final Logger logger, final String name) { 840 final LogManager owner = getOwner(); 841 AccessController.doPrivileged(new PrivilegedAction<Void>() { 842 @Override 843 public Void run() { 844 if (logger != owner.rootLogger) { 845 boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true); 846 if (!useParent) { 847 logger.setUseParentHandlers(false); 848 } 849 } 850 return null; 851 } 852 }); 853 854 int ix = 1; 855 for (;;) { 856 int ix2 = name.indexOf('.', ix); 857 if (ix2 < 0) { 858 break; 859 } 860 String pname = name.substring(0, ix2); 861 if (owner.getProperty(pname + ".level") != null || 862 owner.getProperty(pname + ".handlers") != null) { 863 // This pname has a level/handlers definition. 864 // Make sure it exists. 865 demandLogger(pname, null); 866 } 867 ix = ix2+1; 868 } 869 } 870 871 // Gets a node in our tree of logger nodes. 872 // If necessary, create it. 873 LogNode getNode(String name) { 874 if (name == null || name.equals("")) { 875 return root; 876 } 877 LogNode node = root; 878 while (name.length() > 0) { 879 int ix = name.indexOf('.'); 880 String head; 881 if (ix > 0) { 882 head = name.substring(0, ix); 883 name = name.substring(ix + 1); 884 } else { 885 head = name; 886 name = ""; 887 } 888 if (node.children == null) { 889 node.children = new HashMap<>(); 890 } 891 LogNode child = node.children.get(head); 892 if (child == null) { 893 child = new LogNode(node, this); 894 node.children.put(head, child); 895 } 896 node = child; 897 } 898 return node; 899 } 900 } 901 902 final class SystemLoggerContext extends LoggerContext { 903 // Add a system logger in the system context's namespace as well as 904 // in the LogManager's namespace if not exist so that there is only 905 // one single logger of the given name. System loggers are visible 906 // to applications unless a logger of the same name has been added. 907 @Override 908 Logger demandLogger(String name, String resourceBundleName) { 909 Logger result = findLogger(name); 910 if (result == null) { 911 // only allocate the new system logger once 912 Logger newLogger = new Logger(name, resourceBundleName, null, getOwner(), true); 913 do { 914 if (addLocalLogger(newLogger)) { 915 // We successfully added the new Logger that we 916 // created above so return it without refetching. 917 result = newLogger; 918 } else { 919 // We didn't add the new Logger that we created above 920 // because another thread added a Logger with the same 921 // name after our null check above and before our call 922 // to addLogger(). We have to refetch the Logger because 923 // addLogger() returns a boolean instead of the Logger 924 // reference itself. However, if the thread that created 925 // the other Logger is not holding a strong reference to 926 // the other Logger, then it is possible for the other 927 // Logger to be GC'ed after we saw it in addLogger() and 928 // before we can refetch it. If it has been GC'ed then 929 // we'll just loop around and try again. 930 result = findLogger(name); 931 } 932 } while (result == null); 933 } 934 return result; 935 } 936 } 937 938 // Add new per logger handlers. 939 // We need to raise privilege here. All our decisions will 940 // be made based on the logging configuration, which can 941 // only be modified by trusted code. 942 private void loadLoggerHandlers(final Logger logger, final String name, 943 final String handlersPropertyName) 944 { 945 AccessController.doPrivileged(new PrivilegedAction<Object>() { 946 @Override 947 public Object run() { 948 String names[] = parseClassNames(handlersPropertyName); 949 final boolean ensureCloseOnReset = names.length > 0 950 && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true); 951 952 int count = 0; 953 for (String type : names) { 954 try { 955 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type); 956 Handler hdl = (Handler) clz.newInstance(); 957 // Check if there is a property defining the 958 // this handler's level. 959 String levs = getProperty(type + ".level"); 960 if (levs != null) { 961 Level l = Level.findLevel(levs); 962 if (l != null) { 963 hdl.setLevel(l); 964 } else { 965 // Probably a bad level. Drop through. 966 System.err.println("Can't set level for " + type); 967 } 968 } 969 // Add this Handler to the logger 970 logger.addHandler(hdl); 971 if (++count == 1 && ensureCloseOnReset) { 972 // add this logger to the closeOnResetLoggers list. 973 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger)); 974 } 975 } catch (Exception ex) { 976 System.err.println("Can't load log handler \"" + type + "\""); 977 System.err.println("" + ex); 978 ex.printStackTrace(); 979 } 980 } 981 982 return null; 983 } 984 }); 985 } 986 987 988 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 989 // that have been GC'ed. 990 private final ReferenceQueue<Logger> loggerRefQueue 991 = new ReferenceQueue<>(); 992 993 // Package-level inner class. 994 // Helper class for managing WeakReferences to Logger objects. 995 // 996 // LogManager.namedLoggers 997 // - has weak references to all named Loggers 998 // - namedLoggers keeps the LoggerWeakRef objects for the named 999 // Loggers around until we can deal with the book keeping for 1000 // the named Logger that is being GC'ed. 1001 // LogManager.LogNode.loggerRef 1002 // - has a weak reference to a named Logger 1003 // - the LogNode will also keep the LoggerWeakRef objects for 1004 // the named Loggers around; currently LogNodes never go away. 1005 // Logger.kids 1006 // - has a weak reference to each direct child Logger; this 1007 // includes anonymous and named Loggers 1008 // - anonymous Loggers are always children of the rootLogger 1009 // which is a strong reference; rootLogger.kids keeps the 1010 // LoggerWeakRef objects for the anonymous Loggers around 1011 // until we can deal with the book keeping. 1012 // 1013 final class LoggerWeakRef extends WeakReference<Logger> { 1014 private String name; // for namedLoggers cleanup 1015 private LogNode node; // for loggerRef cleanup 1016 private WeakReference<Logger> parentRef; // for kids cleanup 1017 private boolean disposed = false; // avoid calling dispose twice 1018 1019 LoggerWeakRef(Logger logger) { 1020 super(logger, loggerRefQueue); 1021 1022 name = logger.getName(); // save for namedLoggers cleanup 1023 } 1024 1025 // dispose of this LoggerWeakRef object 1026 void dispose() { 1027 // Avoid calling dispose twice. When a Logger is gc'ed, its 1028 // LoggerWeakRef will be enqueued. 1029 // However, a new logger of the same name may be added (or looked 1030 // up) before the queue is drained. When that happens, dispose() 1031 // will be called by addLocalLogger() or findLogger(). 1032 // Later when the queue is drained, dispose() will be called again 1033 // for the same LoggerWeakRef. Marking LoggerWeakRef as disposed 1034 // avoids processing the data twice (even though the code should 1035 // now be reentrant). 1036 synchronized(this) { 1037 // Note to maintainers: 1038 // Be careful not to call any method that tries to acquire 1039 // another lock from within this block - as this would surely 1040 // lead to deadlocks, given that dispose() can be called by 1041 // multiple threads, and from within different synchronized 1042 // methods/blocks. 1043 if (disposed) return; 1044 disposed = true; 1045 } 1046 1047 final LogNode n = node; 1048 if (n != null) { 1049 // n.loggerRef can only be safely modified from within 1050 // a lock on LoggerContext. removeLoggerRef is already 1051 // synchronized on LoggerContext so calling 1052 // n.context.removeLoggerRef from within this lock is safe. 1053 synchronized (n.context) { 1054 // if we have a LogNode, then we were a named Logger 1055 // so clear namedLoggers weak ref to us 1056 n.context.removeLoggerRef(name, this); 1057 name = null; // clear our ref to the Logger's name 1058 1059 // LogNode may have been reused - so only clear 1060 // LogNode.loggerRef if LogNode.loggerRef == this 1061 if (n.loggerRef == this) { 1062 n.loggerRef = null; // clear LogNode's weak ref to us 1063 } 1064 node = null; // clear our ref to LogNode 1065 } 1066 } 1067 1068 if (parentRef != null) { 1069 // this LoggerWeakRef has or had a parent Logger 1070 Logger parent = parentRef.get(); 1071 if (parent != null) { 1072 // the parent Logger is still there so clear the 1073 // parent Logger's weak ref to us 1074 parent.removeChildLogger(this); 1075 } 1076 parentRef = null; // clear our weak ref to the parent Logger 1077 } 1078 } 1079 1080 // set the node field to the specified value 1081 void setNode(LogNode node) { 1082 this.node = node; 1083 } 1084 1085 // set the parentRef field to the specified value 1086 void setParentRef(WeakReference<Logger> parentRef) { 1087 this.parentRef = parentRef; 1088 } 1089 } 1090 1091 // Package-level method. 1092 // Drain some Logger objects that have been GC'ed. 1093 // 1094 // drainLoggerRefQueueBounded() is called by addLogger() below 1095 // and by Logger.getAnonymousLogger(String) so we'll drain up to 1096 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 1097 // 1098 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 1099 // us about a 50/50 mix in increased weak ref counts versus 1100 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 1101 // Here are stats for cleaning up sets of 400 anonymous Loggers: 1102 // - test duration 1 minute 1103 // - sample size of 125 sets of 400 1104 // - average: 1.99 ms 1105 // - minimum: 0.57 ms 1106 // - maximum: 25.3 ms 1107 // 1108 // The same config gives us a better decreased weak ref count 1109 // than increased weak ref count in the LoggerWeakRefLeak test. 1110 // Here are stats for cleaning up sets of 400 named Loggers: 1111 // - test duration 2 minutes 1112 // - sample size of 506 sets of 400 1113 // - average: 0.57 ms 1114 // - minimum: 0.02 ms 1115 // - maximum: 10.9 ms 1116 // 1117 private final static int MAX_ITERATIONS = 400; 1118 final void drainLoggerRefQueueBounded() { 1119 for (int i = 0; i < MAX_ITERATIONS; i++) { 1120 if (loggerRefQueue == null) { 1121 // haven't finished loading LogManager yet 1122 break; 1123 } 1124 1125 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 1126 if (ref == null) { 1127 break; 1128 } 1129 // a Logger object has been GC'ed so clean it up 1130 ref.dispose(); 1131 } 1132 } 1133 1134 /** 1135 * Add a named logger. This does nothing and returns false if a logger 1136 * with the same name is already registered. 1137 * <p> 1138 * The Logger factory methods call this method to register each 1139 * newly created Logger. 1140 * <p> 1141 * The application should retain its own reference to the Logger 1142 * object to avoid it being garbage collected. The LogManager 1143 * may only retain a weak reference. 1144 * 1145 * @param logger the new logger. 1146 * @return true if the argument logger was registered successfully, 1147 * false if a logger of that name already exists. 1148 * @exception NullPointerException if the logger name is null. 1149 */ 1150 public boolean addLogger(Logger logger) { 1151 final String name = logger.getName(); 1152 if (name == null) { 1153 throw new NullPointerException(); 1154 } 1155 drainLoggerRefQueueBounded(); 1156 LoggerContext cx = getUserContext(); 1157 if (cx.addLocalLogger(logger)) { 1158 // Do we have a per logger handler too? 1159 // Note: this will add a 200ms penalty 1160 loadLoggerHandlers(logger, name, name + ".handlers"); 1161 return true; 1162 } else { 1163 return false; 1164 } 1165 } 1166 1167 // Private method to set a level on a logger. 1168 // If necessary, we raise privilege before doing the call. 1169 private static void doSetLevel(final Logger logger, final Level level) { 1170 SecurityManager sm = System.getSecurityManager(); 1171 if (sm == null) { 1172 // There is no security manager, so things are easy. 1173 logger.setLevel(level); 1174 return; 1175 } 1176 // There is a security manager. Raise privilege before 1177 // calling setLevel. 1178 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1179 @Override 1180 public Object run() { 1181 logger.setLevel(level); 1182 return null; 1183 }}); 1184 } 1185 1186 // Private method to set a parent on a logger. 1187 // If necessary, we raise privilege before doing the setParent call. 1188 private static void doSetParent(final Logger logger, final Logger parent) { 1189 SecurityManager sm = System.getSecurityManager(); 1190 if (sm == null) { 1191 // There is no security manager, so things are easy. 1192 logger.setParent(parent); 1193 return; 1194 } 1195 // There is a security manager. Raise privilege before 1196 // calling setParent. 1197 AccessController.doPrivileged(new PrivilegedAction<Object>() { 1198 @Override 1199 public Object run() { 1200 logger.setParent(parent); 1201 return null; 1202 }}); 1203 } 1204 1205 /** 1206 * Method to find a named logger. 1207 * <p> 1208 * Note that since untrusted code may create loggers with 1209 * arbitrary names this method should not be relied on to 1210 * find Loggers for security sensitive logging. 1211 * It is also important to note that the Logger associated with the 1212 * String {@code name} may be garbage collected at any time if there 1213 * is no strong reference to the Logger. The caller of this method 1214 * must check the return value for null in order to properly handle 1215 * the case where the Logger has been garbage collected. 1216 * 1217 * @param name name of the logger 1218 * @return matching logger or null if none is found 1219 */ 1220 public Logger getLogger(String name) { 1221 return getUserContext().findLogger(name); 1222 } 1223 1224 /** 1225 * Get an enumeration of known logger names. 1226 * <p> 1227 * Note: Loggers may be added dynamically as new classes are loaded. 1228 * This method only reports on the loggers that are currently registered. 1229 * It is also important to note that this method only returns the name 1230 * of a Logger, not a strong reference to the Logger itself. 1231 * The returned String does nothing to prevent the Logger from being 1232 * garbage collected. In particular, if the returned name is passed 1233 * to {@code LogManager.getLogger()}, then the caller must check the 1234 * return value from {@code LogManager.getLogger()} for null to properly 1235 * handle the case where the Logger has been garbage collected in the 1236 * time since its name was returned by this method. 1237 * 1238 * @return enumeration of logger name strings 1239 */ 1240 public Enumeration<String> getLoggerNames() { 1241 return getUserContext().getLoggerNames(); 1242 } 1243 1244 /** 1245 * Reinitialize the logging properties and reread the logging configuration. 1246 * <p> 1247 * The same rules are used for locating the configuration properties 1248 * as are used at startup. So normally the logging properties will 1249 * be re-read from the same file that was used at startup. 1250 * <P> 1251 * Any log level definitions in the new configuration file will be 1252 * applied using Logger.setLevel(), if the target Logger exists. 1253 * <p> 1254 * Any {@linkplain #addConfigurationListener registered configuration 1255 * listener} will be invoked after the properties are read. 1256 * 1257 * @exception SecurityException if a security manager exists and if 1258 * the caller does not have LoggingPermission("control"). 1259 * @exception IOException if there are IO problems reading the configuration. 1260 */ 1261 public void readConfiguration() throws IOException, SecurityException { 1262 checkPermission(); 1263 1264 // if a configuration class is specified, load it and use it. 1265 String cname = System.getProperty("java.util.logging.config.class"); 1266 if (cname != null) { 1267 try { 1268 // Instantiate the named class. It is its constructor's 1269 // responsibility to initialize the logging configuration, by 1270 // calling readConfiguration(InputStream) with a suitable stream. 1271 try { 1272 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 1273 clz.newInstance(); 1274 return; 1275 } catch (ClassNotFoundException ex) { 1276 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 1277 clz.newInstance(); 1278 return; 1279 } 1280 } catch (Exception ex) { 1281 System.err.println("Logging configuration class \"" + cname + "\" failed"); 1282 System.err.println("" + ex); 1283 // keep going and useful config file. 1284 } 1285 } 1286 1287 String fname = System.getProperty("java.util.logging.config.file"); 1288 if (fname == null) { 1289 fname = System.getProperty("java.home"); 1290 if (fname == null) { 1291 throw new Error("Can't find java.home ??"); 1292 } 1293 File f = new File(fname, "conf"); 1294 f = new File(f, "logging.properties"); 1295 fname = f.getCanonicalPath(); 1296 } 1297 try (final InputStream in = new FileInputStream(fname)) { 1298 final BufferedInputStream bin = new BufferedInputStream(in); 1299 readConfiguration(bin); 1300 } 1301 } 1302 1303 /** 1304 * Reset the logging configuration. 1305 * <p> 1306 * For all named loggers, the reset operation removes and closes 1307 * all Handlers and (except for the root logger) sets the level 1308 * to null. The root logger's level is set to Level.INFO. 1309 * 1310 * @exception SecurityException if a security manager exists and if 1311 * the caller does not have LoggingPermission("control"). 1312 */ 1313 1314 public void reset() throws SecurityException { 1315 checkPermission(); 1316 // Since we are doing a reset we no longer want to initialize 1317 // the global handlers, if they haven't been initialized yet. 1318 reset(STATE_INITIALIZED); 1319 } 1320 1321 private void reset(int newGlobalHandlersState) { 1322 List<CloseOnReset> persistent; 1323 1324 // We don't want reset() and readConfiguration() 1325 // to run in parallel 1326 configurationLock.lock(); 1327 try { 1328 if (globalHandlersState == STATE_SHUTDOWN) { 1329 // already in terminal state 1330 return; 1331 } 1332 // install new empty properties 1333 props = new Properties(); 1334 // make sure we keep the loggers persistent until reset is done. 1335 // Those are the loggers for which we previously created a 1336 // handler from the configuration, and we need to prevent them 1337 // from being gc'ed until those handlers are closed. 1338 persistent = new ArrayList<>(closeOnResetLoggers); 1339 closeOnResetLoggers.clear(); 1340 1341 globalHandlersState = newGlobalHandlersState; 1342 1343 for (LoggerContext cx : contexts()) { 1344 resetLoggerContext(cx); 1345 } 1346 1347 persistent.clear(); 1348 } finally { 1349 configurationLock.unlock(); 1350 } 1351 } 1352 1353 private void resetLoggerContext(LoggerContext cx) { 1354 Enumeration<String> enum_ = cx.getLoggerNames(); 1355 while (enum_.hasMoreElements()) { 1356 String name = enum_.nextElement(); 1357 Logger logger = cx.findLogger(name); 1358 if (logger != null) { 1359 resetLogger(logger); 1360 } 1361 } 1362 } 1363 1364 private void closeHandlers(Logger logger) { 1365 Handler[] targets = logger.getHandlers(); 1366 for (Handler h : targets) { 1367 logger.removeHandler(h); 1368 try { 1369 h.close(); 1370 } catch (Exception ex) { 1371 // Problems closing a handler? Keep going... 1372 } 1373 } 1374 } 1375 1376 // Private method to reset an individual target logger. 1377 private void resetLogger(Logger logger) { 1378 // Close all the Logger handlers. 1379 closeHandlers(logger); 1380 1381 // Reset Logger level 1382 String name = logger.getName(); 1383 if (name != null && name.equals("")) { 1384 // This is the root logger. 1385 logger.setLevel(defaultLevel); 1386 } else { 1387 logger.setLevel(null); 1388 } 1389 } 1390 1391 // get a list of whitespace separated classnames from a property. 1392 private String[] parseClassNames(String propertyName) { 1393 String hands = getProperty(propertyName); 1394 if (hands == null) { 1395 return new String[0]; 1396 } 1397 hands = hands.trim(); 1398 int ix = 0; 1399 final List<String> result = new ArrayList<>(); 1400 while (ix < hands.length()) { 1401 int end = ix; 1402 while (end < hands.length()) { 1403 if (Character.isWhitespace(hands.charAt(end))) { 1404 break; 1405 } 1406 if (hands.charAt(end) == ',') { 1407 break; 1408 } 1409 end++; 1410 } 1411 String word = hands.substring(ix, end); 1412 ix = end+1; 1413 word = word.trim(); 1414 if (word.length() == 0) { 1415 continue; 1416 } 1417 result.add(word); 1418 } 1419 return result.toArray(new String[result.size()]); 1420 } 1421 1422 /** 1423 * Reinitialize the logging properties and reread the logging configuration 1424 * from the given stream, which should be in java.util.Properties format. 1425 * Any {@linkplain #addConfigurationListener registered configuration 1426 * listener} will be invoked after the properties are read. 1427 * <p> 1428 * Any log level definitions in the new configuration file will be 1429 * applied using Logger.setLevel(), if the target Logger exists. 1430 * 1431 * @param ins stream to read properties from 1432 * @exception SecurityException if a security manager exists and if 1433 * the caller does not have LoggingPermission("control"). 1434 * @exception IOException if there are problems reading from the stream. 1435 */ 1436 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 1437 checkPermission(); 1438 1439 // We don't want reset() and readConfiguration() to run 1440 // in parallel. 1441 configurationLock.lock(); 1442 try { 1443 if (globalHandlersState == STATE_SHUTDOWN) { 1444 // already in terminal state: don't even bother to read the 1445 // configuration 1446 return; 1447 } 1448 1449 // reset configuration to STATE_INITIALIZING temporarily so that any 1450 // ongoing logging requests block in initializeGlobalHandlers() 1451 // and don't get processed while configuration is still changing 1452 reset(STATE_INITIALIZING); 1453 try { 1454 try { 1455 // Load the properties 1456 props.load(ins); 1457 } catch (IllegalArgumentException x) { 1458 // props.load may throw an IllegalArgumentException if the stream 1459 // contains malformed Unicode escape sequences. 1460 // We wrap that in an IOException as readConfiguration is 1461 // specified to throw IOException if there are problems reading 1462 // from the stream. 1463 // Note: new IOException(x.getMessage(), x) allow us to get a more 1464 // concise error message than new IOException(x); 1465 throw new IOException(x.getMessage(), x); 1466 } 1467 1468 // Instantiate new configuration objects. 1469 String names[] = parseClassNames("config"); 1470 1471 for (String word : names) { 1472 try { 1473 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 1474 clz.newInstance(); 1475 } catch (Exception ex) { 1476 System.err.println("Can't load config class \"" + word + "\""); 1477 System.err.println("" + ex); 1478 // ex.printStackTrace(); 1479 } 1480 } 1481 1482 // Set levels on any pre-existing loggers, based on the new properties. 1483 setLevelsOnExistingLoggers(); 1484 1485 // Note that we need to reinitialize global handles when 1486 // they are first referenced. 1487 // Note: Marking global handlers as not initialized must be done 1488 // before unlocking. 1489 globalHandlersState = STATE_UNINITIALIZED; 1490 } catch (Throwable t) { 1491 // If there were any trouble, then set state to STATE_INITIALIZED 1492 // so that no global handlers reinitialization is performed on not fully 1493 // initialized configuration. 1494 globalHandlersState = STATE_INITIALIZED; 1495 // re-throw 1496 throw t; 1497 } 1498 1499 } finally { 1500 configurationLock.unlock(); 1501 } 1502 1503 // should be called out of lock to avoid dead-lock situations 1504 // when user code is involved 1505 invokeConfigurationListeners(); 1506 } 1507 1508 /** 1509 * Get the value of a logging property. 1510 * The method returns null if the property is not found. 1511 * @param name property name 1512 * @return property value 1513 */ 1514 public String getProperty(String name) { 1515 return props.getProperty(name); 1516 } 1517 1518 // Package private method to get a String property. 1519 // If the property is not defined we return the given 1520 // default value. 1521 String getStringProperty(String name, String defaultValue) { 1522 String val = getProperty(name); 1523 if (val == null) { 1524 return defaultValue; 1525 } 1526 return val.trim(); 1527 } 1528 1529 // Package private method to get an integer property. 1530 // If the property is not defined or cannot be parsed 1531 // we return the given default value. 1532 int getIntProperty(String name, int defaultValue) { 1533 String val = getProperty(name); 1534 if (val == null) { 1535 return defaultValue; 1536 } 1537 try { 1538 return Integer.parseInt(val.trim()); 1539 } catch (Exception ex) { 1540 return defaultValue; 1541 } 1542 } 1543 1544 // Package private method to get a long property. 1545 // If the property is not defined or cannot be parsed 1546 // we return the given default value. 1547 long getLongProperty(String name, long defaultValue) { 1548 String val = getProperty(name); 1549 if (val == null) { 1550 return defaultValue; 1551 } 1552 try { 1553 return Long.parseLong(val.trim()); 1554 } catch (Exception ex) { 1555 return defaultValue; 1556 } 1557 } 1558 1559 // Package private method to get a boolean property. 1560 // If the property is not defined or cannot be parsed 1561 // we return the given default value. 1562 boolean getBooleanProperty(String name, boolean defaultValue) { 1563 String val = getProperty(name); 1564 if (val == null) { 1565 return defaultValue; 1566 } 1567 val = val.toLowerCase(); 1568 if (val.equals("true") || val.equals("1")) { 1569 return true; 1570 } else if (val.equals("false") || val.equals("0")) { 1571 return false; 1572 } 1573 return defaultValue; 1574 } 1575 1576 // Package private method to get a Level property. 1577 // If the property is not defined or cannot be parsed 1578 // we return the given default value. 1579 Level getLevelProperty(String name, Level defaultValue) { 1580 String val = getProperty(name); 1581 if (val == null) { 1582 return defaultValue; 1583 } 1584 Level l = Level.findLevel(val.trim()); 1585 return l != null ? l : defaultValue; 1586 } 1587 1588 // Package private method to get a filter property. 1589 // We return an instance of the class named by the "name" 1590 // property. If the property is not defined or has problems 1591 // we return the defaultValue. 1592 Filter getFilterProperty(String name, Filter defaultValue) { 1593 String val = getProperty(name); 1594 try { 1595 if (val != null) { 1596 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1597 return (Filter) clz.newInstance(); 1598 } 1599 } catch (Exception ex) { 1600 // We got one of a variety of exceptions in creating the 1601 // class or creating an instance. 1602 // Drop through. 1603 } 1604 // We got an exception. Return the defaultValue. 1605 return defaultValue; 1606 } 1607 1608 1609 // Package private method to get a formatter property. 1610 // We return an instance of the class named by the "name" 1611 // property. If the property is not defined or has problems 1612 // we return the defaultValue. 1613 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1614 String val = getProperty(name); 1615 try { 1616 if (val != null) { 1617 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1618 return (Formatter) clz.newInstance(); 1619 } 1620 } catch (Exception ex) { 1621 // We got one of a variety of exceptions in creating the 1622 // class or creating an instance. 1623 // Drop through. 1624 } 1625 // We got an exception. Return the defaultValue. 1626 return defaultValue; 1627 } 1628 1629 // Private method to load the global handlers. 1630 // We do the real work lazily, when the global handlers 1631 // are first used. 1632 private void initializeGlobalHandlers() { 1633 int state = globalHandlersState; 1634 if (state == STATE_INITIALIZED || state == STATE_SHUTDOWN) { 1635 // Nothing to do: return. 1636 return; 1637 } 1638 1639 // If we have not initialized global handlers yet (or need to 1640 // reinitialize them), lets do it now (this case is indicated by 1641 // globalHandlersState == STATE_UNINITIALIZED). 1642 // If we are in the process of initializing global handlers we 1643 // also need to lock & wait (this case is indicated by 1644 // globalHandlersState == STATE_INITIALIZING). 1645 // So in either case we need to acquire the lock. 1646 configurationLock.lock(); 1647 try { 1648 if (globalHandlersState != STATE_UNINITIALIZED) { 1649 return; // recursive call or nothing to do 1650 } 1651 // set globalHandlersState to STATE_INITIALIZING first to avoid 1652 // getting an infinite recursion when loadLoggerHandlers(...) 1653 // is going to call addHandler(...) 1654 globalHandlersState = STATE_INITIALIZING; 1655 try { 1656 loadLoggerHandlers(rootLogger, null, "handlers"); 1657 } finally { 1658 globalHandlersState = STATE_INITIALIZED; 1659 } 1660 } finally { 1661 configurationLock.unlock(); 1662 } 1663 } 1664 1665 static final Permission controlPermission = new LoggingPermission("control", null); 1666 1667 void checkPermission() { 1668 SecurityManager sm = System.getSecurityManager(); 1669 if (sm != null) 1670 sm.checkPermission(controlPermission); 1671 } 1672 1673 /** 1674 * Check that the current context is trusted to modify the logging 1675 * configuration. This requires LoggingPermission("control"). 1676 * <p> 1677 * If the check fails we throw a SecurityException, otherwise 1678 * we return normally. 1679 * 1680 * @exception SecurityException if a security manager exists and if 1681 * the caller does not have LoggingPermission("control"). 1682 */ 1683 public void checkAccess() throws SecurityException { 1684 checkPermission(); 1685 } 1686 1687 // Nested class to represent a node in our tree of named loggers. 1688 private static class LogNode { 1689 HashMap<String,LogNode> children; 1690 LoggerWeakRef loggerRef; 1691 LogNode parent; 1692 final LoggerContext context; 1693 1694 LogNode(LogNode parent, LoggerContext context) { 1695 this.parent = parent; 1696 this.context = context; 1697 } 1698 1699 // Recursive method to walk the tree below a node and set 1700 // a new parent logger. 1701 void walkAndSetParent(Logger parent) { 1702 if (children == null) { 1703 return; 1704 } 1705 for (LogNode node : children.values()) { 1706 LoggerWeakRef ref = node.loggerRef; 1707 Logger logger = (ref == null) ? null : ref.get(); 1708 if (logger == null) { 1709 node.walkAndSetParent(parent); 1710 } else { 1711 doSetParent(logger, parent); 1712 } 1713 } 1714 } 1715 } 1716 1717 // We use a subclass of Logger for the root logger, so 1718 // that we only instantiate the global handlers when they 1719 // are first needed. 1720 private final class RootLogger extends Logger { 1721 private RootLogger() { 1722 // We do not call the protected Logger two args constructor here, 1723 // to avoid calling LogManager.getLogManager() from within the 1724 // RootLogger constructor. 1725 super("", null, null, LogManager.this, true); 1726 } 1727 1728 @Override 1729 public void log(LogRecord record) { 1730 // Make sure that the global handlers have been instantiated. 1731 initializeGlobalHandlers(); 1732 super.log(record); 1733 } 1734 1735 @Override 1736 public void addHandler(Handler h) { 1737 initializeGlobalHandlers(); 1738 super.addHandler(h); 1739 } 1740 1741 @Override 1742 public void removeHandler(Handler h) { 1743 initializeGlobalHandlers(); 1744 super.removeHandler(h); 1745 } 1746 1747 @Override 1748 Handler[] accessCheckedHandlers() { 1749 initializeGlobalHandlers(); 1750 return super.accessCheckedHandlers(); 1751 } 1752 } 1753 1754 1755 // Private method to be called when the configuration has 1756 // changed to apply any level settings to any pre-existing loggers. 1757 private void setLevelsOnExistingLoggers() { 1758 Enumeration<?> enum_ = props.propertyNames(); 1759 while (enum_.hasMoreElements()) { 1760 String key = (String)enum_.nextElement(); 1761 if (!key.endsWith(".level")) { 1762 // Not a level definition. 1763 continue; 1764 } 1765 int ix = key.length() - 6; 1766 String name = key.substring(0, ix); 1767 Level level = getLevelProperty(key, null); 1768 if (level == null) { 1769 System.err.println("Bad level value for property: " + key); 1770 continue; 1771 } 1772 for (LoggerContext cx : contexts()) { 1773 Logger l = cx.findLogger(name); 1774 if (l == null) { 1775 continue; 1776 } 1777 l.setLevel(level); 1778 } 1779 } 1780 } 1781 1782 // Management Support 1783 private static LoggingMXBean loggingMXBean = null; 1784 /** 1785 * String representation of the 1786 * {@link javax.management.ObjectName} for the management interface 1787 * for the logging facility. 1788 * 1789 * @see java.lang.management.PlatformLoggingMXBean 1790 * @see java.util.logging.LoggingMXBean 1791 * 1792 * @since 1.5 1793 */ 1794 public final static String LOGGING_MXBEAN_NAME 1795 = "java.util.logging:type=Logging"; 1796 1797 /** 1798 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1799 * An alternative way to manage loggers is through the 1800 * {@link java.lang.management.PlatformLoggingMXBean} interface 1801 * that can be obtained by calling: 1802 * <pre> 1803 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 1804 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 1805 * </pre> 1806 * 1807 * @return a {@link LoggingMXBean} object. 1808 * 1809 * @see java.lang.management.PlatformLoggingMXBean 1810 * @since 1.5 1811 */ 1812 public static synchronized LoggingMXBean getLoggingMXBean() { 1813 if (loggingMXBean == null) { 1814 loggingMXBean = new Logging(); 1815 } 1816 return loggingMXBean; 1817 } 1818 1819 /** 1820 * Adds a configuration listener to be invoked each time the logging 1821 * configuration is read. 1822 * If the listener is already registered the method does nothing. 1823 * <p> 1824 * The listener is invoked with privileges that are restricted by the 1825 * calling context of this method. 1826 * The order in which the listeners are invoked is unspecified. 1827 * <p> 1828 * It is recommended that listeners do not throw errors or exceptions. 1829 * 1830 * If a listener terminates with an uncaught error or exception then 1831 * the first exception will be propagated to the caller of 1832 * {@link #readConfiguration()} (or {@link #readConfiguration(java.io.InputStream)}) 1833 * after all listeners have been invoked. 1834 * 1835 * @implNote If more than one listener terminates with an uncaught error or 1836 * exception, an implementation may record the additional errors or 1837 * exceptions as {@linkplain Throwable#addSuppressed(java.lang.Throwable) 1838 * suppressed exceptions}. 1839 * 1840 * @param listener A configuration listener that will be invoked after the 1841 * configuration changed. 1842 * @return This LogManager. 1843 * @throws SecurityException if a security manager exists and if the 1844 * caller does not have LoggingPermission("control"). 1845 * @throws NullPointerException if the listener is null. 1846 * 1847 * @since 1.9 1848 */ 1849 public LogManager addConfigurationListener(Runnable listener) { 1850 final Runnable r = Objects.requireNonNull(listener); 1851 checkPermission(); 1852 final SecurityManager sm = System.getSecurityManager(); 1853 final AccessControlContext acc = 1854 sm == null ? null : AccessController.getContext(); 1855 final PrivilegedAction<Void> pa = 1856 acc == null ? null : () -> { r.run() ; return null; }; 1857 final Runnable pr = 1858 acc == null ? r : () -> AccessController.doPrivileged(pa, acc); 1859 // Will do nothing if already registered. 1860 listeners.putIfAbsent(r, pr); 1861 return this; 1862 } 1863 1864 /** 1865 * Removes a previously registered configuration listener. 1866 * 1867 * Returns silently if the listener is not found. 1868 * 1869 * @param listener the configuration listener to remove. 1870 * @throws NullPointerException if the listener is null. 1871 * @throws SecurityException if a security manager exists and if the 1872 * caller does not have LoggingPermission("control"). 1873 * 1874 * @since 1.9 1875 */ 1876 public void removeConfigurationListener(Runnable listener) { 1877 final Runnable key = Objects.requireNonNull(listener); 1878 checkPermission(); 1879 listeners.remove(key); 1880 } 1881 1882 private void invokeConfigurationListeners() { 1883 Throwable t = null; 1884 1885 // We're using an IdentityHashMap because we want to compare 1886 // keys using identity (==). 1887 // We don't want to loop within a block synchronized on 'listeners' 1888 // to avoid invoking listeners from yet another synchronized block. 1889 // So we're taking a snapshot of the values list to avoid the risk of 1890 // ConcurrentModificationException while looping. 1891 // 1892 for (Runnable c : listeners.values().toArray(new Runnable[0])) { 1893 try { 1894 c.run(); 1895 } catch (ThreadDeath death) { 1896 throw death; 1897 } catch (Error | RuntimeException x) { 1898 if (t == null) t = x; 1899 else t.addSuppressed(x); 1900 } 1901 } 1902 // Listeners are not supposed to throw exceptions, but if that 1903 // happens, we will rethrow the first error or exception that is raised 1904 // after all listeners have been invoked. 1905 if (t instanceof Error) throw (Error)t; 1906 if (t instanceof RuntimeException) throw (RuntimeException)t; 1907 } 1908 1909 }