1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.util.logging; 19 20import java.text.MessageFormat; 21import java.util.Date; 22import java.util.ResourceBundle; 23 24/** 25 * Formatter to convert a {@link LogRecord} into an XML string. The DTD 26 * specified in Appendix A to the Java Logging APIs specification is used. 27 * {@code XMLFormatter} uses the output handler's encoding if it is specified, 28 * otherwise the default platform encoding is used instead. UTF-8 is the 29 * recommended encoding. 30 */ 31public class XMLFormatter extends Formatter { 32 33 private static final String indent = " "; 34 35 /** 36 * Constructs a new {@code XMLFormatter}. 37 */ 38 public XMLFormatter() { 39 } 40 41 /** 42 * Converts a {@code LogRecord} into an XML string. 43 * 44 * @param r 45 * the log record to be formatted. 46 * @return the log record formatted as an XML string. 47 */ 48 @Override 49 public String format(LogRecord r) { 50 // call a method of LogRecord to ensure not null 51 long time = r.getMillis(); 52 // format to date 53 String date = MessageFormat.format("{0, date} {0, time}", new Object[] { new Date(time) }); 54 String nl = System.lineSeparator(); 55 56 StringBuilder sb = new StringBuilder(); 57 sb.append("<record>").append(nl); 58 append(sb, 1, "date", date); 59 append(sb, 1, "millis", time); 60 append(sb, 1, "sequence", r.getSequenceNumber()); 61 if (r.getLoggerName() != null) { 62 append(sb, 1, "logger", r.getLoggerName()); 63 } 64 append(sb, 1, "level", r.getLevel().getName()); 65 if (r.getSourceClassName() != null) { 66 append(sb, 1, "class", r.getSourceClassName()); 67 } 68 if (r.getSourceMethodName() != null) { 69 append(sb, 1, "method", r.getSourceMethodName()); 70 } 71 append(sb, 1, "thread", r.getThreadID()); 72 formatMessages(r, sb); 73 Object[] params = r.getParameters(); 74 if (params != null) { 75 for (Object element : params) { 76 append(sb, 1, "param", element); 77 } 78 } 79 formatThrowable(r, sb); 80 sb.append("</record>").append(nl); 81 return sb.toString(); 82 } 83 84 private void formatMessages(LogRecord r, StringBuilder sb) { 85 // get localized message if has, but don't call Formatter.formatMessage 86 // to parse pattern string 87 ResourceBundle rb = r.getResourceBundle(); 88 String pattern = r.getMessage(); 89 if (rb != null && pattern != null) { 90 String message; 91 try { 92 message = rb.getString(pattern); 93 } catch (Exception e) { 94 message = null; 95 } 96 97 if (message == null) { 98 message = pattern; 99 append(sb, 1, "message", message); 100 } else { 101 append(sb, 1, "message", message); 102 append(sb, 1, "key", pattern); 103 append(sb, 1, "catalog", r.getResourceBundleName()); 104 } 105 } else if (pattern != null) { 106 append(sb, 1, "message", pattern); 107 } else { 108 sb.append(indent).append("<message/>"); 109 } 110 } 111 112 private void formatThrowable(LogRecord r, StringBuilder sb) { 113 Throwable t; 114 if ((t = r.getThrown()) != null) { 115 String nl = System.lineSeparator(); 116 sb.append(indent).append("<exception>").append(nl); 117 append(sb, 2, "message", t.toString()); 118 // format throwable's stack trace 119 StackTraceElement[] elements = t.getStackTrace(); 120 for (StackTraceElement e : elements) { 121 sb.append(indent).append(indent).append("<frame>").append(nl); 122 append(sb, 3, "class", e.getClassName()); 123 append(sb, 3, "method", e.getMethodName()); 124 append(sb, 3, "line", e.getLineNumber()); 125 sb.append(indent).append(indent).append("</frame>").append(nl); 126 } 127 sb.append(indent).append("</exception>").append(nl); 128 } 129 } 130 131 private static void append(StringBuilder sb, int indentCount, String tag, Object value) { 132 for (int i = 0; i < indentCount; ++i) { 133 sb.append(indent); 134 } 135 sb.append("<").append(tag).append(">"); 136 sb.append(value); 137 sb.append("</").append(tag).append(">"); 138 sb.append(System.lineSeparator()); 139 } 140 141 /** 142 * Returns the header string for a set of log records formatted as XML 143 * strings, using the output handler's encoding if it is defined, otherwise 144 * using the default platform encoding. 145 * 146 * @param h 147 * the output handler, may be {@code null}. 148 * @return the header string for log records formatted as XML strings. 149 */ 150 @Override 151 public String getHead(Handler h) { 152 String encoding = null; 153 if (h != null) { 154 encoding = h.getEncoding(); 155 } 156 if (encoding == null) { 157 encoding = System.getProperty("file.encoding"); 158 } 159 StringBuilder sb = new StringBuilder(); 160 sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding).append("\" standalone=\"no\"?>"); 161 sb.append(System.lineSeparator()); 162 sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">"); 163 sb.append(System.lineSeparator()); 164 sb.append("<log>"); 165 return sb.toString(); 166 } 167 168 /** 169 * Returns the tail string for a set of log records formatted as XML 170 * strings. 171 * 172 * @param h 173 * the output handler, may be {@code null}. 174 * @return the tail string for log records formatted as XML strings. 175 */ 176 @Override 177 public String getTail(Handler h) { 178 return "</log>"; 179 } 180} 181