XMLFormatter.java revision ad41624e761bcf1af9c8008eb45187fc13983717
1f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements.  See the NOTICE file distributed with
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership.
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License.  You may obtain a copy of the License at
8f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
10f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.util.logging;
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.text.MessageFormat;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Date;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.ResourceBundle;
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Formatter to convert a {@link LogRecord} into an XML string. The DTD
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * specified in Appendix A to the Java Logging APIs specification is used.
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * {@code XMLFormatter} uses the output handler's encoding if it is specified,
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * otherwise the default platform encoding is used instead. UTF-8 is the
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * recommended encoding.
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class XMLFormatter extends Formatter {
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
33ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes    private static final String lineSeperator = LogManager.getSystemLineSeparator();
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
35f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    private static final String indent = "    ";
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Constructs a new {@code XMLFormatter}.
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public XMLFormatter() {
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        super();
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Converts a {@code LogRecord} into an XML string.
469a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson     *
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param r
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the log record to be formatted.
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the log record formatted as an XML string.
50adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
519a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson    @SuppressWarnings("nls")
52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
53adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String format(LogRecord r) {
549a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        // call a method of LogRecord to ensure not null
55adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        long time = r.getMillis();
569a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        // format to date
579a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        String date = MessageFormat.format("{0, date} {0, time}",
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                new Object[] { new Date(time) });
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
60adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        StringBuilder sb = new StringBuilder();
619a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(("<record>")).append(lineSeperator);
629a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(indent).append(("<date>")).append(date).append(("</date>"))
63adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                .append(lineSeperator);
649a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(indent).append(("<millis>")).append(time).append(
659a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                ("</millis>")).append(lineSeperator);
669a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(indent).append(("<sequence>")).append(r.getSequenceNumber())
679a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                .append(("</sequence>")).append(lineSeperator);
68b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (r.getLoggerName() != null) {
699a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(("<logger>")).append(r.getLoggerName())
709a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                    .append(("</logger>")).append(lineSeperator);
71adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
729a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(indent).append(("<level>")).append(r.getLevel().getName())
739a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                .append(("</level>")).append(lineSeperator);
74b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (r.getSourceClassName() != null) {
759a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(("<class>"))
769a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                    .append(r.getSourceClassName()).append(("</class>"))
77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    .append(lineSeperator);
78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
79b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (r.getSourceMethodName() != null) {
809a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(("<method>")).append(
819a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                    r.getSourceMethodName()).append(("</method>")).append(
82adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    lineSeperator);
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
849a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(indent).append(("<thread>")).append(r.getThreadID()).append(
859a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                ("</thread>")).append(lineSeperator);
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        formatMessages(r, sb);
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Object[] params;
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if ((params = r.getParameters()) != null) {
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            for (Object element : params) {
909a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(("<param>")).append(element).append(
919a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        ("</param>")).append(lineSeperator);
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
94adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        formatThrowable(r, sb);
959a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(("</record>")).append(lineSeperator);
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return sb.toString();
97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
999a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson    @SuppressWarnings("nls")
100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void formatMessages(LogRecord r, StringBuilder sb) {
1019a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        // get localized message if has, but don't call Formatter.formatMessage
1029a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        // to parse pattern string
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ResourceBundle rb = r.getResourceBundle();
104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String pattern = r.getMessage();
105b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (rb != null && pattern != null) {
106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String message;
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            try {
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                message = rb.getString(pattern);
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } catch (Exception e) {
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                message = null;
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (message == null) {
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                message = pattern;
1159a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(("<message>")).append(message).append(
1169a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        ("</message>")).append(lineSeperator);
117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            } else {
1189a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(("<message>")).append(message).append(
1199a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        ("</message>")).append(lineSeperator);
1209a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(("<key>")).append(pattern).append(
1219a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        ("</key>")).append(lineSeperator);
1229a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(("<catalog>")).append(
1239a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        r.getResourceBundleName()).append(("</catalog>"))
124adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        .append(lineSeperator);
125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
126b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        } else if (pattern != null) {
1279a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(("<message>")).append(pattern).append(
1289a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                    ("</message>")).append(lineSeperator);
1299a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        } else {
1309a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(("<message/>"));
131adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1349a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson    @SuppressWarnings("nls")
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private void formatThrowable(LogRecord r, StringBuilder sb) {
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        Throwable t;
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if ((t = r.getThrown()) != null) {
1389a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append("<exception>").append(lineSeperator);
1399a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append(indent).append("<message>").append(
1409a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                    t.toString()).append("</message>").append(lineSeperator);
1419a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            // format throwable's stack trace
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            StackTraceElement[] elements = t.getStackTrace();
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            for (StackTraceElement e : elements) {
1449a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(indent).append("<frame>").append(
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        lineSeperator);
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                sb.append(indent).append(indent).append(indent).append(
1479a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        "<class>").append(e.getClassName()).append("</class>")
148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        .append(lineSeperator);
149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                sb.append(indent).append(indent).append(indent).append(
1509a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        "<method>").append(e.getMethodName()).append(
1519a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        "</method>").append(lineSeperator);
152adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                sb.append(indent).append(indent).append(indent)
1539a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                        .append("<line>").append(e.getLineNumber()).append(
1549a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                                "</line>").append(lineSeperator);
1559a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                sb.append(indent).append(indent).append("</frame>").append(
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        lineSeperator);
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1589a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson            sb.append(indent).append("</exception>").append(lineSeperator);
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the header string for a set of log records formatted as XML
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * strings, using the output handler's encoding if it is defined, otherwise
165adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * using the default platform encoding.
1669a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson     *
167adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param h
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the output handler, may be {@code null}.
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the header string for log records formatted as XML strings.
170adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1719a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson    @SuppressWarnings("nls")
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getHead(Handler h) {
174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String encoding = null;
175b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (h != null) {
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            encoding = h.getEncoding();
177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
178b46dab348e2007bc08abaf7ecae34d89a2474e50Elliott Hughes        if (encoding == null) {
179ad41624e761bcf1af9c8008eb45187fc13983717Elliott Hughes            encoding = System.getProperty("file.encoding");
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        StringBuilder sb = new StringBuilder();
1829a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append("<?xml version=\"1.0\" encoding=\"").append(encoding).append(
1839a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson                "\" standalone=\"no\"?>").append(lineSeperator);
1849a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append("<!DOCTYPE log SYSTEM \"logger.dtd\">").append(lineSeperator);
1859a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson        sb.append(("<log>"));
186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return sb.toString();
187adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    /**
190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * Returns the tail string for a set of log records formatted as XML
191adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * strings.
1929a0fbe99031759393563ee69ac4640f66f182686Jesse Wilson     *
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @param h
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     *            the output handler, may be {@code null}.
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     * @return the tail string for log records formatted as XML strings.
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    @Override
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getTail(Handler h) {
199f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes        return "</log>";
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
202