1. /*
  2. * @(#)XMLFormatter.java 1.17 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. import java.io.*;
  9. import java.nio.charset.Charset;
  10. import java.util.*;
  11. /**
  12. * Format a LogRecord into a standard XML format.
  13. * <p>
  14. * The DTD specification is provided as Appendix A to the
  15. * Java Logging APIs specification.
  16. * <p>
  17. * The XMLFormatter can be used with arbitrary character encodings,
  18. * but it is recommended that it normally be used with UTF-8. The
  19. * character encoding can be set on the output Handler.
  20. *
  21. * @version 1.17, 01/23/03
  22. * @since 1.4
  23. */
  24. public class XMLFormatter extends Formatter {
  25. private LogManager manager = LogManager.getLogManager();
  26. // Append a two digit number.
  27. private void a2(StringBuffer sb, int x) {
  28. if (x < 10) {
  29. sb.append('0');
  30. }
  31. sb.append(x);
  32. }
  33. // Append the time and date in ISO 8601 format
  34. private void appendISO8601(StringBuffer sb, long millis) {
  35. Date date = new Date(millis);
  36. sb.append(date.getYear() + 1900);
  37. sb.append('-');
  38. a2(sb, date.getMonth() + 1);
  39. sb.append('-');
  40. a2(sb, date.getDate());
  41. sb.append('T');
  42. a2(sb, date.getHours());
  43. sb.append(':');
  44. a2(sb, date.getMinutes());
  45. sb.append(':');
  46. a2(sb, date.getSeconds());
  47. }
  48. // Append to the given StringBuffer an escaped version of the
  49. // given text string where XML special characters have been escaped.
  50. // For a null string we appebd "<null>"
  51. private void escape(StringBuffer sb, String text) {
  52. if (text == null) {
  53. text = "<null>";
  54. }
  55. for (int i = 0; i < text.length(); i++) {
  56. char ch = text.charAt(i);
  57. if (ch == '<') {
  58. sb.append("<");
  59. } else if (ch == '>') {
  60. sb.append(">");
  61. } else if (ch == '&') {
  62. sb.append("&");
  63. } else {
  64. sb.append(ch);
  65. }
  66. }
  67. }
  68. /**
  69. * Format the given message to XML.
  70. * @param record the log record to be formatted.
  71. * @return a formatted log record
  72. */
  73. public String format(LogRecord record) {
  74. StringBuffer sb = new StringBuffer(500);
  75. sb.append("<record>\n");
  76. sb.append(" <date>");
  77. appendISO8601(sb, record.getMillis());
  78. sb.append("</date>\n");
  79. sb.append(" <millis>");
  80. sb.append(record.getMillis());
  81. sb.append("</millis>\n");
  82. sb.append(" <sequence>");
  83. sb.append(record.getSequenceNumber());
  84. sb.append("</sequence>\n");
  85. String name = record.getLoggerName();
  86. if (name != null) {
  87. sb.append(" <logger>");
  88. escape(sb, name);
  89. sb.append("</logger>\n");
  90. }
  91. sb.append(" <level>");
  92. escape(sb, record.getLevel().toString());
  93. sb.append("</level>\n");
  94. if (record.getSourceClassName() != null) {
  95. sb.append(" <class>");
  96. escape(sb, record.getSourceClassName());
  97. sb.append("</class>\n");
  98. }
  99. if (record.getSourceMethodName() != null) {
  100. sb.append(" <method>");
  101. escape(sb, record.getSourceMethodName());
  102. sb.append("</method>\n");
  103. }
  104. sb.append(" <thread>");
  105. sb.append(record.getThreadID());
  106. sb.append("</thread>\n");
  107. if (record.getMessage() != null) {
  108. // Format the message string and its accompanying parameters.
  109. String message = formatMessage(record);
  110. sb.append(" <message>");
  111. escape(sb, message);
  112. sb.append("</message>");
  113. sb.append("\n");
  114. }
  115. // If the message is being localized, output the key, resource
  116. // bundle name, and params.
  117. ResourceBundle bundle = record.getResourceBundle();
  118. try {
  119. if (bundle != null && bundle.getString(record.getMessage()) != null) {
  120. sb.append(" <key>");
  121. escape(sb, record.getMessage());
  122. sb.append("</key>\n");
  123. sb.append(" <catalog>");
  124. escape(sb, record.getResourceBundleName());
  125. sb.append("</catalog>\n");
  126. Object parameters[] = record.getParameters();
  127. for (int i = 0; i < parameters.length; i++) {
  128. sb.append(" <param>");
  129. try {
  130. escape(sb, parameters[i].toString());
  131. } catch (Exception ex) {
  132. sb.append("???");
  133. }
  134. sb.append("</param>\n");
  135. }
  136. }
  137. } catch (Exception ex) {
  138. // The message is not in the catalog. Drop through.
  139. }
  140. if (record.getThrown() != null) {
  141. // Report on the state of the throwable.
  142. Throwable th = record.getThrown();
  143. sb.append(" <exception>\n");
  144. sb.append(" <message>");
  145. escape(sb, th.toString());
  146. sb.append("</message>\n");
  147. StackTraceElement trace[] = th.getStackTrace();
  148. for (int i = 0; i < trace.length; i++) {
  149. StackTraceElement frame = trace[i];
  150. sb.append(" <frame>\n");
  151. sb.append(" <class>");
  152. escape(sb, frame.getClassName());
  153. sb.append("</class>\n");
  154. sb.append(" <method>");
  155. escape(sb, frame.getMethodName());
  156. sb.append("</method>\n");
  157. // Check for a line number.
  158. if (frame.getLineNumber() >= 0) {
  159. sb.append(" <line>");
  160. sb.append(frame.getLineNumber());
  161. sb.append("</line>\n");
  162. }
  163. sb.append(" </frame>\n");
  164. }
  165. sb.append(" </exception>\n");
  166. }
  167. sb.append("</record>\n");
  168. return sb.toString();
  169. }
  170. /**
  171. * Return the header string for a set of XML formatted records.
  172. *
  173. * @param h The target handler.
  174. * @return header string
  175. */
  176. public String getHead(Handler h) {
  177. StringBuffer sb = new StringBuffer();
  178. sb.append("<?xml version=\"1.0\"");
  179. String encoding = h.getEncoding();
  180. if (encoding == null) {
  181. // Figure out the default encoding.
  182. encoding = sun.io.Converters.getDefaultEncodingName();
  183. }
  184. // Try to map the encoding name to a canonical name.
  185. try {
  186. Charset cs = Charset.forName(encoding);
  187. encoding = cs.name();
  188. } catch (Exception ex) {
  189. // We hit problems finding a canonical name.
  190. // Just use the raw encoding name.
  191. }
  192. sb.append(" encoding=\"");
  193. sb.append(encoding);
  194. sb.append("\"");
  195. sb.append(" standalone=\"no\"?>\n");
  196. sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">\n");
  197. sb.append("<log>\n");
  198. return sb.toString();
  199. }
  200. /**
  201. * Return the tail string for a set of XML formatted records.
  202. *
  203. * @param h The target handler.
  204. * @return tail string
  205. */
  206. public String getTail(Handler h) {
  207. return "</log>\n";
  208. }
  209. }