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