1. /*
  2. * @(#)XMLFormatter.java 1.24 04/01/12
  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.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.24, 01/12/04
  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 append "<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. }
  127. } catch (Exception ex) {
  128. // The message is not in the catalog. Drop through.
  129. }
  130. Object parameters[] = record.getParameters();
  131. // Check to see if the parameter was not a messagetext format
  132. // or was not null or empty
  133. if ( parameters != null && parameters.length != 0
  134. && record.getMessage().indexOf("{") == -1 ) {
  135. for (int i = 0; i < parameters.length; i++) {
  136. sb.append(" <param>");
  137. try {
  138. escape(sb, parameters[i].toString());
  139. } catch (Exception ex) {
  140. sb.append("???");
  141. }
  142. sb.append("</param>\n");
  143. }
  144. }
  145. if (record.getThrown() != null) {
  146. // Report on the state of the throwable.
  147. Throwable th = record.getThrown();
  148. sb.append(" <exception>\n");
  149. sb.append(" <message>");
  150. escape(sb, th.toString());
  151. sb.append("</message>\n");
  152. StackTraceElement trace[] = th.getStackTrace();
  153. for (int i = 0; i < trace.length; i++) {
  154. StackTraceElement frame = trace[i];
  155. sb.append(" <frame>\n");
  156. sb.append(" <class>");
  157. escape(sb, frame.getClassName());
  158. sb.append("</class>\n");
  159. sb.append(" <method>");
  160. escape(sb, frame.getMethodName());
  161. sb.append("</method>\n");
  162. // Check for a line number.
  163. if (frame.getLineNumber() >= 0) {
  164. sb.append(" <line>");
  165. sb.append(frame.getLineNumber());
  166. sb.append("</line>\n");
  167. }
  168. sb.append(" </frame>\n");
  169. }
  170. sb.append(" </exception>\n");
  171. }
  172. sb.append("</record>\n");
  173. return sb.toString();
  174. }
  175. /**
  176. * Return the header string for a set of XML formatted records.
  177. *
  178. * @param h The target handler (can be null)
  179. * @return a valid XML string
  180. */
  181. public String getHead(Handler h) {
  182. StringBuffer sb = new StringBuffer();
  183. String encoding;
  184. sb.append("<?xml version=\"1.0\"");
  185. if (h != null) {
  186. encoding = h.getEncoding();
  187. } else {
  188. encoding = null;
  189. }
  190. if (encoding == null) {
  191. // Figure out the default encoding.
  192. encoding = sun.io.Converters.getDefaultEncodingName();
  193. }
  194. // Try to map the encoding name to a canonical name.
  195. try {
  196. Charset cs = Charset.forName(encoding);
  197. encoding = cs.name();
  198. } catch (Exception ex) {
  199. // We hit problems finding a canonical name.
  200. // Just use the raw encoding name.
  201. }
  202. sb.append(" encoding=\"");
  203. sb.append(encoding);
  204. sb.append("\"");
  205. sb.append(" standalone=\"no\"?>\n");
  206. sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">\n");
  207. sb.append("<log>\n");
  208. return sb.toString();
  209. }
  210. /**
  211. * Return the tail string for a set of XML formatted records.
  212. *
  213. * @param h The target handler (can be null)
  214. * @return a valid XML string
  215. */
  216. public String getTail(Handler h) {
  217. return "</log>\n";
  218. }
  219. }