Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.logging/share/classes/java/util/logging/XMLFormatter.java
41159 views
1
/*
2
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
27
package java.util.logging;
28
29
import java.nio.charset.Charset;
30
import java.time.Instant;
31
import java.time.format.DateTimeFormatter;
32
import java.util.*;
33
34
/**
35
* Format a LogRecord into a standard XML format.
36
* <p>
37
* The DTD specification is provided as Appendix A to the
38
* Java Logging APIs specification.
39
* <p>
40
* The XMLFormatter can be used with arbitrary character encodings,
41
* but it is recommended that it normally be used with UTF-8. The
42
* character encoding can be set on the output Handler.
43
*
44
* @implSpec Since JDK 9, instances of {@linkplain LogRecord} contain
45
* an {@link LogRecord#getInstant() Instant} which can have nanoseconds below
46
* the millisecond resolution.
47
* The DTD specification has been updated to allow for an optional
48
* {@code <nanos>} element. By default, the XMLFormatter will compute the
49
* nanosecond adjustment below the millisecond resolution (using
50
* {@code LogRecord.getInstant().getNano() % 1000_000}) - and if this is not 0,
51
* this adjustment value will be printed in the new {@code <nanos>} element.
52
* The event instant can then be reconstructed using
53
* {@code Instant.ofEpochSecond(millis/1000L, (millis % 1000L) * 1000_000L + nanos)}
54
* where {@code millis} and {@code nanos} represent the numbers serialized in
55
* the {@code <millis>} and {@code <nanos>} elements, respectively.
56
* <br>
57
* The {@code <date>} element will now contain the whole instant as formatted
58
* by the {@link DateTimeFormatter#ISO_INSTANT DateTimeFormatter.ISO_INSTANT}
59
* formatter.
60
* <p>
61
* For compatibility with old parsers, XMLFormatters can
62
* be configured to revert to the old format by specifying a
63
* {@code <xml-formatter-fully-qualified-class-name>.useInstant = false}
64
* {@linkplain LogManager#getProperty(java.lang.String) property} in the
65
* logging configuration. When {@code useInstant} is {@code false}, the old
66
* formatting will be preserved. When {@code useInstant} is {@code true}
67
* (the default), the {@code <nanos>} element will be printed and the
68
* {@code <date>} element will contain the {@linkplain
69
* DateTimeFormatter#ISO_INSTANT formatted} instant.
70
* <p>
71
* For instance, in order to configure plain instances of XMLFormatter to omit
72
* the new {@code <nano>} element,
73
* {@code java.util.logging.XMLFormatter.useInstant = false} can be specified
74
* in the logging configuration.
75
*
76
* @since 1.4
77
*/
78
79
public class XMLFormatter extends Formatter {
80
private final LogManager manager = LogManager.getLogManager();
81
private final boolean useInstant;
82
83
/**
84
* Creates a new instance of XMLFormatter.
85
*
86
* @implSpec
87
* Since JDK 9, the XMLFormatter will print out the record {@linkplain
88
* LogRecord#getInstant() event time} as an Instant. This instant
89
* has the best resolution available on the system. The {@code <date>}
90
* element will contain the instant as formatted by the {@link
91
* DateTimeFormatter#ISO_INSTANT}.
92
* In addition, an optional {@code <nanos>} element containing a
93
* nanosecond adjustment will be printed if the instant contains some
94
* nanoseconds below the millisecond resolution.
95
* <p>
96
* This new behavior can be turned off, and the old formatting restored,
97
* by specifying a property in the {@linkplain
98
* LogManager#getProperty(java.lang.String) logging configuration}.
99
* If {@code LogManager.getLogManager().getProperty(
100
* this.getClass().getName()+".useInstant")} is {@code "false"} or
101
* {@code "0"}, the old formatting will be restored.
102
*/
103
public XMLFormatter() {
104
useInstant = (manager == null)
105
|| manager.getBooleanProperty(
106
this.getClass().getName()+".useInstant", true);
107
}
108
109
// Append a two digit number.
110
private void a2(StringBuilder sb, int x) {
111
if (x < 10) {
112
sb.append('0');
113
}
114
sb.append(x);
115
}
116
117
// Append the time and date in ISO 8601 format
118
private void appendISO8601(StringBuilder sb, long millis) {
119
GregorianCalendar cal = new GregorianCalendar();
120
cal.setTimeInMillis(millis);
121
sb.append(cal.get(Calendar.YEAR));
122
sb.append('-');
123
a2(sb, cal.get(Calendar.MONTH) + 1);
124
sb.append('-');
125
a2(sb, cal.get(Calendar.DAY_OF_MONTH));
126
sb.append('T');
127
a2(sb, cal.get(Calendar.HOUR_OF_DAY));
128
sb.append(':');
129
a2(sb, cal.get(Calendar.MINUTE));
130
sb.append(':');
131
a2(sb, cal.get(Calendar.SECOND));
132
}
133
134
// Append to the given StringBuilder an escaped version of the
135
// given text string where XML special characters have been escaped.
136
// For a null string we append "<null>"
137
private void escape(StringBuilder sb, String text) {
138
if (text == null) {
139
text = "<null>";
140
}
141
for (int i = 0; i < text.length(); i++) {
142
char ch = text.charAt(i);
143
if (ch == '<') {
144
sb.append("&lt;");
145
} else if (ch == '>') {
146
sb.append("&gt;");
147
} else if (ch == '&') {
148
sb.append("&amp;");
149
} else {
150
sb.append(ch);
151
}
152
}
153
}
154
155
/**
156
* Format the given message to XML.
157
* <p>
158
* This method can be overridden in a subclass.
159
* It is recommended to use the {@link Formatter#formatMessage}
160
* convenience method to localize and format the message field.
161
*
162
* @param record the log record to be formatted.
163
* @return a formatted log record
164
*/
165
@Override
166
public String format(LogRecord record) {
167
StringBuilder sb = new StringBuilder(500);
168
sb.append("<record>\n");
169
170
final Instant instant = record.getInstant();
171
172
sb.append(" <date>");
173
if (useInstant) {
174
// If useInstant is true - we will print the instant in the
175
// date field, using the ISO_INSTANT formatter.
176
DateTimeFormatter.ISO_INSTANT.formatTo(instant, sb);
177
} else {
178
// If useInstant is false - we will keep the 'old' formating
179
appendISO8601(sb, instant.toEpochMilli());
180
}
181
sb.append("</date>\n");
182
183
sb.append(" <millis>");
184
sb.append(instant.toEpochMilli());
185
sb.append("</millis>\n");
186
187
final int nanoAdjustment = instant.getNano() % 1000_000;
188
if (useInstant && nanoAdjustment != 0) {
189
sb.append(" <nanos>");
190
sb.append(nanoAdjustment);
191
sb.append("</nanos>\n");
192
}
193
194
sb.append(" <sequence>");
195
sb.append(record.getSequenceNumber());
196
sb.append("</sequence>\n");
197
198
String name = record.getLoggerName();
199
if (name != null) {
200
sb.append(" <logger>");
201
escape(sb, name);
202
sb.append("</logger>\n");
203
}
204
205
sb.append(" <level>");
206
escape(sb, record.getLevel().toString());
207
sb.append("</level>\n");
208
209
if (record.getSourceClassName() != null) {
210
sb.append(" <class>");
211
escape(sb, record.getSourceClassName());
212
sb.append("</class>\n");
213
}
214
215
if (record.getSourceMethodName() != null) {
216
sb.append(" <method>");
217
escape(sb, record.getSourceMethodName());
218
sb.append("</method>\n");
219
}
220
221
sb.append(" <thread>");
222
sb.append(record.getLongThreadID());
223
sb.append("</thread>\n");
224
225
if (record.getMessage() != null) {
226
// Format the message string and its accompanying parameters.
227
String message = formatMessage(record);
228
sb.append(" <message>");
229
escape(sb, message);
230
sb.append("</message>");
231
sb.append("\n");
232
}
233
234
// If the message is being localized, output the key, resource
235
// bundle name, and params.
236
ResourceBundle bundle = record.getResourceBundle();
237
try {
238
if (bundle != null && bundle.getString(record.getMessage()) != null) {
239
sb.append(" <key>");
240
escape(sb, record.getMessage());
241
sb.append("</key>\n");
242
sb.append(" <catalog>");
243
escape(sb, record.getResourceBundleName());
244
sb.append("</catalog>\n");
245
}
246
} catch (Exception ex) {
247
// The message is not in the catalog. Drop through.
248
}
249
250
Object parameters[] = record.getParameters();
251
// Check to see if the parameter was not a messagetext format
252
// or was not null or empty
253
if (parameters != null && parameters.length != 0
254
&& record.getMessage().indexOf('{') == -1 ) {
255
for (Object parameter : parameters) {
256
sb.append(" <param>");
257
try {
258
escape(sb, parameter.toString());
259
} catch (Exception ex) {
260
sb.append("???");
261
}
262
sb.append("</param>\n");
263
}
264
}
265
266
if (record.getThrown() != null) {
267
// Report on the state of the throwable.
268
Throwable th = record.getThrown();
269
sb.append(" <exception>\n");
270
sb.append(" <message>");
271
escape(sb, th.toString());
272
sb.append("</message>\n");
273
StackTraceElement trace[] = th.getStackTrace();
274
for (StackTraceElement frame : trace) {
275
sb.append(" <frame>\n");
276
sb.append(" <class>");
277
escape(sb, frame.getClassName());
278
sb.append("</class>\n");
279
sb.append(" <method>");
280
escape(sb, frame.getMethodName());
281
sb.append("</method>\n");
282
// Check for a line number.
283
if (frame.getLineNumber() >= 0) {
284
sb.append(" <line>");
285
sb.append(frame.getLineNumber());
286
sb.append("</line>\n");
287
}
288
sb.append(" </frame>\n");
289
}
290
sb.append(" </exception>\n");
291
}
292
293
sb.append("</record>\n");
294
return sb.toString();
295
}
296
297
/**
298
* Return the header string for a set of XML formatted records.
299
*
300
* @param h The target handler (can be null)
301
* @return a valid XML string
302
*/
303
@Override
304
public String getHead(Handler h) {
305
StringBuilder sb = new StringBuilder();
306
String encoding;
307
sb.append("<?xml version=\"1.0\"");
308
309
if (h != null) {
310
encoding = h.getEncoding();
311
} else {
312
encoding = null;
313
}
314
315
if (encoding == null) {
316
// Figure out the default encoding.
317
encoding = java.nio.charset.Charset.defaultCharset().name();
318
}
319
// Try to map the encoding name to a canonical name.
320
try {
321
Charset cs = Charset.forName(encoding);
322
encoding = cs.name();
323
} catch (Exception ex) {
324
// We hit problems finding a canonical name.
325
// Just use the raw encoding name.
326
}
327
328
sb.append(" encoding=\"");
329
sb.append(encoding);
330
sb.append("\"");
331
sb.append(" standalone=\"no\"?>\n");
332
333
sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">\n");
334
sb.append("<log>\n");
335
return sb.toString();
336
}
337
338
/**
339
* Return the tail string for a set of XML formatted records.
340
*
341
* @param h The target handler (can be null)
342
* @return a valid XML string
343
*/
344
@Override
345
public String getTail(Handler h) {
346
return "</log>\n";
347
}
348
}
349
350