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