1 /* 2 * Copyright (c) 2000, 2012, 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.lang.reflect.Constructor; 35 import java.lang.reflect.InvocationTargetException; 36 import java.lang.reflect.Method; 37 import java.beans.PropertyChangeListener; 38 39 /** 40 * There is a single global LogManager object that is used to 41 * maintain a set of shared state about Loggers and log services. 42 * <p> 43 * This LogManager object: 44 * <ul> 45 * <li> Manages a hierarchical namespace of Logger objects. All 46 * named Loggers are stored in this namespace. 47 * <li> Manages a set of logging control properties. These are 48 * simple key-value pairs that can be used by Handlers and 49 * other logging objects to configure themselves. 50 * </ul> 51 * <p> 52 * The global LogManager object can be retrieved using LogManager.getLogManager(). 53 * The LogManager object is created during class initialization and 54 * cannot subsequently be changed. 55 * <p> 56 * At startup the LogManager class is located using the 57 * java.util.logging.manager system property. 58 * <p> 59 * The LogManager defines two optional system properties that allow control over 60 * the initial configuration: 61 * <ul> 62 * <li>"java.util.logging.config.class" 63 * <li>"java.util.logging.config.file" 64 * </ul> 65 * These two properties may be specified on the command line to the "java" 66 * command, or as system property definitions passed to JNI_CreateJavaVM. 67 * <p> 68 * If the "java.util.logging.config.class" property is set, then the 69 * property value is treated as a class name. The given class will be 70 * loaded, an object will be instantiated, and that object's constructor 71 * is responsible for reading in the initial configuration. (That object 72 * may use other system properties to control its configuration.) The 73 * alternate configuration class can use <tt>readConfiguration(InputStream)</tt> 74 * to define properties in the LogManager. 75 * <p> 76 * If "java.util.logging.config.class" property is <b>not</b> set, 77 * then the "java.util.logging.config.file" system property can be used 78 * to specify a properties file (in java.util.Properties format). The 79 * initial logging configuration will be read from this file. 80 * <p> 81 * If neither of these properties is defined then the LogManager uses its 82 * default configuration. The default configuration is typically loaded from the 83 * properties file "{@code lib/logging.properties}" in the Java installation 84 * directory. 85 * <p> 86 * The properties for loggers and Handlers will have names starting 87 * with the dot-separated name for the handler or logger. 88 * <p> 89 * The global logging properties may include: 90 * <ul> 91 * <li>A property "handlers". This defines a whitespace or comma separated 92 * list of class names for handler classes to load and register as 93 * handlers on the root Logger (the Logger named ""). Each class 94 * name must be for a Handler class which has a default constructor. 95 * Note that these Handlers may be created lazily, when they are 96 * first used. 97 * 98 * <li>A property "<logger>.handlers". This defines a whitespace or 99 * comma separated list of class names for handlers classes to 100 * load and register as handlers to the specified logger. Each class 101 * name must be for a Handler class which has a default constructor. 102 * Note that these Handlers may be created lazily, when they are 103 * first used. 104 * 105 * <li>A property "<logger>.useParentHandlers". This defines a boolean 106 * value. By default every logger calls its parent in addition to 107 * handling the logging message itself, this often result in messages 108 * being handled by the root logger as well. When setting this property 109 * to false a Handler needs to be configured for this logger otherwise 110 * no logging messages are delivered. 111 * 112 * <li>A property "config". This property is intended to allow 113 * arbitrary configuration code to be run. The property defines a 114 * whitespace or comma separated list of class names. A new instance will be 115 * created for each named class. The default constructor of each class 116 * may execute arbitrary code to update the logging configuration, such as 117 * setting logger levels, adding handlers, adding filters, etc. 118 * </ul> 119 * <p> 120 * Note that all classes loaded during LogManager configuration are 121 * first searched on the system class path before any user class path. 122 * That includes the LogManager class, any config classes, and any 123 * handler classes. 124 * <p> 125 * Loggers are organized into a naming hierarchy based on their 126 * dot separated names. Thus "a.b.c" is a child of "a.b", but 127 * "a.b1" and a.b2" are peers. 128 * <p> 129 * All properties whose names end with ".level" are assumed to define 130 * log levels for Loggers. Thus "foo.level" defines a log level for 131 * the logger called "foo" and (recursively) for any of its children 132 * in the naming hierarchy. Log Levels are applied in the order they 133 * are defined in the properties file. Thus level settings for child 134 * nodes in the tree should come after settings for their parents. 135 * The property name ".level" can be used to set the level for the 136 * root of the tree. 137 * <p> 138 * All methods on the LogManager object are multi-thread safe. 139 * 140 * @since 1.4 141 */ 142 143 public class LogManager { 144 // The global LogManager object 145 private static LogManager manager; 146 147 private final static Handler[] emptyHandlers = { }; 148 private Properties props = new Properties(); 149 private final static Level defaultLevel = Level.INFO; 150 151 // The map of the registered listeners. The map value is the registration 152 // count to allow for cases where the same listener is registered many times. 153 private final Map<Object,Integer> listenerMap = new HashMap<>(); 154 155 // Table of named Loggers that maps names to Loggers. 156 private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); 157 // Tree of named Loggers 158 private LogNode root = new LogNode(null); 159 private Logger rootLogger; 160 161 // Have we done the primordial reading of the configuration file? 162 // (Must be done after a suitable amount of java.lang.System 163 // initialization has been done) 164 private volatile boolean readPrimordialConfiguration; 165 // Have we initialized global (root) handlers yet? 166 // This gets set to false in readConfiguration 167 private boolean initializedGlobalHandlers = true; 168 // True if JVM death is imminent and the exit hook has been called. 169 private boolean deathImminent; 170 171 static { 172 AccessController.doPrivileged(new PrivilegedAction<Object>() { 173 public Object run() { 174 String cname = null; 175 try { 176 cname = System.getProperty("java.util.logging.manager"); 177 if (cname != null) { 178 try { 179 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 180 manager = (LogManager) clz.newInstance(); 181 } catch (ClassNotFoundException ex) { 182 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 183 manager = (LogManager) clz.newInstance(); 184 } 185 } 186 } catch (Exception ex) { 187 System.err.println("Could not load Logmanager \"" + cname + "\""); 188 ex.printStackTrace(); 189 } 190 if (manager == null) { 191 manager = new LogManager(); 192 } 193 194 // Create and retain Logger for the root of the namespace. 195 manager.rootLogger = manager.new RootLogger(); 196 manager.addLogger(manager.rootLogger); 197 198 // Adding the global Logger. Doing so in the Logger.<clinit> 199 // would deadlock with the LogManager.<clinit>. 200 Logger.getGlobal().setLogManager(manager); 201 manager.addLogger(Logger.getGlobal()); 202 203 // We don't call readConfiguration() here, as we may be running 204 // very early in the JVM startup sequence. Instead readConfiguration 205 // will be called lazily in getLogManager(). 206 return null; 207 } 208 }); 209 } 210 211 212 // This private class is used as a shutdown hook. 213 // It does a "reset" to close all open handlers. 214 private class Cleaner extends Thread { 215 216 private Cleaner() { 217 /* Set context class loader to null in order to avoid 218 * keeping a strong reference to an application classloader. 219 */ 220 this.setContextClassLoader(null); 221 } 222 223 public void run() { 224 // This is to ensure the LogManager.<clinit> is completed 225 // before synchronized block. Otherwise deadlocks are possible. 226 LogManager mgr = manager; 227 228 // If the global handlers haven't been initialized yet, we 229 // don't want to initialize them just so we can close them! 230 synchronized (LogManager.this) { 231 // Note that death is imminent. 232 deathImminent = true; 233 initializedGlobalHandlers = true; 234 } 235 236 // Do a reset to close all active handlers. 237 reset(); 238 } 239 } 240 241 242 /** 243 * Protected constructor. This is protected so that container applications 244 * (such as J2EE containers) can subclass the object. It is non-public as 245 * it is intended that there only be one LogManager object, whose value is 246 * retrieved by calling LogManager.getLogManager. 247 */ 248 protected LogManager() { 249 // Add a shutdown hook to close the global handlers. 250 try { 251 Runtime.getRuntime().addShutdownHook(new Cleaner()); 252 } catch (IllegalStateException e) { 253 // If the VM is already shutting down, 254 // We do not need to register shutdownHook. 255 } 256 } 257 258 /** 259 * Return the global LogManager object. 260 */ 261 public static LogManager getLogManager() { 262 if (manager != null) { 263 manager.readPrimordialConfiguration(); 264 } 265 return manager; 266 } 267 268 private void readPrimordialConfiguration() { 269 if (!readPrimordialConfiguration) { 270 synchronized (this) { 271 if (!readPrimordialConfiguration) { 272 // If System.in/out/err are null, it's a good 273 // indication that we're still in the 274 // bootstrapping phase 275 if (System.out == null) { 276 return; 277 } 278 readPrimordialConfiguration = true; 279 try { 280 AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { 281 public Object run() throws Exception { 282 readConfiguration(); 283 284 // Platform loggers begin to delegate to java.util.logging.Logger 285 sun.util.logging.PlatformLogger.redirectPlatformLoggers(); 286 287 return null; 288 } 289 }); 290 } catch (Exception ex) { 291 // System.err.println("Can't read logging configuration:"); 292 // ex.printStackTrace(); 293 } 294 } 295 } 296 } 297 } 298 299 /** 300 * Adds an event listener to be invoked when the logging 301 * properties are re-read. Adding multiple instances of 302 * the same event Listener results in multiple entries 303 * in the property event listener table. 304 * 305 * <p><b>WARNING:</b> This method is not a declared method of this class 306 * in a subset Profile of Java SE. In other words, this method does not 307 * exist on runtimes that only implement a profile of Java SE.</p> 308 * 309 * @param l event listener 310 * @exception SecurityException if a security manager exists and if 311 * the caller does not have LoggingPermission("control"). 312 * @exception NullPointerException if the PropertyChangeListener is null. 313 * @deprecated The dependency on {@code PropertyChangeListener} creates a 314 * significant impediment to future modularization of the Java 315 * platform. This method will be removed in a future release. 316 * The global {@code LogManager} can detect changes to the 317 * logging configuration by overridding the {@link 318 * #readConfiguration readConfiguration} method. 319 */ 320 @Deprecated 321 public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException { 322 PropertyChangeListener listener = Objects.requireNonNull(l); 323 checkPermission(); 324 synchronized (listenerMap) { 325 // increment the registration count if already registered 326 Integer value = listenerMap.get(listener); 327 value = (value == null) ? 1 : (value + 1); 328 listenerMap.put(listener, value); 329 } 330 } 331 332 /** 333 * Removes an event listener for property change events. 334 * If the same listener instance has been added to the listener table 335 * through multiple invocations of <CODE>addPropertyChangeListener</CODE>, 336 * then an equivalent number of 337 * <CODE>removePropertyChangeListener</CODE> invocations are required to remove 338 * all instances of that listener from the listener table. 339 * <P> 340 * Returns silently if the given listener is not found. 341 * 342 * <p><b>WARNING:</b> This method is not a declared method of this class 343 * in a subset Profile of Java SE. In other words, this method does not 344 * exist on runtimes that only implement a profile of Java SE.</p> 345 * 346 * @param l event listener (can be null) 347 * @exception SecurityException if a security manager exists and if 348 * the caller does not have LoggingPermission("control"). 349 * @deprecated The dependency on {@code PropertyChangeListener} creates a 350 * significant impediment to future modularization of the Java 351 * platform. This method will be removed in a future release. 352 * The global {@code LogManager} can detect changes to the 353 * logging configuration by overridding the {@link 354 * #readConfiguration readConfiguration} method. 355 */ 356 @Deprecated 357 public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException { 358 checkPermission(); 359 if (l != null) { 360 PropertyChangeListener listener = l; 361 synchronized (listenerMap) { 362 Integer value = listenerMap.get(listener); 363 if (value != null) { 364 // remove from map if registration count is 1, otherwise 365 // just decrement its count 366 int i = value.intValue(); 367 if (i == 1) { 368 listenerMap.remove(listener); 369 } else { 370 assert i > 1; 371 listenerMap.put(listener, i - 1); 372 } 373 } 374 } 375 } 376 } 377 378 // Package-level method. 379 // Find or create a specified logger instance. If a logger has 380 // already been created with the given name it is returned. 381 // Otherwise a new logger instance is created and registered 382 // in the LogManager global namespace. 383 384 // This method will always return a non-null Logger object. 385 // Synchronization is not required here. All synchronization for 386 // adding a new Logger object is handled by addLogger(). 387 Logger demandLogger(String name) { 388 Logger result = getLogger(name); 389 if (result == null) { 390 // only allocate the new logger once 391 Logger newLogger = new Logger(name, null); 392 do { 393 if (addLogger(newLogger)) { 394 // We successfully added the new Logger that we 395 // created above so return it without refetching. 396 return newLogger; 397 } 398 399 // We didn't add the new Logger that we created above 400 // because another thread added a Logger with the same 401 // name after our null check above and before our call 402 // to addLogger(). We have to refetch the Logger because 403 // addLogger() returns a boolean instead of the Logger 404 // reference itself. However, if the thread that created 405 // the other Logger is not holding a strong reference to 406 // the other Logger, then it is possible for the other 407 // Logger to be GC'ed after we saw it in addLogger() and 408 // before we can refetch it. If it has been GC'ed then 409 // we'll just loop around and try again. 410 result = getLogger(name); 411 } while (result == null); 412 } 413 return result; 414 } 415 416 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 417 // parents have levels or handlers defined, make sure they are instantiated. 418 private void processParentHandlers(Logger logger, String name) { 419 int ix = 1; 420 for (;;) { 421 int ix2 = name.indexOf(".", ix); 422 if (ix2 < 0) { 423 break; 424 } 425 String pname = name.substring(0,ix2); 426 427 if (getProperty(pname+".level") != null || 428 getProperty(pname+".handlers") != null) { 429 // This pname has a level/handlers definition. 430 // Make sure it exists. 431 demandLogger(pname); 432 } 433 ix = ix2+1; 434 } 435 } 436 437 // Add new per logger handlers. 438 // We need to raise privilege here. All our decisions will 439 // be made based on the logging configuration, which can 440 // only be modified by trusted code. 441 private void loadLoggerHandlers(final Logger logger, final String name, 442 final String handlersPropertyName) { 443 AccessController.doPrivileged(new PrivilegedAction<Object>() { 444 public Object run() { 445 if (logger != rootLogger) { 446 boolean useParent = getBooleanProperty(name + ".useParentHandlers", true); 447 if (!useParent) { 448 logger.setUseParentHandlers(false); 449 } 450 } 451 452 String names[] = parseClassNames(handlersPropertyName); 453 for (int i = 0; i < names.length; i++) { 454 String word = names[i]; 455 try { 456 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 457 Handler hdl = (Handler) clz.newInstance(); 458 try { 459 // Check if there is a property defining the 460 // this handler's level. 461 String levs = getProperty(word + ".level"); 462 if (levs != null) { 463 hdl.setLevel(Level.parse(levs)); 464 } 465 } catch (Exception ex) { 466 System.err.println("Can't set level for " + word); 467 // Probably a bad level. Drop through. 468 } 469 // Add this Handler to the logger 470 logger.addHandler(hdl); 471 } catch (Exception ex) { 472 System.err.println("Can't load log handler \"" + word + "\""); 473 System.err.println("" + ex); 474 ex.printStackTrace(); 475 } 476 } 477 return null; 478 }}); 479 } 480 481 482 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 483 // that have been GC'ed. 484 private final ReferenceQueue<Logger> loggerRefQueue 485 = new ReferenceQueue<>(); 486 487 // Package-level inner class. 488 // Helper class for managing WeakReferences to Logger objects. 489 // 490 // LogManager.namedLoggers 491 // - has weak references to all named Loggers 492 // - namedLoggers keeps the LoggerWeakRef objects for the named 493 // Loggers around until we can deal with the book keeping for 494 // the named Logger that is being GC'ed. 495 // LogManager.LogNode.loggerRef 496 // - has a weak reference to a named Logger 497 // - the LogNode will also keep the LoggerWeakRef objects for 498 // the named Loggers around; currently LogNodes never go away. 499 // Logger.kids 500 // - has a weak reference to each direct child Logger; this 501 // includes anonymous and named Loggers 502 // - anonymous Loggers are always children of the rootLogger 503 // which is a strong reference; rootLogger.kids keeps the 504 // LoggerWeakRef objects for the anonymous Loggers around 505 // until we can deal with the book keeping. 506 // 507 final class LoggerWeakRef extends WeakReference<Logger> { 508 private String name; // for namedLoggers cleanup 509 private LogNode node; // for loggerRef cleanup 510 private WeakReference<Logger> parentRef; // for kids cleanup 511 512 LoggerWeakRef(Logger logger) { 513 super(logger, loggerRefQueue); 514 515 name = logger.getName(); // save for namedLoggers cleanup 516 } 517 518 // dispose of this LoggerWeakRef object 519 void dispose() { 520 if (node != null) { 521 // if we have a LogNode, then we were a named Logger 522 // so clear namedLoggers weak ref to us 523 manager.namedLoggers.remove(name); 524 name = null; // clear our ref to the Logger's name 525 526 node.loggerRef = null; // clear LogNode's weak ref to us 527 node = null; // clear our ref to LogNode 528 } 529 530 if (parentRef != null) { 531 // this LoggerWeakRef has or had a parent Logger 532 Logger parent = parentRef.get(); 533 if (parent != null) { 534 // the parent Logger is still there so clear the 535 // parent Logger's weak ref to us 536 parent.removeChildLogger(this); 537 } 538 parentRef = null; // clear our weak ref to the parent Logger 539 } 540 } 541 542 // set the node field to the specified value 543 void setNode(LogNode node) { 544 this.node = node; 545 } 546 547 // set the parentRef field to the specified value 548 void setParentRef(WeakReference<Logger> parentRef) { 549 this.parentRef = parentRef; 550 } 551 } 552 553 // Package-level method. 554 // Drain some Logger objects that have been GC'ed. 555 // 556 // drainLoggerRefQueueBounded() is called by addLogger() below 557 // and by Logger.getAnonymousLogger(String) so we'll drain up to 558 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 559 // 560 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 561 // us about a 50/50 mix in increased weak ref counts versus 562 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 563 // Here are stats for cleaning up sets of 400 anonymous Loggers: 564 // - test duration 1 minute 565 // - sample size of 125 sets of 400 566 // - average: 1.99 ms 567 // - minimum: 0.57 ms 568 // - maximum: 25.3 ms 569 // 570 // The same config gives us a better decreased weak ref count 571 // than increased weak ref count in the LoggerWeakRefLeak test. 572 // Here are stats for cleaning up sets of 400 named Loggers: 573 // - test duration 2 minutes 574 // - sample size of 506 sets of 400 575 // - average: 0.57 ms 576 // - minimum: 0.02 ms 577 // - maximum: 10.9 ms 578 // 579 private final static int MAX_ITERATIONS = 400; 580 final synchronized void drainLoggerRefQueueBounded() { 581 for (int i = 0; i < MAX_ITERATIONS; i++) { 582 if (loggerRefQueue == null) { 583 // haven't finished loading LogManager yet 584 break; 585 } 586 587 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 588 if (ref == null) { 589 break; 590 } 591 // a Logger object has been GC'ed so clean it up 592 ref.dispose(); 593 } 594 } 595 596 /** 597 * Add a named logger. This does nothing and returns false if a logger 598 * with the same name is already registered. 599 * <p> 600 * The Logger factory methods call this method to register each 601 * newly created Logger. 602 * <p> 603 * The application should retain its own reference to the Logger 604 * object to avoid it being garbage collected. The LogManager 605 * may only retain a weak reference. 606 * 607 * @param logger the new logger. 608 * @return true if the argument logger was registered successfully, 609 * false if a logger of that name already exists. 610 * @exception NullPointerException if the logger name is null. 611 */ 612 public synchronized boolean addLogger(Logger logger) { 613 final String name = logger.getName(); 614 if (name == null) { 615 throw new NullPointerException(); 616 } 617 618 // cleanup some Loggers that have been GC'ed 619 drainLoggerRefQueueBounded(); 620 621 LoggerWeakRef ref = namedLoggers.get(name); 622 if (ref != null) { 623 if (ref.get() == null) { 624 // It's possible that the Logger was GC'ed after the 625 // drainLoggerRefQueueBounded() call above so allow 626 // a new one to be registered. 627 namedLoggers.remove(name); 628 } else { 629 // We already have a registered logger with the given name. 630 return false; 631 } 632 } 633 634 // We're adding a new logger. 635 // Note that we are creating a weak reference here. 636 ref = new LoggerWeakRef(logger); 637 namedLoggers.put(name, ref); 638 639 // Apply any initial level defined for the new logger. 640 Level level = getLevelProperty(name+".level", null); 641 if (level != null) { 642 doSetLevel(logger, level); 643 } 644 645 // Do we have a per logger handler too? 646 // Note: this will add a 200ms penalty 647 loadLoggerHandlers(logger, name, name+".handlers"); 648 processParentHandlers(logger, name); 649 650 // Find the new node and its parent. 651 LogNode node = findNode(name); 652 node.loggerRef = ref; 653 Logger parent = null; 654 LogNode nodep = node.parent; 655 while (nodep != null) { 656 LoggerWeakRef nodeRef = nodep.loggerRef; 657 if (nodeRef != null) { 658 parent = nodeRef.get(); 659 if (parent != null) { 660 break; 661 } 662 } 663 nodep = nodep.parent; 664 } 665 666 if (parent != null) { 667 doSetParent(logger, parent); 668 } 669 // Walk over the children and tell them we are their new parent. 670 node.walkAndSetParent(logger); 671 672 // new LogNode is ready so tell the LoggerWeakRef about it 673 ref.setNode(node); 674 675 return true; 676 } 677 678 679 // Private method to set a level on a logger. 680 // If necessary, we raise privilege before doing the call. 681 private static void doSetLevel(final Logger logger, final Level level) { 682 SecurityManager sm = System.getSecurityManager(); 683 if (sm == null) { 684 // There is no security manager, so things are easy. 685 logger.setLevel(level); 686 return; 687 } 688 // There is a security manager. Raise privilege before 689 // calling setLevel. 690 AccessController.doPrivileged(new PrivilegedAction<Object>() { 691 public Object run() { 692 logger.setLevel(level); 693 return null; 694 }}); 695 } 696 697 698 699 // Private method to set a parent on a logger. 700 // If necessary, we raise privilege before doing the setParent call. 701 private static void doSetParent(final Logger logger, final Logger parent) { 702 SecurityManager sm = System.getSecurityManager(); 703 if (sm == null) { 704 // There is no security manager, so things are easy. 705 logger.setParent(parent); 706 return; 707 } 708 // There is a security manager. Raise privilege before 709 // calling setParent. 710 AccessController.doPrivileged(new PrivilegedAction<Object>() { 711 public Object run() { 712 logger.setParent(parent); 713 return null; 714 }}); 715 } 716 717 // Find a node in our tree of logger nodes. 718 // If necessary, create it. 719 private LogNode findNode(String name) { 720 if (name == null || name.equals("")) { 721 return root; 722 } 723 LogNode node = root; 724 while (name.length() > 0) { 725 int ix = name.indexOf("."); 726 String head; 727 if (ix > 0) { 728 head = name.substring(0,ix); 729 name = name.substring(ix+1); 730 } else { 731 head = name; 732 name = ""; 733 } 734 if (node.children == null) { 735 node.children = new HashMap<>(); 736 } 737 LogNode child = node.children.get(head); 738 if (child == null) { 739 child = new LogNode(node); 740 node.children.put(head, child); 741 } 742 node = child; 743 } 744 return node; 745 } 746 747 /** 748 * Method to find a named logger. 749 * <p> 750 * Note that since untrusted code may create loggers with 751 * arbitrary names this method should not be relied on to 752 * find Loggers for security sensitive logging. 753 * It is also important to note that the Logger associated with the 754 * String {@code name} may be garbage collected at any time if there 755 * is no strong reference to the Logger. The caller of this method 756 * must check the return value for null in order to properly handle 757 * the case where the Logger has been garbage collected. 758 * <p> 759 * @param name name of the logger 760 * @return matching logger or null if none is found 761 */ 762 public synchronized Logger getLogger(String name) { 763 LoggerWeakRef ref = namedLoggers.get(name); 764 if (ref == null) { 765 return null; 766 } 767 Logger logger = ref.get(); 768 if (logger == null) { 769 // Hashtable holds stale weak reference 770 // to a logger which has been GC-ed. 771 namedLoggers.remove(name); 772 } 773 return logger; 774 } 775 776 /** 777 * Get an enumeration of known logger names. 778 * <p> 779 * Note: Loggers may be added dynamically as new classes are loaded. 780 * This method only reports on the loggers that are currently registered. 781 * It is also important to note that this method only returns the name 782 * of a Logger, not a strong reference to the Logger itself. 783 * The returned String does nothing to prevent the Logger from being 784 * garbage collected. In particular, if the returned name is passed 785 * to {@code LogManager.getLogger()}, then the caller must check the 786 * return value from {@code LogManager.getLogger()} for null to properly 787 * handle the case where the Logger has been garbage collected in the 788 * time since its name was returned by this method. 789 * <p> 790 * @return enumeration of logger name strings 791 */ 792 public synchronized Enumeration<String> getLoggerNames() { 793 return namedLoggers.keys(); 794 } 795 796 /** 797 * Reinitialize the logging properties and reread the logging configuration. 798 * <p> 799 * The same rules are used for locating the configuration properties 800 * as are used at startup. So normally the logging properties will 801 * be re-read from the same file that was used at startup. 802 * <P> 803 * Any log level definitions in the new configuration file will be 804 * applied using Logger.setLevel(), if the target Logger exists. 805 * <p> 806 * A PropertyChangeEvent will be fired after the properties are read. 807 * 808 * @exception SecurityException if a security manager exists and if 809 * the caller does not have LoggingPermission("control"). 810 * @exception IOException if there are IO problems reading the configuration. 811 */ 812 public void readConfiguration() throws IOException, SecurityException { 813 checkPermission(); 814 815 // if a configuration class is specified, load it and use it. 816 String cname = System.getProperty("java.util.logging.config.class"); 817 if (cname != null) { 818 try { 819 // Instantiate the named class. It is its constructor's 820 // responsibility to initialize the logging configuration, by 821 // calling readConfiguration(InputStream) with a suitable stream. 822 try { 823 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname); 824 clz.newInstance(); 825 return; 826 } catch (ClassNotFoundException ex) { 827 Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 828 clz.newInstance(); 829 return; 830 } 831 } catch (Exception ex) { 832 System.err.println("Logging configuration class \"" + cname + "\" failed"); 833 System.err.println("" + ex); 834 // keep going and useful config file. 835 } 836 } 837 838 String fname = System.getProperty("java.util.logging.config.file"); 839 if (fname == null) { 840 fname = System.getProperty("java.home"); 841 if (fname == null) { 842 throw new Error("Can't find java.home ??"); 843 } 844 File f = new File(fname, "lib"); 845 f = new File(f, "logging.properties"); 846 fname = f.getCanonicalPath(); 847 } 848 InputStream in = new FileInputStream(fname); 849 BufferedInputStream bin = new BufferedInputStream(in); 850 try { 851 readConfiguration(bin); 852 } finally { 853 if (in != null) { 854 in.close(); 855 } 856 } 857 } 858 859 /** 860 * Reset the logging configuration. 861 * <p> 862 * For all named loggers, the reset operation removes and closes 863 * all Handlers and (except for the root logger) sets the level 864 * to null. The root logger's level is set to Level.INFO. 865 * 866 * @exception SecurityException if a security manager exists and if 867 * the caller does not have LoggingPermission("control"). 868 */ 869 870 public void reset() throws SecurityException { 871 checkPermission(); 872 synchronized (this) { 873 props = new Properties(); 874 // Since we are doing a reset we no longer want to initialize 875 // the global handlers, if they haven't been initialized yet. 876 initializedGlobalHandlers = true; 877 } 878 Enumeration<String> enum_ = getLoggerNames(); 879 while (enum_.hasMoreElements()) { 880 String name = enum_.nextElement(); 881 resetLogger(name); 882 } 883 } 884 885 886 // Private method to reset an individual target logger. 887 private void resetLogger(String name) { 888 Logger logger = getLogger(name); 889 if (logger == null) { 890 return; 891 } 892 // Close all the Logger's handlers. 893 Handler[] targets = logger.getHandlers(); 894 for (int i = 0; i < targets.length; i++) { 895 Handler h = targets[i]; 896 logger.removeHandler(h); 897 try { 898 h.close(); 899 } catch (Exception ex) { 900 // Problems closing a handler? Keep going... 901 } 902 } 903 if (name != null && name.equals("")) { 904 // This is the root logger. 905 logger.setLevel(defaultLevel); 906 } else { 907 logger.setLevel(null); 908 } 909 } 910 911 // get a list of whitespace separated classnames from a property. 912 private String[] parseClassNames(String propertyName) { 913 String hands = getProperty(propertyName); 914 if (hands == null) { 915 return new String[0]; 916 } 917 hands = hands.trim(); 918 int ix = 0; 919 Vector<String> result = new Vector<>(); 920 while (ix < hands.length()) { 921 int end = ix; 922 while (end < hands.length()) { 923 if (Character.isWhitespace(hands.charAt(end))) { 924 break; 925 } 926 if (hands.charAt(end) == ',') { 927 break; 928 } 929 end++; 930 } 931 String word = hands.substring(ix, end); 932 ix = end+1; 933 word = word.trim(); 934 if (word.length() == 0) { 935 continue; 936 } 937 result.add(word); 938 } 939 return result.toArray(new String[result.size()]); 940 } 941 942 /** 943 * Reinitialize the logging properties and reread the logging configuration 944 * from the given stream, which should be in java.util.Properties format. 945 * A PropertyChangeEvent will be fired after the properties are read. 946 * <p> 947 * Any log level definitions in the new configuration file will be 948 * applied using Logger.setLevel(), if the target Logger exists. 949 * 950 * @param ins stream to read properties from 951 * @exception SecurityException if a security manager exists and if 952 * the caller does not have LoggingPermission("control"). 953 * @exception IOException if there are problems reading from the stream. 954 */ 955 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 956 checkPermission(); 957 reset(); 958 959 // Load the properties 960 props.load(ins); 961 // Instantiate new configuration objects. 962 String names[] = parseClassNames("config"); 963 964 for (int i = 0; i < names.length; i++) { 965 String word = names[i]; 966 try { 967 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); 968 clz.newInstance(); 969 } catch (Exception ex) { 970 System.err.println("Can't load config class \"" + word + "\""); 971 System.err.println("" + ex); 972 // ex.printStackTrace(); 973 } 974 } 975 976 // Set levels on any pre-existing loggers, based on the new properties. 977 setLevelsOnExistingLoggers(); 978 979 // Notify any interested parties that our properties have changed. 980 // We first take a copy of the listener map so that we aren't holding any 981 // locks when calling the listeners. 982 Map<Object,Integer> listeners = null; 983 synchronized (listenerMap) { 984 if (!listenerMap.isEmpty()) 985 listeners = new HashMap<>(listenerMap); 986 } 987 if (listeners != null) { 988 assert Beans.isBeansPresent(); 989 Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null); 990 for (Map.Entry<Object,Integer> entry : listeners.entrySet()) { 991 Object listener = entry.getKey(); 992 int count = entry.getValue().intValue(); 993 for (int i = 0; i < count; i++) { 994 Beans.invokePropertyChange(listener, ev); 995 } 996 } 997 } 998 999 1000 // Note that we need to reinitialize global handles when 1001 // they are first referenced. 1002 synchronized (this) { 1003 initializedGlobalHandlers = false; 1004 } 1005 } 1006 1007 /** 1008 * Get the value of a logging property. 1009 * The method returns null if the property is not found. 1010 * @param name property name 1011 * @return property value 1012 */ 1013 public String getProperty(String name) { 1014 return props.getProperty(name); 1015 } 1016 1017 // Package private method to get a String property. 1018 // If the property is not defined we return the given 1019 // default value. 1020 String getStringProperty(String name, String defaultValue) { 1021 String val = getProperty(name); 1022 if (val == null) { 1023 return defaultValue; 1024 } 1025 return val.trim(); 1026 } 1027 1028 // Package private method to get an integer property. 1029 // If the property is not defined or cannot be parsed 1030 // we return the given default value. 1031 int getIntProperty(String name, int defaultValue) { 1032 String val = getProperty(name); 1033 if (val == null) { 1034 return defaultValue; 1035 } 1036 try { 1037 return Integer.parseInt(val.trim()); 1038 } catch (Exception ex) { 1039 return defaultValue; 1040 } 1041 } 1042 1043 // Package private method to get a boolean property. 1044 // If the property is not defined or cannot be parsed 1045 // we return the given default value. 1046 boolean getBooleanProperty(String name, boolean defaultValue) { 1047 String val = getProperty(name); 1048 if (val == null) { 1049 return defaultValue; 1050 } 1051 val = val.toLowerCase(); 1052 if (val.equals("true") || val.equals("1")) { 1053 return true; 1054 } else if (val.equals("false") || val.equals("0")) { 1055 return false; 1056 } 1057 return defaultValue; 1058 } 1059 1060 // Package private method to get a Level property. 1061 // If the property is not defined or cannot be parsed 1062 // we return the given default value. 1063 Level getLevelProperty(String name, Level defaultValue) { 1064 String val = getProperty(name); 1065 if (val == null) { 1066 return defaultValue; 1067 } 1068 try { 1069 return Level.parse(val.trim()); 1070 } catch (Exception ex) { 1071 return defaultValue; 1072 } 1073 } 1074 1075 // Package private method to get a filter property. 1076 // We return an instance of the class named by the "name" 1077 // property. If the property is not defined or has problems 1078 // we return the defaultValue. 1079 Filter getFilterProperty(String name, Filter defaultValue) { 1080 String val = getProperty(name); 1081 try { 1082 if (val != null) { 1083 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1084 return (Filter) clz.newInstance(); 1085 } 1086 } catch (Exception ex) { 1087 // We got one of a variety of exceptions in creating the 1088 // class or creating an instance. 1089 // Drop through. 1090 } 1091 // We got an exception. Return the defaultValue. 1092 return defaultValue; 1093 } 1094 1095 1096 // Package private method to get a formatter property. 1097 // We return an instance of the class named by the "name" 1098 // property. If the property is not defined or has problems 1099 // we return the defaultValue. 1100 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1101 String val = getProperty(name); 1102 try { 1103 if (val != null) { 1104 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(val); 1105 return (Formatter) clz.newInstance(); 1106 } 1107 } catch (Exception ex) { 1108 // We got one of a variety of exceptions in creating the 1109 // class or creating an instance. 1110 // Drop through. 1111 } 1112 // We got an exception. Return the defaultValue. 1113 return defaultValue; 1114 } 1115 1116 // Private method to load the global handlers. 1117 // We do the real work lazily, when the global handlers 1118 // are first used. 1119 private synchronized void initializeGlobalHandlers() { 1120 if (initializedGlobalHandlers) { 1121 return; 1122 } 1123 1124 initializedGlobalHandlers = true; 1125 1126 if (deathImminent) { 1127 // Aaargh... 1128 // The VM is shutting down and our exit hook has been called. 1129 // Avoid allocating global handlers. 1130 return; 1131 } 1132 loadLoggerHandlers(rootLogger, null, "handlers"); 1133 } 1134 1135 private final Permission controlPermission = new LoggingPermission("control", null); 1136 1137 void checkPermission() { 1138 SecurityManager sm = System.getSecurityManager(); 1139 if (sm != null) 1140 sm.checkPermission(controlPermission); 1141 } 1142 1143 /** 1144 * Check that the current context is trusted to modify the logging 1145 * configuration. This requires LoggingPermission("control"). 1146 * <p> 1147 * If the check fails we throw a SecurityException, otherwise 1148 * we return normally. 1149 * 1150 * @exception SecurityException if a security manager exists and if 1151 * the caller does not have LoggingPermission("control"). 1152 */ 1153 public void checkAccess() throws SecurityException { 1154 checkPermission(); 1155 } 1156 1157 // Nested class to represent a node in our tree of named loggers. 1158 private static class LogNode { 1159 HashMap<String,LogNode> children; 1160 LoggerWeakRef loggerRef; 1161 LogNode parent; 1162 1163 LogNode(LogNode parent) { 1164 this.parent = parent; 1165 } 1166 1167 // Recursive method to walk the tree below a node and set 1168 // a new parent logger. 1169 void walkAndSetParent(Logger parent) { 1170 if (children == null) { 1171 return; 1172 } 1173 Iterator<LogNode> values = children.values().iterator(); 1174 while (values.hasNext()) { 1175 LogNode node = values.next(); 1176 LoggerWeakRef ref = node.loggerRef; 1177 Logger logger = (ref == null) ? null : ref.get(); 1178 if (logger == null) { 1179 node.walkAndSetParent(parent); 1180 } else { 1181 doSetParent(logger, parent); 1182 } 1183 } 1184 } 1185 } 1186 1187 // We use a subclass of Logger for the root logger, so 1188 // that we only instantiate the global handlers when they 1189 // are first needed. 1190 private class RootLogger extends Logger { 1191 1192 private RootLogger() { 1193 super("", null); 1194 setLevel(defaultLevel); 1195 } 1196 1197 public void log(LogRecord record) { 1198 // Make sure that the global handlers have been instantiated. 1199 initializeGlobalHandlers(); 1200 super.log(record); 1201 } 1202 1203 public void addHandler(Handler h) { 1204 initializeGlobalHandlers(); 1205 super.addHandler(h); 1206 } 1207 1208 public void removeHandler(Handler h) { 1209 initializeGlobalHandlers(); 1210 super.removeHandler(h); 1211 } 1212 1213 public Handler[] getHandlers() { 1214 initializeGlobalHandlers(); 1215 return super.getHandlers(); 1216 } 1217 } 1218 1219 1220 // Private method to be called when the configuration has 1221 // changed to apply any level settings to any pre-existing loggers. 1222 synchronized private void setLevelsOnExistingLoggers() { 1223 Enumeration<?> enum_ = props.propertyNames(); 1224 while (enum_.hasMoreElements()) { 1225 String key = (String)enum_.nextElement(); 1226 if (!key.endsWith(".level")) { 1227 // Not a level definition. 1228 continue; 1229 } 1230 int ix = key.length() - 6; 1231 String name = key.substring(0, ix); 1232 Level level = getLevelProperty(key, null); 1233 if (level == null) { 1234 System.err.println("Bad level value for property: " + key); 1235 continue; 1236 } 1237 Logger l = getLogger(name); 1238 if (l == null) { 1239 continue; 1240 } 1241 l.setLevel(level); 1242 } 1243 } 1244 1245 // Management Support 1246 private static LoggingMXBean loggingMXBean = null; 1247 /** 1248 * String representation of the 1249 * {@link javax.management.ObjectName} for the management interface 1250 * for the logging facility. 1251 * 1252 * @see java.lang.management.PlatformLoggingMXBean 1253 * @see java.util.logging.LoggingMXBean 1254 * 1255 * @since 1.5 1256 */ 1257 public final static String LOGGING_MXBEAN_NAME 1258 = "java.util.logging:type=Logging"; 1259 1260 /** 1261 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1262 * An alternative way to manage loggers is through the 1263 * {@link java.lang.management.PlatformLoggingMXBean} interface 1264 * that can be obtained by calling: 1265 * <pre> 1266 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 1267 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 1268 * </pre> 1269 * 1270 * @return a {@link LoggingMXBean} object. 1271 * 1272 * @see java.lang.management.PlatformLoggingMXBean 1273 * @since 1.5 1274 */ 1275 public static synchronized LoggingMXBean getLoggingMXBean() { 1276 if (loggingMXBean == null) { 1277 loggingMXBean = new Logging(); 1278 } 1279 return loggingMXBean; 1280 } 1281 1282 /** 1283 * A class that provides access to the java.beans.PropertyChangeListener 1284 * and java.beans.PropertyChangeEvent without creating a static dependency 1285 * on java.beans. This class can be removed once the addPropertyChangeListener 1286 * and removePropertyChangeListener methods are removed. 1287 */ 1288 private static class Beans { 1289 private static final Class<?> propertyChangeListenerClass = 1290 getClass("java.beans.PropertyChangeListener"); 1291 1292 private static final Class<?> propertyChangeEventClass = 1293 getClass("java.beans.PropertyChangeEvent"); 1294 1295 private static final Method propertyChangeMethod = 1296 getMethod(propertyChangeListenerClass, 1297 "propertyChange", 1298 propertyChangeEventClass); 1299 1300 private static final Constructor<?> propertyEventCtor = 1301 getConstructor(propertyChangeEventClass, 1302 Object.class, 1303 String.class, 1304 Object.class, 1305 Object.class); 1306 1307 private static Class<?> getClass(String name) { 1308 try { 1309 return Class.forName(name, true, Beans.class.getClassLoader()); 1310 } catch (ClassNotFoundException e) { 1311 return null; 1312 } 1313 } 1314 private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { 1315 try { 1316 return (c == null) ? null : c.getDeclaredConstructor(types); 1317 } catch (NoSuchMethodException x) { 1318 throw new AssertionError(x); 1319 } 1320 } 1321 1322 private static Method getMethod(Class<?> c, String name, Class<?>... types) { 1323 try { 1324 return (c == null) ? null : c.getMethod(name, types); 1325 } catch (NoSuchMethodException e) { 1326 throw new AssertionError(e); 1327 } 1328 } 1329 1330 /** 1331 * Returns {@code true} if java.beans is present. 1332 */ 1333 static boolean isBeansPresent() { 1334 return propertyChangeListenerClass != null && 1335 propertyChangeEventClass != null; 1336 } 1337 1338 /** 1339 * Returns a new PropertyChangeEvent with the given source, property 1340 * name, old and new values. 1341 */ 1342 static Object newPropertyChangeEvent(Object source, String prop, 1343 Object oldValue, Object newValue) 1344 { 1345 try { 1346 return propertyEventCtor.newInstance(source, prop, oldValue, newValue); 1347 } catch (InstantiationException | IllegalAccessException x) { 1348 throw new AssertionError(x); 1349 } catch (InvocationTargetException x) { 1350 Throwable cause = x.getCause(); 1351 if (cause instanceof Error) 1352 throw (Error)cause; 1353 if (cause instanceof RuntimeException) 1354 throw (RuntimeException)cause; 1355 throw new AssertionError(x); 1356 } 1357 } 1358 1359 /** 1360 * Invokes the given PropertyChangeListener's propertyChange method 1361 * with the given event. 1362 */ 1363 static void invokePropertyChange(Object listener, Object ev) { 1364 try { 1365 propertyChangeMethod.invoke(listener, ev); 1366 } catch (IllegalAccessException x) { 1367 throw new AssertionError(x); 1368 } catch (InvocationTargetException x) { 1369 Throwable cause = x.getCause(); 1370 if (cause instanceof Error) 1371 throw (Error)cause; 1372 if (cause instanceof RuntimeException) 1373 throw (RuntimeException)cause; 1374 throw new AssertionError(x); 1375 } 1376 } 1377 } 1378 }