1. /*
  2. * @(#)LogManager.java 1.46 04/06/07
  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 "<logger>.handlers". This defines a whitespace or
  80. * comma separated list of class names for handlers classes to
  81. * load and register as handlers to the specified logger. Each class
  82. * name must be for a Handler class which has a default constructor.
  83. * Note that these Handlers may be created lazily, when they are
  84. * first used.
  85. *
  86. * <li>A property "<logger>.useParentHandlers". This defines a boolean
  87. * value. By default every logger calls its parent in addition to
  88. * handling the logging message itself, this often result in messages
  89. * being handled by the root logger as well. When setting this property
  90. * to false a Handler needs to be configured for this logger otherwise
  91. * no logging messages are delivered.
  92. *
  93. * <li>A property "config". This property is intended to allow
  94. * arbitrary configuration code to be run. The property defines a
  95. * whitespace separated list of class names. A new instance will be
  96. * created for each named class. The default constructor of each class
  97. * may execute arbitrary code to update the logging configuration, such as
  98. * setting logger levels, adding handlers, adding filters, etc.
  99. * </ul>
  100. * <p>
  101. * Note that all classes loaded during LogManager configuration are
  102. * first searched on the system class path before any user class path.
  103. * That includes the LogManager class, any config classes, and any
  104. * handler classes.
  105. * <p>
  106. * Loggers are organized into a naming hierarchy based on their
  107. * dot separated names. Thus "a.b.c" is a child of "a.b", but
  108. * "a.b1" and a.b2" are peers.
  109. * <p>
  110. * All properties whose names end with ".level" are assumed to define
  111. * log levels for Loggers. Thus "foo.level" defines a log level for
  112. * the logger called "foo" and (recursively) for any of its children
  113. * in the naming hierarchy. Log Levels are applied in the order they
  114. * are defined in the properties file. Thus level settings for child
  115. * nodes in the tree should come after settings for their parents.
  116. * The property name ".level" can be used to set the level for the
  117. * root of the tree.
  118. * <p>
  119. * All methods on the LogManager object are multi-thread safe.
  120. *
  121. * @version 1.46, 06/07/04
  122. * @since 1.4
  123. */
  124. public class LogManager {
  125. // The global LogManager object
  126. private static LogManager manager;
  127. private final static Handler[] emptyHandlers = { };
  128. private Properties props = new Properties();
  129. private PropertyChangeSupport changes
  130. = new PropertyChangeSupport(LogManager.class);
  131. private final static Level defaultLevel = Level.INFO;
  132. // Table of known loggers. Maps names to Loggers.
  133. private Hashtable<String,Logger> loggers = new Hashtable<String,Logger>();
  134. // Tree of known loggers
  135. private LogNode root = new LogNode(null);
  136. private Logger rootLogger;
  137. // Have we done the primordial reading of the configuration file?
  138. // (Must be done after a suitable amount of java.lang.System
  139. // initialization has been done)
  140. private volatile boolean readPrimordialConfiguration;
  141. // Have we initialized global (root) handlers yet?
  142. // This gets set to false in readConfiguration
  143. private boolean initializedGlobalHandlers = true;
  144. // True if JVM death is imminent and the exit hook has been called.
  145. private boolean deathImminent;
  146. static {
  147. AccessController.doPrivileged(new PrivilegedAction() {
  148. public Object run() {
  149. String cname = null;
  150. try {
  151. cname = System.getProperty("java.util.logging.manager");
  152. if (cname != null) {
  153. try {
  154. Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
  155. manager = (LogManager) clz.newInstance();
  156. } catch (ClassNotFoundException ex) {
  157. Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
  158. manager = (LogManager) clz.newInstance();
  159. }
  160. }
  161. } catch (Exception ex) {
  162. System.err.println("Could not load Logmanager \"" + cname + "\"");
  163. ex.printStackTrace();
  164. }
  165. if (manager == null) {
  166. manager = new LogManager();
  167. }
  168. // Create and retain Logger for the root of the namespace.
  169. manager.rootLogger = manager.new RootLogger();
  170. manager.addLogger(manager.rootLogger);
  171. // We don't call readConfiguration() here, as we may be running
  172. // very early in the JVM startup sequence. Instead readConfiguration
  173. // will be called lazily in getLogManager().
  174. return null;
  175. }
  176. });
  177. }
  178. // This private class is used as a shutdown hook.
  179. // It does a "reset" to close all open handlers.
  180. private class Cleaner extends Thread {
  181. public void run() {
  182. // If the global handlers haven't been initialized yet, we
  183. // don't want to initialize them just so we can close them!
  184. synchronized (LogManager.this) {
  185. // Note that death is imminent.
  186. deathImminent = true;
  187. initializedGlobalHandlers = true;
  188. }
  189. // Do a reset to close all active handlers.
  190. reset();
  191. }
  192. }
  193. /**
  194. * Protected constructor. This is protected so that container applications
  195. * (such as J2EE containers) can subclass the object. It is non-public as
  196. * it is intended that there only be one LogManager object, whose value is
  197. * retrieved by calling Logmanager.getLogManager.
  198. */
  199. protected LogManager() {
  200. // Add a shutdown hook to close the global handlers.
  201. try {
  202. Runtime.getRuntime().addShutdownHook(new Cleaner());
  203. } catch (IllegalStateException e) {
  204. // If the VM is already shutting down,
  205. // We do not need to register shutdownHook.
  206. }
  207. }
  208. /**
  209. * Return the global LogManager object.
  210. */
  211. public static LogManager getLogManager() {
  212. if (manager != null) {
  213. manager.readPrimordialConfiguration();
  214. }
  215. return manager;
  216. }
  217. private void readPrimordialConfiguration() {
  218. if (!readPrimordialConfiguration) {
  219. synchronized (this) {
  220. if (!readPrimordialConfiguration) {
  221. // If System.in/out/err are null, it's a good
  222. // indication that we're still in the
  223. // bootstrapping phase
  224. if (System.out == null) {
  225. return;
  226. }
  227. readPrimordialConfiguration = true;
  228. try {
  229. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  230. public Object run() throws Exception {
  231. readConfiguration();
  232. return null;
  233. }
  234. });
  235. } catch (Exception ex) {
  236. // System.err.println("Can't read logging configuration:");
  237. // ex.printStackTrace();
  238. }
  239. }
  240. }
  241. }
  242. }
  243. /**
  244. * Adds an event listener to be invoked when the logging
  245. * properties are re-read. Adding multiple instances of
  246. * the same event Listener results in multiple entries
  247. * in the property event listener table.
  248. *
  249. * @param l event listener
  250. * @exception SecurityException if a security manager exists and if
  251. * the caller does not have LoggingPermission("control").
  252. * @exception NullPointerException if the PropertyChangeListener is null.
  253. */
  254. public void addPropertyChangeListener(PropertyChangeListener l) throws SecurityException {
  255. if (l == null) {
  256. throw new NullPointerException();
  257. }
  258. checkAccess();
  259. changes.addPropertyChangeListener(l);
  260. }
  261. /**
  262. * Removes an event listener for property change events.
  263. * If the same listener instance has been added to the listener table
  264. * through multiple invocations of <CODE>addPropertyChangeListener</CODE>,
  265. * then an equivalent number of
  266. * <CODE>removePropertyChangeListener</CODE> invocations are required to remove
  267. * all instances of that listener from the listener table.
  268. * <P>
  269. * Returns silently if the given listener is not found.
  270. *
  271. * @param l event listener (can be null)
  272. * @exception SecurityException if a security manager exists and if
  273. * the caller does not have LoggingPermission("control").
  274. */
  275. public void removePropertyChangeListener(PropertyChangeListener l) throws SecurityException {
  276. checkAccess();
  277. changes.removePropertyChangeListener(l);
  278. }
  279. /**
  280. * Add a named logger. This does nothing and returns false if a logger
  281. * with the same name is already registered.
  282. * <p>
  283. * The Logger factory methods call this method to register each
  284. * newly created Logger.
  285. * <p>
  286. * The application should retain its own reference to the Logger
  287. * object to avoid it being garbage collected. The LogManager
  288. * may only retain a weak reference.
  289. *
  290. * @param logger the new logger.
  291. * @return true if the argument logger was registered successfully,
  292. * false if a logger of that name already exists.
  293. * @exception NullPointerException if the logger name is null.
  294. */
  295. public synchronized boolean addLogger(Logger logger) {
  296. final String name = logger.getName();
  297. if (name == null) {
  298. throw new NullPointerException();
  299. }
  300. Logger old = loggers.get(name);
  301. if (old != null) {
  302. // We already have a registered logger with the given name.
  303. return false;
  304. }
  305. // We're adding a new logger.
  306. // Note that we are creating a strong reference here that will
  307. // keep the Logger in existence indefinitely.
  308. loggers.put(name, logger);
  309. // Apply any initial level defined for the new logger.
  310. Level level = getLevelProperty(name+".level", null);
  311. if (level != null) {
  312. doSetLevel(logger, level);
  313. }
  314. // Do we have a per logger handler too?
  315. // Note: this will add a 200ms penalty
  316. if (getProperty(name+".handlers") != null) {
  317. // This code is taken from the root handler initialization
  318. AccessController.doPrivileged(new PrivilegedAction() {
  319. public Object run() {
  320. // Add new per logger handlers.
  321. String names[] = parseClassNames(name+".handlers");
  322. for (int i = 0; i < names.length; i++) {
  323. String word = names[i];
  324. try {
  325. Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
  326. Handler h = (Handler) clz.newInstance();
  327. try {
  328. // Check if there is a property defining the
  329. // this handler's level.
  330. String levs = getProperty(word + ".level");
  331. if (levs != null) {
  332. h.setLevel(Level.parse(levs));
  333. }
  334. boolean useParent = getBooleanProperty(name + ".useParentHandlers", true);
  335. if (!useParent) {
  336. getLogger(name).setUseParentHandlers(false);
  337. }
  338. } catch (Exception ex) {
  339. System.err.println("Can't set level for " + word);
  340. // Probably a bad level. Drop through.
  341. }
  342. // Add this Handler to the logger
  343. getLogger(name).addHandler(h);
  344. } catch (Exception ex) {
  345. System.err.println("Can't load log handler \"" + word + "\"");
  346. System.err.println("" + ex);
  347. ex.printStackTrace();
  348. }
  349. }
  350. return null;
  351. }});
  352. } // do we have per logger handlers
  353. // If any of the logger's parents have levels defined,
  354. // make sure they are instantiated.
  355. int ix = 1;
  356. for (;;) {
  357. int ix2 = name.indexOf(".", ix);
  358. if (ix2 < 0) {
  359. break;
  360. }
  361. String pname = name.substring(0,ix2);
  362. if (getProperty(pname+".level") != null) {
  363. // This pname has a level definition. Make sure it exists.
  364. Logger plogger = Logger.getLogger(pname);
  365. }
  366. // While we are walking up the tree I can check for our
  367. // own root logger and get its handlers initialized too with
  368. // the same code
  369. if (getProperty(pname+".handlers") != null) {
  370. final String nname=pname;
  371. AccessController.doPrivileged(new PrivilegedAction() {
  372. public Object run() {
  373. String names[] = parseClassNames(nname+".handlers");
  374. for (int i = 0; i < names.length; i++) {
  375. String word = names[i];
  376. try {
  377. Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
  378. Handler h = (Handler) clz.newInstance();
  379. try {
  380. // Check if there is a property defining the
  381. // handler's level.
  382. String levs = getProperty(word + ".level");
  383. if (levs != null) {
  384. h.setLevel(Level.parse(levs));
  385. }
  386. } catch (Exception ex) {
  387. System.err.println("Can't set level for " + word);
  388. // Probably a bad level. Drop through.
  389. }
  390. if (getLogger(nname) == null ) {
  391. Logger nplogger=Logger.getLogger(nname);
  392. addLogger(nplogger);
  393. }
  394. boolean useParent = getBooleanProperty(nname + ".useParentHandlers", true);
  395. if (!useParent) {
  396. getLogger(nname).setUseParentHandlers(false);
  397. }
  398. } catch (Exception ex) {
  399. System.err.println("Can't load log handler \"" + word + "\"");
  400. System.err.println("" + ex);
  401. ex.printStackTrace();
  402. }
  403. }
  404. return null;
  405. }});
  406. } //found a parent handler
  407. ix = ix2+1;
  408. }
  409. // Find the new node and its parent.
  410. LogNode node = findNode(name);
  411. node.logger = logger;
  412. Logger parent = null;
  413. LogNode nodep = node.parent;
  414. while (nodep != null) {
  415. if (nodep.logger != null) {
  416. parent = nodep.logger;
  417. break;
  418. }
  419. nodep = nodep.parent;
  420. }
  421. if (parent != null) {
  422. doSetParent(logger, parent);
  423. }
  424. // Walk over the children and tell them we are their new parent.
  425. node.walkAndSetParent(logger);
  426. return true;
  427. }
  428. // Private method to set a level on a logger.
  429. // If necessary, we raise privilege before doing the call.
  430. private static void doSetLevel(final Logger logger, final Level level) {
  431. SecurityManager sm = System.getSecurityManager();
  432. if (sm == null) {
  433. // There is no security manager, so things are easy.
  434. logger.setLevel(level);
  435. return;
  436. }
  437. // There is a security manager. Raise privilege before
  438. // calling setLevel.
  439. AccessController.doPrivileged(new PrivilegedAction() {
  440. public Object run() {
  441. logger.setLevel(level);
  442. return null;
  443. }});
  444. }
  445. // Private method to set a parent on a logger.
  446. // If necessary, we raise privilege before doing the setParent call.
  447. private static void doSetParent(final Logger logger, final Logger parent) {
  448. SecurityManager sm = System.getSecurityManager();
  449. if (sm == null) {
  450. // There is no security manager, so things are easy.
  451. logger.setParent(parent);
  452. return;
  453. }
  454. // There is a security manager. Raise privilege before
  455. // calling setParent.
  456. AccessController.doPrivileged(new PrivilegedAction() {
  457. public Object run() {
  458. logger.setParent(parent);
  459. return null;
  460. }});
  461. }
  462. // Find a node in our tree of logger nodes.
  463. // If necessary, create it.
  464. private LogNode findNode(String name) {
  465. if (name == null || name.equals("")) {
  466. return root;
  467. }
  468. LogNode node = root;
  469. while (name.length() > 0) {
  470. int ix = name.indexOf(".");
  471. String head;
  472. if (ix > 0) {
  473. head = name.substring(0,ix);
  474. name = name.substring(ix+1);
  475. } else {
  476. head = name;
  477. name = "";
  478. }
  479. if (node.children == null) {
  480. node.children = new HashMap<Object,Object>();
  481. }
  482. LogNode child = (LogNode)node.children.get(head);
  483. if (child == null) {
  484. child = new LogNode(node);
  485. node.children.put(head, child);
  486. }
  487. node = child;
  488. }
  489. return node;
  490. }
  491. /**
  492. * Method to find a named logger.
  493. * <p>
  494. * Note that since untrusted code may create loggers with
  495. * arbitrary names this method should not be relied on to
  496. * find Loggers for security sensitive logging.
  497. * <p>
  498. * @param name name of the logger
  499. * @return matching logger or null if none is found
  500. */
  501. public synchronized Logger getLogger(String name) {
  502. return loggers.get(name);
  503. }
  504. /**
  505. * Get an enumeration of known logger names.
  506. * <p>
  507. * Note: Loggers may be added dynamically as new classes are loaded.
  508. * This method only reports on the loggers that are currently registered.
  509. * <p>
  510. * @return enumeration of logger name strings
  511. */
  512. public synchronized Enumeration<String> getLoggerNames() {
  513. return loggers.keys();
  514. }
  515. /**
  516. * Reinitialize the logging properties and reread the logging configuration.
  517. * <p>
  518. * The same rules are used for locating the configuration properties
  519. * as are used at startup. So normally the logging properties will
  520. * be re-read from the same file that was used at startup.
  521. * <P>
  522. * Any log level definitions in the new configuration file will be
  523. * applied using Logger.setLevel(), if the target Logger exists.
  524. * <p>
  525. * A PropertyChangeEvent will be fired after the properties are read.
  526. *
  527. * @exception SecurityException if a security manager exists and if
  528. * the caller does not have LoggingPermission("control").
  529. * @exception IOException if there are IO problems reading the configuration.
  530. */
  531. public void readConfiguration() throws IOException, SecurityException {
  532. checkAccess();
  533. // if a configuration class is specified, load it and use it.
  534. String cname = System.getProperty("java.util.logging.config.class");
  535. if (cname != null) {
  536. try {
  537. // Instantiate the named class. It is its contructor's
  538. // responsibility to initialize the logging configuration, by
  539. // calling readConfiguration(InputStream) with a suitable stream.
  540. try {
  541. Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
  542. clz.newInstance();
  543. return;
  544. } catch (ClassNotFoundException ex) {
  545. Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
  546. clz.newInstance();
  547. return;
  548. }
  549. } catch (Exception ex) {
  550. System.err.println("Logging configuration class \"" + cname + "\" failed");
  551. System.err.println("" + ex);
  552. // keep going and useful config file.
  553. }
  554. }
  555. String fname = System.getProperty("java.util.logging.config.file");
  556. if (fname == null) {
  557. fname = System.getProperty("java.home");
  558. if (fname == null) {
  559. throw new Error("Can't find java.home ??");
  560. }
  561. File f = new File(fname, "lib");
  562. f = new File(f, "logging.properties");
  563. fname = f.getCanonicalPath();
  564. }
  565. InputStream in = new FileInputStream(fname);
  566. BufferedInputStream bin = new BufferedInputStream(in);
  567. try {
  568. readConfiguration(bin);
  569. } finally {
  570. if (in != null) {
  571. in.close();
  572. }
  573. }
  574. }
  575. /**
  576. * Reset the logging configuration.
  577. * <p>
  578. * For all named loggers, the reset operation removes and closes
  579. * all Handlers and (except for the root logger) sets the level
  580. * to null. The root logger's level is set to Level.INFO.
  581. *
  582. * @exception SecurityException if a security manager exists and if
  583. * the caller does not have LoggingPermission("control").
  584. */
  585. public void reset() throws SecurityException {
  586. checkAccess();
  587. synchronized (this) {
  588. props = new Properties();
  589. // Since we are doing a reset we no longer want to initialize
  590. // the global handlers, if they haven't been initialized yet.
  591. initializedGlobalHandlers = true;
  592. }
  593. Enumeration enum_ = getLoggerNames();
  594. while (enum_.hasMoreElements()) {
  595. String name = (String)enum_.nextElement();
  596. resetLogger(name);
  597. }
  598. }
  599. // Private method to reset an individual target logger.
  600. private void resetLogger(String name) {
  601. Logger logger = getLogger(name);
  602. if (logger == null) {
  603. return;
  604. }
  605. // Close all the Logger's handlers.
  606. Handler[] targets = logger.getHandlers();
  607. for (int i = 0; i < targets.length; i++) {
  608. Handler h = targets[i];
  609. logger.removeHandler(h);
  610. try {
  611. h.close();
  612. } catch (Exception ex) {
  613. // Problems closing a handler? Keep going...
  614. }
  615. }
  616. if (name != null && name.equals("")) {
  617. // This is the root logger.
  618. logger.setLevel(defaultLevel);
  619. } else {
  620. logger.setLevel(null);
  621. }
  622. }
  623. // get a list of whitespace separated classnames from a property.
  624. private String[] parseClassNames(String propertyName) {
  625. String hands = getProperty(propertyName);
  626. if (hands == null) {
  627. return new String[0];
  628. }
  629. hands = hands.trim();
  630. int ix = 0;
  631. Vector<String> result = new Vector<String>();
  632. while (ix < hands.length()) {
  633. int end = ix;
  634. while (end < hands.length()) {
  635. if (Character.isWhitespace(hands.charAt(end))) {
  636. break;
  637. }
  638. if (hands.charAt(end) == ',') {
  639. break;
  640. }
  641. end++;
  642. }
  643. String word = hands.substring(ix, end);
  644. ix = end+1;
  645. word = word.trim();
  646. if (word.length() == 0) {
  647. continue;
  648. }
  649. result.add(word);
  650. }
  651. return result.toArray(new String[result.size()]);
  652. }
  653. /**
  654. * Reinitialize the logging properties and reread the logging configuration
  655. * from the given stream, which should be in java.util.Properties format.
  656. * A PropertyChangeEvent will be fired after the properties are read.
  657. * <p>
  658. * Any log level definitions in the new configuration file will be
  659. * applied using Logger.setLevel(), if the target Logger exists.
  660. *
  661. * @param ins stream to read properties from
  662. * @exception SecurityException if a security manager exists and if
  663. * the caller does not have LoggingPermission("control").
  664. * @exception IOException if there are problems reading from the stream.
  665. */
  666. public void readConfiguration(InputStream ins) throws IOException, SecurityException {
  667. checkAccess();
  668. reset();
  669. // Load the properties
  670. props.load(ins);
  671. // Instantiate new configuration objects.
  672. String names[] = parseClassNames("config");
  673. for (int i = 0; i < names.length; i++) {
  674. String word = names[i];
  675. try {
  676. Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
  677. clz.newInstance();
  678. } catch (Exception ex) {
  679. System.err.println("Can't load config class \"" + word + "\"");
  680. System.err.println("" + ex);
  681. // ex.printStackTrace();
  682. }
  683. }
  684. // Set levels on any pre-existing loggers, based on the new properties.
  685. setLevelsOnExistingLoggers();
  686. // Notify any interested parties that our properties have changed.
  687. changes.firePropertyChange(null, null, null);
  688. // Note that we need to reinitialize global handles when
  689. // they are first referenced.
  690. synchronized (this) {
  691. initializedGlobalHandlers = false;
  692. }
  693. }
  694. /**
  695. * Get the value of a logging property.
  696. * The method returns null if the property is not found.
  697. * @param name property name
  698. * @return property value
  699. */
  700. public String getProperty(String name) {
  701. return props.getProperty(name);
  702. }
  703. // Package private method to get a String property.
  704. // If the property is not defined we return the given
  705. // default value.
  706. String getStringProperty(String name, String defaultValue) {
  707. String val = getProperty(name);
  708. if (val == null) {
  709. return defaultValue;
  710. }
  711. return val.trim();
  712. }
  713. // Package private method to get an integer property.
  714. // If the property is not defined or cannot be parsed
  715. // we return the given default value.
  716. int getIntProperty(String name, int defaultValue) {
  717. String val = getProperty(name);
  718. if (val == null) {
  719. return defaultValue;
  720. }
  721. try {
  722. return Integer.parseInt(val.trim());
  723. } catch (Exception ex) {
  724. return defaultValue;
  725. }
  726. }
  727. // Package private method to get a boolean property.
  728. // If the property is not defined or cannot be parsed
  729. // we return the given default value.
  730. boolean getBooleanProperty(String name, boolean defaultValue) {
  731. String val = getProperty(name);
  732. if (val == null) {
  733. return defaultValue;
  734. }
  735. val = val.toLowerCase();
  736. if (val.equals("true") || val.equals("1")) {
  737. return true;
  738. } else if (val.equals("false") || val.equals("0")) {
  739. return false;
  740. }
  741. return defaultValue;
  742. }
  743. // Package private method to get a Level property.
  744. // If the property is not defined or cannot be parsed
  745. // we return the given default value.
  746. Level getLevelProperty(String name, Level defaultValue) {
  747. String val = getProperty(name);
  748. if (val == null) {
  749. return defaultValue;
  750. }
  751. try {
  752. return Level.parse(val.trim());
  753. } catch (Exception ex) {
  754. return defaultValue;
  755. }
  756. }
  757. // Package private method to get a filter property.
  758. // We return an instance of the class named by the "name"
  759. // property. If the property is not defined or has problems
  760. // we return the defaultValue.
  761. Filter getFilterProperty(String name, Filter defaultValue) {
  762. String val = getProperty(name);
  763. try {
  764. if (val != null) {
  765. Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
  766. return (Filter) clz.newInstance();
  767. }
  768. } catch (Exception ex) {
  769. // We got one of a variety of exceptions in creating the
  770. // class or creating an instance.
  771. // Drop through.
  772. }
  773. // We got an exception. Return the defaultValue.
  774. return defaultValue;
  775. }
  776. // Package private method to get a formatter property.
  777. // We return an instance of the class named by the "name"
  778. // property. If the property is not defined or has problems
  779. // we return the defaultValue.
  780. Formatter getFormatterProperty(String name, Formatter defaultValue) {
  781. String val = getProperty(name);
  782. try {
  783. if (val != null) {
  784. Class clz = ClassLoader.getSystemClassLoader().loadClass(val);
  785. return (Formatter) clz.newInstance();
  786. }
  787. } catch (Exception ex) {
  788. // We got one of a variety of exceptions in creating the
  789. // class or creating an instance.
  790. // Drop through.
  791. }
  792. // We got an exception. Return the defaultValue.
  793. return defaultValue;
  794. }
  795. // Private method to load the global handlers.
  796. // We do the real work lazily, when the global handlers
  797. // are first used.
  798. private synchronized void initializeGlobalHandlers() {
  799. if (initializedGlobalHandlers) {
  800. return;
  801. }
  802. initializedGlobalHandlers = true;
  803. if (deathImminent) {
  804. // Aaargh...
  805. // The VM is shutting down and our exit hook has been called.
  806. // Avoid allocating global handlers.
  807. return;
  808. }
  809. // We need to raise privilege here. All our decisions will
  810. // be made based on the logging configuration, which can
  811. // only be modified by trusted code.
  812. AccessController.doPrivileged(new PrivilegedAction() {
  813. public Object run() {
  814. // Add new global handlers.
  815. String names[] = parseClassNames("handlers");
  816. for (int i = 0; i < names.length; i++) {
  817. String word = names[i];
  818. try {
  819. Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
  820. Handler h = (Handler) clz.newInstance();
  821. try {
  822. // Check if there is a property defining the
  823. // handler's level.
  824. String levs = getProperty(word + ".level");
  825. if (levs != null) {
  826. h.setLevel(Level.parse(levs));
  827. }
  828. } catch (Exception ex) {
  829. System.err.println("Can't set level for " + word);
  830. // Probably a bad level. Drop through.
  831. }
  832. rootLogger.addHandler(h);
  833. } catch (Exception ex) {
  834. System.err.println("Can't load log handler \"" + word + "\"");
  835. System.err.println("" + ex);
  836. ex.printStackTrace();
  837. }
  838. }
  839. return null;
  840. }});
  841. }
  842. private Permission ourPermission = new LoggingPermission("control", null);
  843. /**
  844. * Check that the current context is trusted to modify the logging
  845. * configuration. This requires LoggingPermission("control").
  846. * <p>
  847. * If the check fails we throw a SecurityException, otherwise
  848. * we return normally.
  849. *
  850. * @exception SecurityException if a security manager exists and if
  851. * the caller does not have LoggingPermission("control").
  852. */
  853. public void checkAccess() throws SecurityException {
  854. SecurityManager sm = System.getSecurityManager();
  855. if (sm == null) {
  856. return;
  857. }
  858. sm.checkPermission(ourPermission);
  859. }
  860. // Nested class to represent a node in our tree of named loggers.
  861. private static class LogNode {
  862. HashMap<Object,Object> children;
  863. Logger logger;
  864. LogNode parent;
  865. LogNode(LogNode parent) {
  866. this.parent = parent;
  867. }
  868. // Recursive method to walk the tree below a node and set
  869. // a new parent logger.
  870. void walkAndSetParent(Logger parent) {
  871. if (children == null) {
  872. return;
  873. }
  874. Iterator values = children.values().iterator();
  875. while (values.hasNext()) {
  876. LogNode node = (LogNode) values.next();
  877. if (node.logger == null) {
  878. node.walkAndSetParent(parent);
  879. } else {
  880. doSetParent(node.logger, parent);
  881. }
  882. }
  883. }
  884. }
  885. // We use a subclass of Logger for the root logger, so
  886. // that we only instantiate the global handlers when they
  887. // are first needed.
  888. private class RootLogger extends Logger {
  889. private RootLogger() {
  890. super("", null);
  891. setLevel(defaultLevel);
  892. }
  893. public void log(LogRecord record) {
  894. // Make sure that the global handlers have been instantiated.
  895. initializeGlobalHandlers();
  896. super.log(record);
  897. }
  898. public void addHandler(Handler h) {
  899. initializeGlobalHandlers();
  900. super.addHandler(h);
  901. }
  902. public void removeHandler(Handler h) {
  903. initializeGlobalHandlers();
  904. super.removeHandler(h);
  905. }
  906. public Handler[] getHandlers() {
  907. initializeGlobalHandlers();
  908. return super.getHandlers();
  909. }
  910. }
  911. // Private method to be called when the configuration has
  912. // changed to apply any level settings to any pre-existing loggers.
  913. synchronized private void setLevelsOnExistingLoggers() {
  914. Enumeration enum_ = props.propertyNames();
  915. while (enum_.hasMoreElements()) {
  916. String key = (String)enum_.nextElement();
  917. if (!key.endsWith(".level")) {
  918. // Not a level definition.
  919. continue;
  920. }
  921. int ix = key.length() - 6;
  922. String name = key.substring(0, ix);
  923. Level level = getLevelProperty(key, null);
  924. if (level == null) {
  925. System.err.println("Bad level value for property: " + key);
  926. continue;
  927. }
  928. Logger l = getLogger(name);
  929. if (l == null) {
  930. continue;
  931. }
  932. l.setLevel(level);
  933. }
  934. }
  935. // Management Support
  936. private static LoggingMXBean loggingMXBean = null;
  937. /**
  938. * String representation of the
  939. * {@link javax.management.ObjectName} for {@link LoggingMXBean}.
  940. */
  941. public final static String LOGGING_MXBEAN_NAME
  942. = "java.util.logging:type=Logging";
  943. /**
  944. * Returns <tt>LoggingMXBean</tt> for managing loggers.
  945. * The <tt>LoggingMXBean</tt> can also obtained from the
  946. * {@link java.lang.management.ManagementFactory#getPlatformMBeanServer
  947. * platform <tt>MBeanServer</tt>} method.
  948. *
  949. * @return a {@link LoggingMXBean} object.
  950. *
  951. * @see java.lang.management.ManagementFactory
  952. */
  953. public static synchronized LoggingMXBean getLoggingMXBean() {
  954. if (loggingMXBean == null) {
  955. loggingMXBean = new Logging();
  956. }
  957. return loggingMXBean;
  958. }
  959. }