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