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