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