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