1. /*
  2. * @(#)StreamHandler.java 1.13 03/01/27
  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. import java.io.*;
  9. /**
  10. * Stream based logging <tt>Handler</tt>.
  11. * <p>
  12. * This is primarily intended as a base class or support class to
  13. * be used in implementing other logging <tt>Handlers</tt>.
  14. * <p>
  15. * <tt>LogRecords</tt> are published to a given <tt>java.io.OutputStream</tt>.
  16. * <p>
  17. * <b>Configuration:</b>
  18. * By default each <tt>StreamHandler</tt> is initialized using the following
  19. * <tt>LogManager</tt> configuration properties. If properties are not defined
  20. * (or have invalid values) then the specified default values are used.
  21. * <ul>
  22. * <li> java.util.logging.StreamHandler.level
  23. * specifies the default level for the <tt>Handler</tt>
  24. * (defaults to <tt>Level.INFO</tt>).
  25. * <li> java.util.logging.StreamHandler.filter
  26. * specifies the name of a <tt>Filter</tt> class to use
  27. * (defaults to no <tt>Filter</tt>).
  28. * <li> java.util.logging.StreamHandler.formatter
  29. * specifies the name of a <tt>Formatter</tt> class to use
  30. * (defaults to <tt>java.util.logging.SimpleFormatter</tt>).
  31. * <li> java.util.logging.StreamHandler.encoding
  32. * the name of the character set encoding to use (defaults to
  33. * the default platform encoding).
  34. * </ul>
  35. *
  36. * @version 1.13, 01/27/03
  37. * @since 1.4
  38. */
  39. public class StreamHandler extends Handler {
  40. private LogManager manager = LogManager.getLogManager();
  41. private OutputStream output;
  42. private boolean doneHeader;
  43. private Writer writer;
  44. // Private method to configure a StreamHandler from LogManager
  45. // properties and/or default values as specified in the class
  46. // javadoc.
  47. private void configure() {
  48. LogManager manager = LogManager.getLogManager();
  49. String cname = StreamHandler.class.getName();
  50. setLevel(manager.getLevelProperty(cname +".level", Level.INFO));
  51. setFilter(manager.getFilterProperty(cname +".filter", null));
  52. setFormatter(manager.getFormatterProperty(cname +".formatter", new SimpleFormatter()));
  53. try {
  54. setEncoding(manager.getStringProperty(cname +".encoding", null));
  55. } catch (Exception ex) {
  56. try {
  57. setEncoding(null);
  58. } catch (Exception ex2) {
  59. // doing a setEncoding with null should always work.
  60. // assert false;
  61. }
  62. }
  63. }
  64. /**
  65. * Create a <tt>StreamHandler</tt>, with no current output stream.
  66. */
  67. public StreamHandler() {
  68. sealed = false;
  69. configure();
  70. sealed = true;
  71. }
  72. /**
  73. * Create a <tt>StreamHandler</tt> with a given <tt>Formatter</tt>
  74. * and output stream.
  75. * <p>
  76. * @param out the target output stream
  77. * @param formatter Formatter to be used to format output
  78. */
  79. public StreamHandler(OutputStream out, Formatter formatter) {
  80. sealed = false;
  81. configure();
  82. setFormatter(formatter);
  83. setOutputStream(out);
  84. sealed = true;
  85. }
  86. /**
  87. * Change the output stream.
  88. * <P>
  89. * If there is a current output stream then the <tt>Formatter</tt>'s
  90. * tail string is written and the stream is flushed and closed.
  91. * Then the output stream is replaced with the new output stream.
  92. *
  93. * @param out New output stream. May not be null.
  94. * @exception SecurityException if a security manager exists and if
  95. * the caller does not have <tt>LoggingPermission("control")</tt>.
  96. */
  97. protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
  98. if (out == null) {
  99. throw new NullPointerException();
  100. }
  101. flushAndClose();
  102. output = out;
  103. doneHeader = false;
  104. String encoding = getEncoding();
  105. if (encoding == null) {
  106. writer = new OutputStreamWriter(output);
  107. } else {
  108. try {
  109. writer = new OutputStreamWriter(output, encoding);
  110. } catch (UnsupportedEncodingException ex) {
  111. // This shouldn't happen. The setEncoding method
  112. // should have validated that the encoding is OK.
  113. throw new Error("Unexpected exception " + ex);
  114. }
  115. }
  116. }
  117. /**
  118. * Set (or change) the character encoding used by this <tt>Handler</tt>.
  119. * <p>
  120. * The encoding should be set before any <tt>LogRecords</tt> are written
  121. * to the <tt>Handler</tt>.
  122. *
  123. * @param encoding The name of a supported character encoding.
  124. * May be null, to indicate the default platform encoding.
  125. * @exception SecurityException if a security manager exists and if
  126. * the caller does not have <tt>LoggingPermission("control")</tt>.
  127. * @exception UnsupportedEncodingException if the named encoding is
  128. * not supported.
  129. */
  130. public void setEncoding(String encoding)
  131. throws SecurityException, java.io.UnsupportedEncodingException {
  132. super.setEncoding(encoding);
  133. if (output == null) {
  134. return;
  135. }
  136. // Replace the current writer with a writer for the new encoding.
  137. flush();
  138. if (encoding == null) {
  139. writer = new OutputStreamWriter(output);
  140. } else {
  141. writer = new OutputStreamWriter(output, encoding);
  142. }
  143. }
  144. /**
  145. * Format and publish a <tt>LogRecord</tt>.
  146. * <p>
  147. * The <tt>StreamHandler</tt> first checks if there is an <tt>OutputStream</tt>
  148. * and if the given <tt>LogRecord</tt> has at least the required log level.
  149. * If not it silently returns. If so, it calls any associated
  150. * <tt>Filter</tt> to check if the record should be published. If so,
  151. * it calls its <tt>Formatter</tt> to format the record and then writes
  152. * the result to the current output stream.
  153. * <p>
  154. * If this is the first <tt>LogRecord</tt> to be written to a given
  155. * <tt>OutputStream</tt>, the <tt>Formatter</tt>'s "head" string is
  156. * written to the stream before the <tt>LogRecord</tt> is written.
  157. *
  158. * @param record description of the log event
  159. */
  160. public synchronized void publish(LogRecord record) {
  161. if (!isLoggable(record)) {
  162. return;
  163. }
  164. String msg;
  165. try {
  166. msg = getFormatter().format(record);
  167. } catch (Exception ex) {
  168. // We don't want to throw an exception here, but we
  169. // report the exception to any registered ErrorManager.
  170. reportError(null, ex, ErrorManager.FORMAT_FAILURE);
  171. return;
  172. }
  173. try {
  174. if (!doneHeader) {
  175. writer.write(getFormatter().getHead(this));
  176. doneHeader = true;
  177. }
  178. writer.write(msg);
  179. } catch (Exception ex) {
  180. // We don't want to throw an exception here, but we
  181. // report the exception to any registered ErrorManager.
  182. reportError(null, ex, ErrorManager.WRITE_FAILURE);
  183. }
  184. }
  185. /**
  186. * Check if this <tt>Handler</tt> would actually log a given <tt>LogRecord</tt>.
  187. * <p>
  188. * This method checks if the <tt>LogRecord</tt> has an appropriate level and
  189. * whether it satisfies any <tt>Filter</tt>. It will also return false if
  190. * no output stream has been assigned yet.
  191. * <p>
  192. * @param record a <tt>LogRecord</tt>
  193. * @return true if the <tt>LogRecord</tt> would be logged.
  194. *
  195. */
  196. public boolean isLoggable(LogRecord record) {
  197. if (writer == null) {
  198. return false;
  199. }
  200. return super.isLoggable(record);
  201. }
  202. /**
  203. * Flush any buffered messages.
  204. */
  205. public synchronized void flush() {
  206. if (writer != null) {
  207. try {
  208. writer.flush();
  209. } catch (Exception ex) {
  210. // We don't want to throw an exception here, but we
  211. // report the exception to any registered ErrorManager.
  212. reportError(null, ex, ErrorManager.FLUSH_FAILURE);
  213. }
  214. }
  215. }
  216. private synchronized void flushAndClose() throws SecurityException {
  217. checkAccess();
  218. if (writer != null) {
  219. try {
  220. if (!doneHeader) {
  221. writer.write(getFormatter().getHead(this));
  222. doneHeader = true;
  223. }
  224. writer.write(getFormatter().getTail(this));
  225. writer.flush();
  226. writer.close();
  227. } catch (Exception ex) {
  228. // We don't want to throw an exception here, but we
  229. // report the exception to any registered ErrorManager.
  230. reportError(null, ex, ErrorManager.CLOSE_FAILURE);
  231. }
  232. writer = null;
  233. output = null;
  234. }
  235. }
  236. /**
  237. * Close the current output stream.
  238. * <p>
  239. * The <tt>Formatter</tt>'s "tail" string is written to the stream before it
  240. * is closed. In addition, if the <tt>Formatter</tt>'s "head" string has not
  241. * yet been written to the stream, it will be written before the
  242. * "tail" string.
  243. *
  244. * @exception SecurityException if a security manager exists and if
  245. * the caller does not have LoggingPermission("control").
  246. * @exception SecurityException if a security manager exists and if
  247. * the caller does not have LoggingPermission("control").
  248. */
  249. public synchronized void close() throws SecurityException {
  250. flushAndClose();
  251. }
  252. }