1 /* 2 * Copyright (c) 2000, 2011, 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 = new Hashtable<>(); 160 // Tree of named 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 346 // This method will always return a non-null Logger object. 347 // Synchronization is not required here. All synchronization for 348 // adding a new Logger object is handled by addLogger(). 349 Logger demandLogger(String name) { 350 Logger result = getLogger(name); 351 if (result == null) { 352 // only allocate the new logger once 353 Logger newLogger = new Logger(name, null); 354 do { 355 if (addLogger(newLogger)) { 356 // We successfully added the new Logger that we 357 // created above so return it without refetching. 358 return newLogger; 359 } 360 361 // We didn't add the new Logger that we created above 362 // because another thread added a Logger with the same 363 // name after our null check above and before our call 364 // to addLogger(). We have to refetch the Logger because 365 // addLogger() returns a boolean instead of the Logger 366 // reference itself. However, if the thread that created 367 // the other Logger is not holding a strong reference to 368 // the other Logger, then it is possible for the other 369 // Logger to be GC'ed after we saw it in addLogger() and 370 // before we can refetch it. If it has been GC'ed then 371 // we'll just loop around and try again. 372 result = getLogger(name); 373 } while (result == null); 374 } 375 return result; 376 } 377 378 // If logger.getUseParentHandlers() returns 'true' and any of the logger's 379 // parents have levels or handlers defined, make sure they are instantiated. 380 private void processParentHandlers(Logger logger, String name) { 381 int ix = 1; 382 for (;;) { 383 int ix2 = name.indexOf(".", ix); 384 if (ix2 < 0) { 385 break; 386 } 387 String pname = name.substring(0,ix2); 388 389 if (getProperty(pname+".level") != null || 390 getProperty(pname+".handlers") != null) { 391 // This pname has a level/handlers definition. 392 // Make sure it exists. 393 demandLogger(pname); 394 } 395 ix = ix2+1; 396 } 397 } 398 399 // Add new per logger handlers. 400 // We need to raise privilege here. All our decisions will 401 // be made based on the logging configuration, which can 402 // only be modified by trusted code. 403 private void loadLoggerHandlers(final Logger logger, final String name, 404 final String handlersPropertyName) { 405 AccessController.doPrivileged(new PrivilegedAction<Object>() { 406 public Object run() { 407 if (logger != rootLogger) { 408 boolean useParent = getBooleanProperty(name + ".useParentHandlers", true); 409 if (!useParent) { 410 logger.setUseParentHandlers(false); 411 } 412 } 413 414 String names[] = parseClassNames(handlersPropertyName); 415 for (int i = 0; i < names.length; i++) { 416 String word = names[i]; 417 try { 418 Class clz = ClassLoader.getSystemClassLoader().loadClass(word); 419 Handler hdl = (Handler) clz.newInstance(); 420 try { 421 // Check if there is a property defining the 422 // this handler's level. 423 String levs = getProperty(word + ".level"); 424 if (levs != null) { 425 hdl.setLevel(Level.parse(levs)); 426 } 427 } catch (Exception ex) { 428 System.err.println("Can't set level for " + word); 429 // Probably a bad level. Drop through. 430 } 431 // Add this Handler to the logger 432 logger.addHandler(hdl); 433 } catch (Exception ex) { 434 System.err.println("Can't load log handler \"" + word + "\""); 435 System.err.println("" + ex); 436 ex.printStackTrace(); 437 } 438 } 439 return null; 440 }}); 441 } 442 443 444 // loggerRefQueue holds LoggerWeakRef objects for Logger objects 445 // that have been GC'ed. 446 private final ReferenceQueue<Logger> loggerRefQueue 447 = new ReferenceQueue<>(); 448 449 // Package-level inner class. 450 // Helper class for managing WeakReferences to Logger objects. 451 // 452 // LogManager.namedLoggers 453 // - has weak references to all named Loggers 454 // - namedLoggers keeps the LoggerWeakRef objects for the named 455 // Loggers around until we can deal with the book keeping for 456 // the named Logger that is being GC'ed. 457 // LogManager.LogNode.loggerRef 458 // - has a weak reference to a named Logger 459 // - the LogNode will also keep the LoggerWeakRef objects for 460 // the named Loggers around; currently LogNodes never go away. 461 // Logger.kids 462 // - has a weak reference to each direct child Logger; this 463 // includes anonymous and named Loggers 464 // - anonymous Loggers are always children of the rootLogger 465 // which is a strong reference; rootLogger.kids keeps the 466 // LoggerWeakRef objects for the anonymous Loggers around 467 // until we can deal with the book keeping. 468 // 469 final class LoggerWeakRef extends WeakReference<Logger> { 470 private String name; // for namedLoggers cleanup 471 private LogNode node; // for loggerRef cleanup 472 private WeakReference<Logger> parentRef; // for kids cleanup 473 474 LoggerWeakRef(Logger logger) { 475 super(logger, loggerRefQueue); 476 477 name = logger.getName(); // save for namedLoggers cleanup 478 } 479 480 // dispose of this LoggerWeakRef object 481 void dispose() { 482 if (node != null) { 483 // if we have a LogNode, then we were a named Logger 484 // so clear namedLoggers weak ref to us 485 manager.namedLoggers.remove(name); 486 name = null; // clear our ref to the Logger's name 487 488 node.loggerRef = null; // clear LogNode's weak ref to us 489 node = null; // clear our ref to LogNode 490 } 491 492 if (parentRef != null) { 493 // this LoggerWeakRef has or had a parent Logger 494 Logger parent = parentRef.get(); 495 if (parent != null) { 496 // the parent Logger is still there so clear the 497 // parent Logger's weak ref to us 498 parent.removeChildLogger(this); 499 } 500 parentRef = null; // clear our weak ref to the parent Logger 501 } 502 } 503 504 // set the node field to the specified value 505 void setNode(LogNode node) { 506 this.node = node; 507 } 508 509 // set the parentRef field to the specified value 510 void setParentRef(WeakReference<Logger> parentRef) { 511 this.parentRef = parentRef; 512 } 513 } 514 515 // Package-level method. 516 // Drain some Logger objects that have been GC'ed. 517 // 518 // drainLoggerRefQueueBounded() is called by addLogger() below 519 // and by Logger.getAnonymousLogger(String) so we'll drain up to 520 // MAX_ITERATIONS GC'ed Loggers for every Logger we add. 521 // 522 // On a WinXP VMware client, a MAX_ITERATIONS value of 400 gives 523 // us about a 50/50 mix in increased weak ref counts versus 524 // decreased weak ref counts in the AnonLoggerWeakRefLeak test. 525 // Here are stats for cleaning up sets of 400 anonymous Loggers: 526 // - test duration 1 minute 527 // - sample size of 125 sets of 400 528 // - average: 1.99 ms 529 // - minimum: 0.57 ms 530 // - maximum: 25.3 ms 531 // 532 // The same config gives us a better decreased weak ref count 533 // than increased weak ref count in the LoggerWeakRefLeak test. 534 // Here are stats for cleaning up sets of 400 named Loggers: 535 // - test duration 2 minutes 536 // - sample size of 506 sets of 400 537 // - average: 0.57 ms 538 // - minimum: 0.02 ms 539 // - maximum: 10.9 ms 540 // 541 private final static int MAX_ITERATIONS = 400; 542 final synchronized void drainLoggerRefQueueBounded() { 543 for (int i = 0; i < MAX_ITERATIONS; i++) { 544 if (loggerRefQueue == null) { 545 // haven't finished loading LogManager yet 546 break; 547 } 548 549 LoggerWeakRef ref = (LoggerWeakRef) loggerRefQueue.poll(); 550 if (ref == null) { 551 break; 552 } 553 // a Logger object has been GC'ed so clean it up 554 ref.dispose(); 555 } 556 } 557 558 /** 559 * Add a named logger. This does nothing and returns false if a logger 560 * with the same name is already registered. 561 * <p> 562 * The Logger factory methods call this method to register each 563 * newly created Logger. 564 * <p> 565 * The application should retain its own reference to the Logger 566 * object to avoid it being garbage collected. The LogManager 567 * may only retain a weak reference. 568 * 569 * @param logger the new logger. 570 * @return true if the argument logger was registered successfully, 571 * false if a logger of that name already exists. 572 * @exception NullPointerException if the logger name is null. 573 */ 574 public synchronized boolean addLogger(Logger logger) { 575 final String name = logger.getName(); 576 if (name == null) { 577 throw new NullPointerException(); 578 } 579 580 // cleanup some Loggers that have been GC'ed 581 drainLoggerRefQueueBounded(); 582 583 LoggerWeakRef ref = namedLoggers.get(name); 584 if (ref != null) { 585 if (ref.get() == null) { 586 // It's possible that the Logger was GC'ed after the 587 // drainLoggerRefQueueBounded() call above so allow 588 // a new one to be registered. 589 namedLoggers.remove(name); 590 } else { 591 // We already have a registered logger with the given name. 592 return false; 593 } 594 } 595 596 // We're adding a new logger. 597 // Note that we are creating a weak reference here. 598 ref = new LoggerWeakRef(logger); 599 namedLoggers.put(name, ref); 600 601 // Apply any initial level defined for the new logger. 602 Level level = getLevelProperty(name+".level", null); 603 if (level != null) { 604 doSetLevel(logger, level); 605 } 606 607 // Do we have a per logger handler too? 608 // Note: this will add a 200ms penalty 609 loadLoggerHandlers(logger, name, name+".handlers"); 610 processParentHandlers(logger, name); 611 612 // Find the new node and its parent. 613 LogNode node = findNode(name); 614 node.loggerRef = ref; 615 Logger parent = null; 616 LogNode nodep = node.parent; 617 while (nodep != null) { 618 LoggerWeakRef nodeRef = nodep.loggerRef; 619 if (nodeRef != null) { 620 parent = nodeRef.get(); 621 if (parent != null) { 622 break; 623 } 624 } 625 nodep = nodep.parent; 626 } 627 628 if (parent != null) { 629 doSetParent(logger, parent); 630 } 631 // Walk over the children and tell them we are their new parent. 632 node.walkAndSetParent(logger); 633 634 // new LogNode is ready so tell the LoggerWeakRef about it 635 ref.setNode(node); 636 637 return true; 638 } 639 640 641 // Private method to set a level on a logger. 642 // If necessary, we raise privilege before doing the call. 643 private static void doSetLevel(final Logger logger, final Level level) { 644 SecurityManager sm = System.getSecurityManager(); 645 if (sm == null) { 646 // There is no security manager, so things are easy. 647 logger.setLevel(level); 648 return; 649 } 650 // There is a security manager. Raise privilege before 651 // calling setLevel. 652 AccessController.doPrivileged(new PrivilegedAction<Object>() { 653 public Object run() { 654 logger.setLevel(level); 655 return null; 656 }}); 657 } 658 659 660 661 // Private method to set a parent on a logger. 662 // If necessary, we raise privilege before doing the setParent call. 663 private static void doSetParent(final Logger logger, final Logger parent) { 664 SecurityManager sm = System.getSecurityManager(); 665 if (sm == null) { 666 // There is no security manager, so things are easy. 667 logger.setParent(parent); 668 return; 669 } 670 // There is a security manager. Raise privilege before 671 // calling setParent. 672 AccessController.doPrivileged(new PrivilegedAction<Object>() { 673 public Object run() { 674 logger.setParent(parent); 675 return null; 676 }}); 677 } 678 679 // Find a node in our tree of logger nodes. 680 // If necessary, create it. 681 private LogNode findNode(String name) { 682 if (name == null || name.equals("")) { 683 return root; 684 } 685 LogNode node = root; 686 while (name.length() > 0) { 687 int ix = name.indexOf("."); 688 String head; 689 if (ix > 0) { 690 head = name.substring(0,ix); 691 name = name.substring(ix+1); 692 } else { 693 head = name; 694 name = ""; 695 } 696 if (node.children == null) { 697 node.children = new HashMap<>(); 698 } 699 LogNode child = node.children.get(head); 700 if (child == null) { 701 child = new LogNode(node); 702 node.children.put(head, child); 703 } 704 node = child; 705 } 706 return node; 707 } 708 709 /** 710 * Method to find a named logger. 711 * <p> 712 * Note that since untrusted code may create loggers with 713 * arbitrary names this method should not be relied on to 714 * find Loggers for security sensitive logging. 715 * It is also important to note that the Logger associated with the 716 * String {@code name} may be garbage collected at any time if there 717 * is no strong reference to the Logger. The caller of this method 718 * must check the return value for null in order to properly handle 719 * the case where the Logger has been garbage collected. 720 * <p> 721 * @param name name of the logger 722 * @return matching logger or null if none is found 723 */ 724 public synchronized Logger getLogger(String name) { 725 LoggerWeakRef ref = namedLoggers.get(name); 726 if (ref == null) { 727 return null; 728 } 729 Logger logger = ref.get(); 730 if (logger == null) { 731 // Hashtable holds stale weak reference 732 // to a logger which has been GC-ed. 733 namedLoggers.remove(name); 734 } 735 return logger; 736 } 737 738 /** 739 * Get an enumeration of known logger names. 740 * <p> 741 * Note: Loggers may be added dynamically as new classes are loaded. 742 * This method only reports on the loggers that are currently registered. 743 * It is also important to note that this method only returns the name 744 * of a Logger, not a strong reference to the Logger itself. 745 * The returned String does nothing to prevent the Logger from being 746 * garbage collected. In particular, if the returned name is passed 747 * to {@code LogManager.getLogger()}, then the caller must check the 748 * return value from {@code LogManager.getLogger()} for null to properly 749 * handle the case where the Logger has been garbage collected in the 750 * time since its name was returned by this method. 751 * <p> 752 * @return enumeration of logger name strings 753 */ 754 public synchronized Enumeration<String> getLoggerNames() { 755 return namedLoggers.keys(); 756 } 757 758 /** 759 * Reinitialize the logging properties and reread the logging configuration. 760 * <p> 761 * The same rules are used for locating the configuration properties 762 * as are used at startup. So normally the logging properties will 763 * be re-read from the same file that was used at startup. 764 * <P> 765 * Any log level definitions in the new configuration file will be 766 * applied using Logger.setLevel(), if the target Logger exists. 767 * <p> 768 * A PropertyChangeEvent will be fired after the properties are read. 769 * 770 * @exception SecurityException if a security manager exists and if 771 * the caller does not have LoggingPermission("control"). 772 * @exception IOException if there are IO problems reading the configuration. 773 */ 774 public void readConfiguration() throws IOException, SecurityException { 775 checkAccess(); 776 777 // if a configuration class is specified, load it and use it. 778 String cname = System.getProperty("java.util.logging.config.class"); 779 if (cname != null) { 780 try { 781 // Instantiate the named class. It is its constructor's 782 // responsibility to initialize the logging configuration, by 783 // calling readConfiguration(InputStream) with a suitable stream. 784 try { 785 Class clz = ClassLoader.getSystemClassLoader().loadClass(cname); 786 clz.newInstance(); 787 return; 788 } catch (ClassNotFoundException ex) { 789 Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname); 790 clz.newInstance(); 791 return; 792 } 793 } catch (Exception ex) { 794 System.err.println("Logging configuration class \"" + cname + "\" failed"); 795 System.err.println("" + ex); 796 // keep going and useful config file. 797 } 798 } 799 800 String fname = System.getProperty("java.util.logging.config.file"); 801 if (fname == null) { 802 fname = System.getProperty("java.home"); 803 if (fname == null) { 804 throw new Error("Can't find java.home ??"); 805 } 806 File f = new File(fname, "lib"); 807 f = new File(f, "logging.properties"); 808 fname = f.getCanonicalPath(); 809 } 810 InputStream in = new FileInputStream(fname); 811 BufferedInputStream bin = new BufferedInputStream(in); 812 try { 813 readConfiguration(bin); 814 } finally { 815 if (in != null) { 816 in.close(); 817 } 818 } 819 } 820 821 /** 822 * Reset the logging configuration. 823 * <p> 824 * For all named loggers, the reset operation removes and closes 825 * all Handlers and (except for the root logger) sets the level 826 * to null. The root logger's level is set to Level.INFO. 827 * 828 * @exception SecurityException if a security manager exists and if 829 * the caller does not have LoggingPermission("control"). 830 */ 831 832 public void reset() throws SecurityException { 833 checkAccess(); 834 synchronized (this) { 835 props = new Properties(); 836 // Since we are doing a reset we no longer want to initialize 837 // the global handlers, if they haven't been initialized yet. 838 initializedGlobalHandlers = true; 839 } 840 Enumeration enum_ = getLoggerNames(); 841 while (enum_.hasMoreElements()) { 842 String name = (String)enum_.nextElement(); 843 resetLogger(name); 844 } 845 } 846 847 848 // Private method to reset an individual target logger. 849 private void resetLogger(String name) { 850 Logger logger = getLogger(name); 851 if (logger == null) { 852 return; 853 } 854 // Close all the Logger's handlers. 855 Handler[] targets = logger.getHandlers(); 856 for (int i = 0; i < targets.length; i++) { 857 Handler h = targets[i]; 858 logger.removeHandler(h); 859 try { 860 h.close(); 861 } catch (Exception ex) { 862 // Problems closing a handler? Keep going... 863 } 864 } 865 if (name != null && name.equals("")) { 866 // This is the root logger. 867 logger.setLevel(defaultLevel); 868 } else { 869 logger.setLevel(null); 870 } 871 } 872 873 // get a list of whitespace separated classnames from a property. 874 private String[] parseClassNames(String propertyName) { 875 String hands = getProperty(propertyName); 876 if (hands == null) { 877 return new String[0]; 878 } 879 hands = hands.trim(); 880 int ix = 0; 881 Vector<String> result = new Vector<>(); 882 while (ix < hands.length()) { 883 int end = ix; 884 while (end < hands.length()) { 885 if (Character.isWhitespace(hands.charAt(end))) { 886 break; 887 } 888 if (hands.charAt(end) == ',') { 889 break; 890 } 891 end++; 892 } 893 String word = hands.substring(ix, end); 894 ix = end+1; 895 word = word.trim(); 896 if (word.length() == 0) { 897 continue; 898 } 899 result.add(word); 900 } 901 return result.toArray(new String[result.size()]); 902 } 903 904 /** 905 * Reinitialize the logging properties and reread the logging configuration 906 * from the given stream, which should be in java.util.Properties format. 907 * A PropertyChangeEvent will be fired after the properties are read. 908 * <p> 909 * Any log level definitions in the new configuration file will be 910 * applied using Logger.setLevel(), if the target Logger exists. 911 * 912 * @param ins stream to read properties from 913 * @exception SecurityException if a security manager exists and if 914 * the caller does not have LoggingPermission("control"). 915 * @exception IOException if there are problems reading from the stream. 916 */ 917 public void readConfiguration(InputStream ins) throws IOException, SecurityException { 918 checkAccess(); 919 reset(); 920 921 // Load the properties 922 props.load(ins); 923 // Instantiate new configuration objects. 924 String names[] = parseClassNames("config"); 925 926 for (int i = 0; i < names.length; i++) { 927 String word = names[i]; 928 try { 929 Class clz = ClassLoader.getSystemClassLoader().loadClass(word); 930 clz.newInstance(); 931 } catch (Exception ex) { 932 System.err.println("Can't load config class \"" + word + "\""); 933 System.err.println("" + ex); 934 // ex.printStackTrace(); 935 } 936 } 937 938 // Set levels on any pre-existing loggers, based on the new properties. 939 setLevelsOnExistingLoggers(); 940 941 // Notify any interested parties that our properties have changed. 942 changes.firePropertyChange(null, null, null); 943 944 // Note that we need to reinitialize global handles when 945 // they are first referenced. 946 synchronized (this) { 947 initializedGlobalHandlers = false; 948 } 949 } 950 951 /** 952 * Get the value of a logging property. 953 * The method returns null if the property is not found. 954 * @param name property name 955 * @return property value 956 */ 957 public String getProperty(String name) { 958 return props.getProperty(name); 959 } 960 961 // Package private method to get a String property. 962 // If the property is not defined we return the given 963 // default value. 964 String getStringProperty(String name, String defaultValue) { 965 String val = getProperty(name); 966 if (val == null) { 967 return defaultValue; 968 } 969 return val.trim(); 970 } 971 972 // Package private method to get an integer property. 973 // If the property is not defined or cannot be parsed 974 // we return the given default value. 975 int getIntProperty(String name, int defaultValue) { 976 String val = getProperty(name); 977 if (val == null) { 978 return defaultValue; 979 } 980 try { 981 return Integer.parseInt(val.trim()); 982 } catch (Exception ex) { 983 return defaultValue; 984 } 985 } 986 987 // Package private method to get a boolean property. 988 // If the property is not defined or cannot be parsed 989 // we return the given default value. 990 boolean getBooleanProperty(String name, boolean defaultValue) { 991 String val = getProperty(name); 992 if (val == null) { 993 return defaultValue; 994 } 995 val = val.toLowerCase(); 996 if (val.equals("true") || val.equals("1")) { 997 return true; 998 } else if (val.equals("false") || val.equals("0")) { 999 return false; 1000 } 1001 return defaultValue; 1002 } 1003 1004 // Package private method to get a Level property. 1005 // If the property is not defined or cannot be parsed 1006 // we return the given default value. 1007 Level getLevelProperty(String name, Level defaultValue) { 1008 String val = getProperty(name); 1009 if (val == null) { 1010 return defaultValue; 1011 } 1012 try { 1013 return Level.parse(val.trim()); 1014 } catch (Exception ex) { 1015 return defaultValue; 1016 } 1017 } 1018 1019 // Package private method to get a filter property. 1020 // We return an instance of the class named by the "name" 1021 // property. If the property is not defined or has problems 1022 // we return the defaultValue. 1023 Filter getFilterProperty(String name, Filter defaultValue) { 1024 String val = getProperty(name); 1025 try { 1026 if (val != null) { 1027 Class clz = ClassLoader.getSystemClassLoader().loadClass(val); 1028 return (Filter) clz.newInstance(); 1029 } 1030 } catch (Exception ex) { 1031 // We got one of a variety of exceptions in creating the 1032 // class or creating an instance. 1033 // Drop through. 1034 } 1035 // We got an exception. Return the defaultValue. 1036 return defaultValue; 1037 } 1038 1039 1040 // Package private method to get a formatter property. 1041 // We return an instance of the class named by the "name" 1042 // property. If the property is not defined or has problems 1043 // we return the defaultValue. 1044 Formatter getFormatterProperty(String name, Formatter defaultValue) { 1045 String val = getProperty(name); 1046 try { 1047 if (val != null) { 1048 Class clz = ClassLoader.getSystemClassLoader().loadClass(val); 1049 return (Formatter) clz.newInstance(); 1050 } 1051 } catch (Exception ex) { 1052 // We got one of a variety of exceptions in creating the 1053 // class or creating an instance. 1054 // Drop through. 1055 } 1056 // We got an exception. Return the defaultValue. 1057 return defaultValue; 1058 } 1059 1060 // Private method to load the global handlers. 1061 // We do the real work lazily, when the global handlers 1062 // are first used. 1063 private synchronized void initializeGlobalHandlers() { 1064 if (initializedGlobalHandlers) { 1065 return; 1066 } 1067 1068 initializedGlobalHandlers = true; 1069 1070 if (deathImminent) { 1071 // Aaargh... 1072 // The VM is shutting down and our exit hook has been called. 1073 // Avoid allocating global handlers. 1074 return; 1075 } 1076 loadLoggerHandlers(rootLogger, null, "handlers"); 1077 } 1078 1079 1080 private Permission ourPermission = new LoggingPermission("control", null); 1081 1082 /** 1083 * Check that the current context is trusted to modify the logging 1084 * configuration. This requires LoggingPermission("control"). 1085 * <p> 1086 * If the check fails we throw a SecurityException, otherwise 1087 * we return normally. 1088 * 1089 * @exception SecurityException if a security manager exists and if 1090 * the caller does not have LoggingPermission("control"). 1091 */ 1092 public void checkAccess() throws SecurityException { 1093 SecurityManager sm = System.getSecurityManager(); 1094 if (sm == null) { 1095 return; 1096 } 1097 sm.checkPermission(ourPermission); 1098 } 1099 1100 // Nested class to represent a node in our tree of named loggers. 1101 private static class LogNode { 1102 HashMap<String,LogNode> children; 1103 LoggerWeakRef loggerRef; 1104 LogNode parent; 1105 1106 LogNode(LogNode parent) { 1107 this.parent = parent; 1108 } 1109 1110 // Recursive method to walk the tree below a node and set 1111 // a new parent logger. 1112 void walkAndSetParent(Logger parent) { 1113 if (children == null) { 1114 return; 1115 } 1116 Iterator<LogNode> values = children.values().iterator(); 1117 while (values.hasNext()) { 1118 LogNode node = values.next(); 1119 LoggerWeakRef ref = node.loggerRef; 1120 Logger logger = (ref == null) ? null : ref.get(); 1121 if (logger == null) { 1122 node.walkAndSetParent(parent); 1123 } else { 1124 doSetParent(logger, parent); 1125 } 1126 } 1127 } 1128 } 1129 1130 // We use a subclass of Logger for the root logger, so 1131 // that we only instantiate the global handlers when they 1132 // are first needed. 1133 private class RootLogger extends Logger { 1134 1135 private RootLogger() { 1136 super("", null); 1137 setLevel(defaultLevel); 1138 } 1139 1140 public void log(LogRecord record) { 1141 // Make sure that the global handlers have been instantiated. 1142 initializeGlobalHandlers(); 1143 super.log(record); 1144 } 1145 1146 public void addHandler(Handler h) { 1147 initializeGlobalHandlers(); 1148 super.addHandler(h); 1149 } 1150 1151 public void removeHandler(Handler h) { 1152 initializeGlobalHandlers(); 1153 super.removeHandler(h); 1154 } 1155 1156 public Handler[] getHandlers() { 1157 initializeGlobalHandlers(); 1158 return super.getHandlers(); 1159 } 1160 } 1161 1162 1163 // Private method to be called when the configuration has 1164 // changed to apply any level settings to any pre-existing loggers. 1165 synchronized private void setLevelsOnExistingLoggers() { 1166 Enumeration enum_ = props.propertyNames(); 1167 while (enum_.hasMoreElements()) { 1168 String key = (String)enum_.nextElement(); 1169 if (!key.endsWith(".level")) { 1170 // Not a level definition. 1171 continue; 1172 } 1173 int ix = key.length() - 6; 1174 String name = key.substring(0, ix); 1175 Level level = getLevelProperty(key, null); 1176 if (level == null) { 1177 System.err.println("Bad level value for property: " + key); 1178 continue; 1179 } 1180 Logger l = getLogger(name); 1181 if (l == null) { 1182 continue; 1183 } 1184 l.setLevel(level); 1185 } 1186 } 1187 1188 // Management Support 1189 private static LoggingMXBean loggingMXBean = null; 1190 /** 1191 * String representation of the 1192 * {@link javax.management.ObjectName} for the management interface 1193 * for the logging facility. 1194 * 1195 * @see java.lang.management.PlatformLoggingMXBean 1196 * @see java.util.logging.LoggingMXBean 1197 * 1198 * @since 1.5 1199 */ 1200 public final static String LOGGING_MXBEAN_NAME 1201 = "java.util.logging:type=Logging"; 1202 1203 /** 1204 * Returns <tt>LoggingMXBean</tt> for managing loggers. 1205 * An alternative way to manage loggers is through the 1206 * {@link java.lang.management.PlatformLoggingMXBean} interface 1207 * that can be obtained by calling: 1208 * <pre> 1209 * PlatformLoggingMXBean logging = {@link java.lang.management.ManagementFactory#getPlatformMXBean(Class) 1210 * ManagementFactory.getPlatformMXBean}(PlatformLoggingMXBean.class); 1211 * </pre> 1212 * 1213 * @return a {@link LoggingMXBean} object. 1214 * 1215 * @see java.lang.management.PlatformLoggingMXBean 1216 * @since 1.5 1217 */ 1218 public static synchronized LoggingMXBean getLoggingMXBean() { 1219 if (loggingMXBean == null) { 1220 loggingMXBean = new Logging(); 1221 } 1222 return loggingMXBean; 1223 } 1224 1225 }