001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.logging;
019:
020: import java.security.AccessController;
021: import java.security.PrivilegedAction;
022: import java.text.MessageFormat;
023: import java.util.Date;
024: import java.util.ResourceBundle;
025:
026: /**
027: * Format a given <code>LogRecord</code> into string represents XML. The DTD
028: * specified in Appendix A to Java Logging APIs specification is used.
029: *
030: * <code>XMLFormatter</code> uses given <code>Handler</code>'s encoding if
031: * has, otherwise uses default platform encoding instead. However, the UTF-8 is
032: * recommended encoding.
033: */
034: public class XMLFormatter extends Formatter {
035:
036: private static final String lineSeperator = LogManager
037: .getSystemLineSeparator();
038:
039: private static final String indent = " "; //$NON-NLS-1$
040:
041: /**
042: * Default constructor
043: */
044: public XMLFormatter() {
045: super ();
046: }
047:
048: /**
049: * Format a <code>LogRecord</code> into string which represents XML.
050: *
051: * @param r
052: * the given LogRecord instance to be formatted
053: * @return string which represents XML
054: */
055: @SuppressWarnings("nls")
056: @Override
057: public String format(LogRecord r) {
058: // call a method of LogRecord to ensure not null
059: long time = r.getMillis();
060: // format to date
061: String date = MessageFormat.format("{0, date} {0, time}",
062: new Object[] { new Date(time) });
063:
064: StringBuilder sb = new StringBuilder();
065: sb.append(("<record>")).append(lineSeperator);
066: sb.append(indent).append(("<date>")).append(date).append(
067: ("</date>")).append(lineSeperator);
068: sb.append(indent).append(("<millis>")).append(time).append(
069: ("</millis>")).append(lineSeperator);
070: sb.append(indent).append(("<sequence>")).append(
071: r.getSequenceNumber()).append(("</sequence>")).append(
072: lineSeperator);
073: if (null != r.getLoggerName()) {
074: sb.append(indent).append(("<logger>")).append(
075: r.getLoggerName()).append(("</logger>")).append(
076: lineSeperator);
077: }
078: sb.append(indent).append(("<level>")).append(
079: r.getLevel().getName()).append(("</level>")).append(
080: lineSeperator);
081: if (null != r.getSourceClassName()) {
082: sb.append(indent).append(("<class>")).append(
083: r.getSourceClassName()).append(("</class>"))
084: .append(lineSeperator);
085: }
086: if (null != r.getSourceMethodName()) {
087: sb.append(indent).append(("<method>")).append(
088: r.getSourceMethodName()).append(("</method>"))
089: .append(lineSeperator);
090: }
091: sb.append(indent).append(("<thread>")).append(r.getThreadID())
092: .append(("</thread>")).append(lineSeperator);
093: formatMessages(r, sb);
094: Object[] params;
095: if ((params = r.getParameters()) != null) {
096: for (Object element : params) {
097: sb.append(indent).append(("<param>")).append(element)
098: .append(("</param>")).append(lineSeperator);
099: }
100: }
101: formatThrowable(r, sb);
102: sb.append(("</record>")).append(lineSeperator);
103: return sb.toString();
104: }
105:
106: @SuppressWarnings("nls")
107: private void formatMessages(LogRecord r, StringBuilder sb) {
108: // get localized message if has, but don't call Formatter.formatMessage
109: // to parse pattern string
110: ResourceBundle rb = r.getResourceBundle();
111: String pattern = r.getMessage();
112: if (null != rb && null != pattern) {
113: String message;
114: try {
115: message = rb.getString(pattern);
116: } catch (Exception e) {
117: message = null;
118: }
119:
120: if (message == null) {
121: message = pattern;
122: sb.append(indent).append(("<message>")).append(message)
123: .append(("</message>")).append(lineSeperator);
124: } else {
125: sb.append(indent).append(("<message>")).append(message)
126: .append(("</message>")).append(lineSeperator);
127: sb.append(indent).append(("<key>")).append(pattern)
128: .append(("</key>")).append(lineSeperator);
129: sb.append(indent).append(("<catalog>")).append(
130: r.getResourceBundleName()).append(
131: ("</catalog>")).append(lineSeperator);
132: }
133: } else if (null != pattern) {
134: sb.append(indent).append(("<message>")).append(pattern)
135: .append(("</message>")).append(lineSeperator);
136: } else {
137: sb.append(indent).append(("<message/>"));
138: }
139: }
140:
141: @SuppressWarnings("nls")
142: private void formatThrowable(LogRecord r, StringBuilder sb) {
143: Throwable t;
144: if ((t = r.getThrown()) != null) {
145: sb.append(indent).append("<exception>").append(
146: lineSeperator);
147: sb.append(indent).append(indent).append("<message>")
148: .append(t.toString()).append("</message>").append(
149: lineSeperator);
150: // format throwable's stack trace
151: StackTraceElement[] elements = t.getStackTrace();
152: for (StackTraceElement e : elements) {
153: sb.append(indent).append(indent).append("<frame>")
154: .append(lineSeperator);
155: sb.append(indent).append(indent).append(indent).append(
156: "<class>").append(e.getClassName()).append(
157: "</class>").append(lineSeperator);
158: sb.append(indent).append(indent).append(indent).append(
159: "<method>").append(e.getMethodName()).append(
160: "</method>").append(lineSeperator);
161: sb.append(indent).append(indent).append(indent).append(
162: "<line>").append(e.getLineNumber()).append(
163: "</line>").append(lineSeperator);
164: sb.append(indent).append(indent).append("</frame>")
165: .append(lineSeperator);
166: }
167: sb.append(indent).append("</exception>").append(
168: lineSeperator);
169: }
170: }
171:
172: /**
173: * Return the header string for XML, use given handler's encoding if has,
174: * otherwise use default platform encoding
175: *
176: * @param h
177: * the given handler
178: * @return the header string for XML
179: */
180: @SuppressWarnings("nls")
181: @Override
182: public String getHead(Handler h) {
183: String encoding = null;
184: if (null != h) {
185: encoding = h.getEncoding();
186: }
187: if (null == encoding) {
188: encoding = getSystemProperty("file.encoding");
189: }
190: StringBuilder sb = new StringBuilder();
191: sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding)
192: .append("\" standalone=\"no\"?>").append(lineSeperator);
193: sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">").append(
194: lineSeperator);
195: sb.append(("<log>"));
196: return sb.toString();
197: }
198:
199: /**
200: * Return the tail string for XML
201: *
202: * @param h
203: * the given handler
204: * @return the tail string for XML
205: */
206: @Override
207: @SuppressWarnings("unused")
208: public String getTail(Handler h) {
209: return "</log>"; //$NON-NLS-1$
210: }
211:
212: // use privilege code to get system property
213: private static String getSystemProperty(final String key) {
214: return AccessController
215: .doPrivileged(new PrivilegedAction<String>() {
216: public String run() {
217: return System.getProperty(key);
218: }
219: });
220: }
221: }
|