blob: cfc51e1b8ad2be2f9e0fd8d9e1c51f02a9ba444a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package java.util.logging;
28
29import java.io.*;
30import java.nio.charset.Charset;
31import java.util.*;
32
33/**
34 * Format a LogRecord into a standard XML format.
35 * <p>
36 * The DTD specification is provided as Appendix A to the
37 * Java Logging APIs specification.
38 * <p>
39 * The XMLFormatter can be used with arbitrary character encodings,
40 * but it is recommended that it normally be used with UTF-8. The
41 * character encoding can be set on the output Handler.
42 *
43 * @since 1.4
44 */
45
46public class XMLFormatter extends Formatter {
47 private LogManager manager = LogManager.getLogManager();
48
49 // Append a two digit number.
50 private void a2(StringBuffer sb, int x) {
51 if (x < 10) {
52 sb.append('0');
53 }
54 sb.append(x);
55 }
56
57 // Append the time and date in ISO 8601 format
58 private void appendISO8601(StringBuffer sb, long millis) {
59 Date date = new Date(millis);
60 sb.append(date.getYear() + 1900);
61 sb.append('-');
62 a2(sb, date.getMonth() + 1);
63 sb.append('-');
64 a2(sb, date.getDate());
65 sb.append('T');
66 a2(sb, date.getHours());
67 sb.append(':');
68 a2(sb, date.getMinutes());
69 sb.append(':');
70 a2(sb, date.getSeconds());
71 }
72
73 // Append to the given StringBuffer an escaped version of the
74 // given text string where XML special characters have been escaped.
75 // For a null string we append "<null>"
76 private void escape(StringBuffer sb, String text) {
77 if (text == null) {
78 text = "<null>";
79 }
80 for (int i = 0; i < text.length(); i++) {
81 char ch = text.charAt(i);
82 if (ch == '<') {
83 sb.append("&lt;");
84 } else if (ch == '>') {
85 sb.append("&gt;");
86 } else if (ch == '&') {
87 sb.append("&amp;");
88 } else {
89 sb.append(ch);
90 }
91 }
92 }
93
94 /**
95 * Format the given message to XML.
96 * <p>
97 * This method can be overridden in a subclass.
98 * It is recommended to use the {@link Formatter#formatMessage}
99 * convenience method to localize and format the message field.
100 *
101 * @param record the log record to be formatted.
102 * @return a formatted log record
103 */
104 public String format(LogRecord record) {
105 StringBuffer sb = new StringBuffer(500);
106 sb.append("<record>\n");
107
108 sb.append(" <date>");
109 appendISO8601(sb, record.getMillis());
110 sb.append("</date>\n");
111
112 sb.append(" <millis>");
113 sb.append(record.getMillis());
114 sb.append("</millis>\n");
115
116 sb.append(" <sequence>");
117 sb.append(record.getSequenceNumber());
118 sb.append("</sequence>\n");
119
120 String name = record.getLoggerName();
121 if (name != null) {
122 sb.append(" <logger>");
123 escape(sb, name);
124 sb.append("</logger>\n");
125 }
126
127 sb.append(" <level>");
128 escape(sb, record.getLevel().toString());
129 sb.append("</level>\n");
130
131 if (record.getSourceClassName() != null) {
132 sb.append(" <class>");
133 escape(sb, record.getSourceClassName());
134 sb.append("</class>\n");
135 }
136
137 if (record.getSourceMethodName() != null) {
138 sb.append(" <method>");
139 escape(sb, record.getSourceMethodName());
140 sb.append("</method>\n");
141 }
142
143 sb.append(" <thread>");
144 sb.append(record.getThreadID());
145 sb.append("</thread>\n");
146
147 if (record.getMessage() != null) {
148 // Format the message string and its accompanying parameters.
149 String message = formatMessage(record);
150 sb.append(" <message>");
151 escape(sb, message);
152 sb.append("</message>");
153 sb.append("\n");
154 }
155
156 // If the message is being localized, output the key, resource
157 // bundle name, and params.
158 ResourceBundle bundle = record.getResourceBundle();
159 try {
160 if (bundle != null && bundle.getString(record.getMessage()) != null) {
161 sb.append(" <key>");
162 escape(sb, record.getMessage());
163 sb.append("</key>\n");
164 sb.append(" <catalog>");
165 escape(sb, record.getResourceBundleName());
166 sb.append("</catalog>\n");
167 }
168 } catch (Exception ex) {
169 // The message is not in the catalog. Drop through.
170 }
171
172 Object parameters[] = record.getParameters();
173 // Check to see if the parameter was not a messagetext format
174 // or was not null or empty
175 if ( parameters != null && parameters.length != 0
176 && record.getMessage().indexOf("{") == -1 ) {
177 for (int i = 0; i < parameters.length; i++) {
178 sb.append(" <param>");
179 try {
180 escape(sb, parameters[i].toString());
181 } catch (Exception ex) {
182 sb.append("???");
183 }
184 sb.append("</param>\n");
185 }
186 }
187
188 if (record.getThrown() != null) {
189 // Report on the state of the throwable.
190 Throwable th = record.getThrown();
191 sb.append(" <exception>\n");
192 sb.append(" <message>");
193 escape(sb, th.toString());
194 sb.append("</message>\n");
195 StackTraceElement trace[] = th.getStackTrace();
196 for (int i = 0; i < trace.length; i++) {
197 StackTraceElement frame = trace[i];
198 sb.append(" <frame>\n");
199 sb.append(" <class>");
200 escape(sb, frame.getClassName());
201 sb.append("</class>\n");
202 sb.append(" <method>");
203 escape(sb, frame.getMethodName());
204 sb.append("</method>\n");
205 // Check for a line number.
206 if (frame.getLineNumber() >= 0) {
207 sb.append(" <line>");
208 sb.append(frame.getLineNumber());
209 sb.append("</line>\n");
210 }
211 sb.append(" </frame>\n");
212 }
213 sb.append(" </exception>\n");
214 }
215
216 sb.append("</record>\n");
217 return sb.toString();
218 }
219
220 /**
221 * Return the header string for a set of XML formatted records.
222 *
223 * @param h The target handler (can be null)
224 * @return a valid XML string
225 */
226 public String getHead(Handler h) {
227 StringBuffer sb = new StringBuffer();
228 String encoding;
229 sb.append("<?xml version=\"1.0\"");
230
231 if (h != null) {
232 encoding = h.getEncoding();
233 } else {
234 encoding = null;
235 }
236
237 if (encoding == null) {
238 // Figure out the default encoding.
239 encoding = java.nio.charset.Charset.defaultCharset().name();
240 }
241 // Try to map the encoding name to a canonical name.
242 try {
243 Charset cs = Charset.forName(encoding);
244 encoding = cs.name();
245 } catch (Exception ex) {
246 // We hit problems finding a canonical name.
247 // Just use the raw encoding name.
248 }
249
250 sb.append(" encoding=\"");
251 sb.append(encoding);
252 sb.append("\"");
253 sb.append(" standalone=\"no\"?>\n");
254 sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">\n");
255 sb.append("<log>\n");
256 return sb.toString();
257 }
258
259 /**
260 * Return the tail string for a set of XML formatted records.
261 *
262 * @param h The target handler (can be null)
263 * @return a valid XML string
264 */
265 public String getTail(Handler h) {
266 return "</log>\n";
267 }
268}