1. /*
  2. * @(#)MemoryHandler.java 1.24 03/12/19
  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. /**
  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. * If no default value is defined then a RuntimeException is thrown.
  36. * <ul>
  37. * <li> java.util.logging.MemoryHandler.level
  38. * specifies the level for the <tt>Handler</tt>
  39. * (defaults to <tt>Level.ALL</tt>).
  40. * <li> java.util.logging.MemoryHandler.filter
  41. * specifies the name of a <tt>Filter</tt> class to use
  42. * (defaults to no <tt>Filter</tt>).
  43. * <li> java.util.logging.MemoryHandler.size
  44. * defines the buffer size (defaults to 1000).
  45. * <li> java.util.logging.MemoryHandler.push
  46. * defines the <tt>pushLevel</tt> (defaults to <tt>level.SEVERE</tt>).
  47. * <li> java.util.logging.MemoryHandler.target
  48. * specifies the name of the target <tt>Handler </tt> class.
  49. * (no default).
  50. * </ul>
  51. *
  52. * @version 1.24, 12/19/03
  53. * @since 1.4
  54. */
  55. public class MemoryHandler extends Handler {
  56. private final static int DEFAULT_SIZE = 1000;
  57. private Level pushLevel;
  58. private int size;
  59. private Handler target;
  60. private LogRecord buffer[];
  61. int start, count;
  62. // Private method to configure a ConsoleHandler from LogManager
  63. // properties and/or default values as specified in the class
  64. // javadoc.
  65. private void configure() {
  66. LogManager manager = LogManager.getLogManager();
  67. String cname = getClass().getName();
  68. pushLevel = manager.getLevelProperty(cname +".push", Level.SEVERE);
  69. size = manager.getIntProperty(cname + ".size", DEFAULT_SIZE);
  70. if (size <= 0) {
  71. size = DEFAULT_SIZE;
  72. }
  73. setLevel(manager.getLevelProperty(cname +".level", Level.ALL));
  74. setFilter(manager.getFilterProperty(cname +".filter", null));
  75. setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
  76. }
  77. /**
  78. * Create a <tt>MemoryHandler</tt> and configure it based on
  79. * <tt>LogManager</tt> configuration properties.
  80. */
  81. public MemoryHandler() {
  82. sealed = false;
  83. configure();
  84. sealed = true;
  85. String name = "???";
  86. try {
  87. LogManager manager = LogManager.getLogManager();
  88. name = manager.getProperty("java.util.logging.MemoryHandler.target");
  89. Class clz = ClassLoader.getSystemClassLoader().loadClass(name);
  90. target = (Handler) clz.newInstance();
  91. } catch (Exception ex) {
  92. throw new RuntimeException("MemoryHandler can't load handler \"" + name + "\"" , ex);
  93. }
  94. init();
  95. }
  96. // Initialize. Size is a count of LogRecords.
  97. private void init() {
  98. buffer = new LogRecord[size];
  99. start = 0;
  100. count = 0;
  101. }
  102. /**
  103. * Create a <tt>MemoryHandler</tt>.
  104. * <p>
  105. * The <tt>MemoryHandler</tt> is configured based on <tt>LogManager</tt>
  106. * properties (or their default values) except that the given <tt>pushLevel</tt>
  107. * argument and buffer size argument are used.
  108. *
  109. * @param target the Handler to which to publish output.
  110. * @param size the number of log records to buffer (must be greater than zero)
  111. * @param pushLevel message level to push on
  112. *
  113. * @throws IllegalArgumentException is size is <= 0
  114. */
  115. public MemoryHandler(Handler target, int size, Level pushLevel) {
  116. if (target == null || pushLevel == null) {
  117. throw new NullPointerException();
  118. }
  119. if (size <= 0) {
  120. throw new IllegalArgumentException();
  121. }
  122. sealed = false;
  123. configure();
  124. sealed = true;
  125. this.target = target;
  126. this.pushLevel = pushLevel;
  127. this.size = size;
  128. init();
  129. }
  130. /**
  131. * Store a <tt>LogRecord</tt> in an internal buffer.
  132. * <p>
  133. * If there is a <tt>Filter</tt>, its <tt>isLoggable</tt>
  134. * method is called to check if the given log record is loggable.
  135. * If not we return. Otherwise the given record is copied into
  136. * an internal circular buffer. Then the record's level property is
  137. * compared with the <tt>pushLevel</tt>. If the given level is
  138. * greater than or equal to the <tt>pushLevel</tt> then <tt>push</tt>
  139. * is called to write all buffered records to the target output
  140. * <tt>Handler</tt>.
  141. *
  142. * @param record description of the log event. A null record is
  143. * silently ignored and is not published
  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. It will return false if the <tt>LogRecord</tt> is Null.
  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. }