1. /*
  2. * @(#)MemoryHandler.java 1.18 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util.logging;
  8. /**
  9. * <tt>Handler</tt> that buffers requests in a circular buffer in memory.
  10. * <p>
  11. * Normally this <tt>Handler</tt> simply stores incoming <tt>LogRecords</tt>
  12. * into its memory buffer and discards earlier records. This buffering
  13. * is very cheap and avoids formatting costs. On certain trigger
  14. * conditions, the <tt>MemoryHandler</tt> will push out its current buffer
  15. * contents to a target <tt>Handler</tt>, which will typically publish
  16. * them to the outside world.
  17. * <p>
  18. * There are three main models for triggering a push of the buffer:
  19. * <ul>
  20. * <li>
  21. * An incoming <tt>LogRecord</tt> has a type that is greater than
  22. * a pre-defined level, the <tt>pushLevel</tt>.
  23. * <li>
  24. * An external class calls the <tt>push</tt> method explicitly.
  25. * <li>
  26. * A subclass overrides the <tt>log</tt> method and scans each incoming
  27. * <tt>LogRecord</tt> and calls <tt>push</tt> if a record matches some
  28. * desired criteria.
  29. * </ul>
  30. * <p>
  31. * <b>Configuration:</b>
  32. * By default each <tt>MemoryHandler</tt> is initialized using the following
  33. * LogManager configuration properties. If properties are not defined
  34. * (or have invalid values) then the specified default values are used.
  35. * <ul>
  36. * <li> java.util.logging.MemoryHandler.level
  37. * specifies the level for the <tt>Handler</tt>
  38. * (defaults to <tt>Level.ALL</tt>).
  39. * <li> java.util.logging.MemoryHandler.filter
  40. * specifies the name of a <tt>Filter</tt> class to use
  41. * (defaults to no <tt>Filter</tt>).
  42. * <li> java.util.logging.MemoryHandler.size
  43. * defines the buffer size (defaults to 1000).
  44. * <li> java.util.logging.MemoryHandler.push
  45. * defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>).
  46. * <li> java.util.logging.MemoryHandler.target
  47. * specifies the name of the target <tt>Handler </tt> class.
  48. * (no default).
  49. * </ul>
  50. *
  51. * @version 1.18, 01/23/03
  52. * @since 1.4
  53. */
  54. public class MemoryHandler extends Handler {
  55. private final static int DEFAULT_SIZE = 1000;
  56. private Level pushLevel;
  57. private int size;
  58. private Handler target;
  59. private LogRecord buffer[];
  60. int start, count;
  61. // Private method to configure a ConsoleHandler from LogManager
  62. // properties and/or default values as specified in the class
  63. // javadoc.
  64. private void configure() {
  65. LogManager manager = LogManager.getLogManager();
  66. String cname = MemoryHandler.class.getName();
  67. pushLevel = manager.getLevelProperty(cname +".push", Level.SEVERE);
  68. size = manager.getIntProperty(cname + ".size", DEFAULT_SIZE);
  69. if (size <= 0) {
  70. size = DEFAULT_SIZE;
  71. }
  72. setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
  73. setFilter(manager.getFilterProperty(cname +".filter", null));
  74. setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
  75. }
  76. /**
  77. * Create a <tt>MemoryHandler</tt> and configure it based on
  78. * <tt>LogManager</tt> configuration properties.
  79. */
  80. public MemoryHandler() {
  81. sealed = false;
  82. configure();
  83. sealed = true;
  84. String name = "???";
  85. try {
  86. LogManager manager = LogManager.getLogManager();
  87. name = manager.getProperty("java.util.logging.MemoryHandler.target");
  88. Class clz = ClassLoader.getSystemClassLoader().loadClass(name);
  89. target = (Handler) clz.newInstance();
  90. } catch (Exception ex) {
  91. System.err.println("MemoryHandler can't load handler \"" + name + "\"");
  92. System.err.println("" + ex);
  93. throw new RuntimeException("Can't load " + name);
  94. }
  95. init();
  96. }
  97. // Initialize. Size is a count of LogRecords.
  98. private void init() {
  99. buffer = new LogRecord[size];
  100. start = 0;
  101. count = 0;
  102. }
  103. /**
  104. * Create a <tt>MemoryHandler</tt>.
  105. * <p>
  106. * The <tt>MemoryHandler</tt> is configured based on <tt>LogManager</tt>
  107. * properties (or their default values) except that the given <tt>pushLevel</tt>
  108. * argument and buffer size argument are used.
  109. *
  110. * @param target the Handler to which to publish output.
  111. * @param size the number of log records to buffer (must be greater than zero)
  112. * @param pushLevel message level to push on
  113. *
  114. * @throws IllegalArgumentException is size is <= 0
  115. */
  116. public MemoryHandler(Handler target, int size, Level pushLevel) {
  117. if (target == null || pushLevel == null) {
  118. throw new NullPointerException();
  119. }
  120. if (size <= 0) {
  121. throw new IllegalArgumentException();
  122. }
  123. sealed = false;
  124. configure();
  125. sealed = true;
  126. this.target = target;
  127. this.pushLevel = pushLevel;
  128. this.size = size;
  129. init();
  130. }
  131. /**
  132. * Store a <tt>LogRecord</tt> in an internal buffer.
  133. * <p>
  134. * If there is a <tt>Filter</tt>, its <tt>isLoggable</tt>
  135. * method is called to check if the given log record is loggable.
  136. * If not we return. Otherwise the given record is copied into
  137. * an internal circular buffer. Then the record's level property is
  138. * compared with the <tt>pushLevel</tt>. If the given level is
  139. * greater than or equal to the <tt>pushLevel</tt> then <tt>push</tt>
  140. * is called to write all buffered records to the target output
  141. * <tt>Handler</tt>.
  142. *
  143. * @param record description of the log event
  144. */
  145. public synchronized void publish(LogRecord record) {
  146. if (!isLoggable(record)) {
  147. return;
  148. }
  149. int ix = (start+count)%buffer.length;
  150. buffer[ix] = record;
  151. if (count < buffer.length) {
  152. count++;
  153. } else {
  154. start++;
  155. }
  156. if (record.getLevel().intValue() >= pushLevel.intValue()) {
  157. push();
  158. }
  159. }
  160. /**
  161. * Push any buffered output to the target <tt>Handler</tt>.
  162. * <p>
  163. * The buffer is then cleared.
  164. */
  165. public synchronized void push() {
  166. for (int i = 0; i < count; i++) {
  167. int ix = (start+i)%buffer.length;
  168. LogRecord record = buffer[ix];
  169. target.publish(record);
  170. }
  171. // Empty the buffer.
  172. start = 0;
  173. count = 0;
  174. }
  175. /**
  176. * Causes a flush on the target <tt>Handler</tt>.
  177. * <p>
  178. * Note that the current contents of the <tt>MemoryHandler</tt>
  179. * buffer are <b>not</b> written out. That requires a "push".
  180. */
  181. public void flush() {
  182. target.flush();
  183. }
  184. /**
  185. * Close the <tt>Handler</tt> and free all associated resources.
  186. * This will also close the target <tt>Handler</tt>.
  187. *
  188. * @exception SecurityException if a security manager exists and if
  189. * the caller does not have <tt>LoggingPermission("control")</tt>.
  190. */
  191. public void close() throws SecurityException {
  192. target.close();
  193. setLevel(Level.OFF);
  194. }
  195. /**
  196. * Set the <tt>pushLevel</tt>. After a <tt>LogRecord</tt> is copied
  197. * into our internal buffer, if its level is greater than or equal to
  198. * the <tt>pushLevel</tt>, then <tt>push</tt> will be called.
  199. *
  200. * @param newLevel the new value of the <tt>pushLevel</tt>
  201. * @exception SecurityException if a security manager exists and if
  202. * the caller does not have <tt>LoggingPermission("control")</tt>.
  203. */
  204. public void setPushLevel(Level newLevel) throws SecurityException {
  205. if (newLevel == null) {
  206. throw new NullPointerException();
  207. }
  208. LogManager manager = LogManager.getLogManager();
  209. checkAccess();
  210. pushLevel = newLevel;
  211. }
  212. /**
  213. * Get the <tt>pushLevel</tt>.
  214. *
  215. * @return the value of the <tt>pushLevel</tt>
  216. */
  217. public synchronized Level getPushLevel() {
  218. return pushLevel;
  219. }
  220. /**
  221. * Check if this <tt>Handler</tt> would actually log a given
  222. * <tt>LogRecord</tt> into its internal buffer.
  223. * <p>
  224. * This method checks if the <tt>LogRecord</tt> has an appropriate level and
  225. * whether it satisfies any <tt>Filter</tt>. However it does <b>not</b>
  226. * check whether the <tt>LogRecord</tt> would result in a "push" of the
  227. * buffer contents.
  228. * <p>
  229. * @param record a <tt>LogRecord</tt>
  230. * @return true if the <tt>LogRecord</tt> would be logged.
  231. *
  232. */
  233. public boolean isLoggable(LogRecord record) {
  234. return super.isLoggable(record);
  235. }
  236. }