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