JDK-4629315 : Appending of XML Logfiles doesn't merge new records
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.logging
  • Affected Version: 1.4.0,5.0
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: windows_nt,windows_2000
  • CPU: x86
  • Submitted: 2002-01-28
  • Updated: 2013-09-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_majorUnresolved
Description
Name: nt126004			Date: 01/28/2002


FULL PRODUCT VERSION :
java version "1.4.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode)

FULL OPERATING SYSTEM VERSION :
Windows NT Ver 4.0 SP 6



A DESCRIPTION OF THE PROBLEM :
When using the new looging feature, and appending to a XML-
Logfile, the Header and Footer gets written each time the
file is reopened.
This causes a not well formed error.
When appending, records must be merged.

Just compile the testcase and execute. A 
new XML Logfile gets created called 'logfile.xml'.
Open the logfile and check for the 2 entries. 
Everything is OK.
Execute the Testcase a second time. Because the 'append-flag' is set to 'true', the old 'logfile.xml' doesn't 
get deleted, all new entries get appended. when you 
open the 'logfile.xml' now, and check for well-
formedness, you will receive an error, because the xml 
file isn't well formed anymore. Each time you start the 
TestCase, a whole XML-File (including Header and 
Footer) is appended to the old File.
The right way this must be done, is to merge the new 
entries between the </record> tag of the last entry and 
the </log> tag.

Example:
<?xml version="1.0" encoding="windows-1252" standalone="no"?
>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
  <date>2002-01-09T09:26:31</date>
  <millis>1010564791613</millis>
  <sequence>0</sequence>
  <logger>build.generate.antxml</logger>
  <level>FINE</level>
  <class>Test</class>
  <method>main</method>
  <thread>10</thread>
  <message>.....message.......</message>
</record>
</log>
<?xml version="1.0" encoding="windows-1252" standalone="no"?
>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
  <date>2002-01-09T09:26:36</date>
  <millis>1010564796711</millis>
  <sequence>0</sequence>
  <logger>build.generate.antxml</logger>
  <level>FINE</level>
  <class>Test</class>
  <method>main</method>
  <thread>10</thread>
  <message>.....message.....</message>
</record>
</log>


This bug can be reproduced always.

---------- BEGIN SOURCE --------
import java.util.logging.*;

/**
 * Short desc.
 *<br>
 * long description
 *
 */

public class TestCase {

    public static void main( String[] args) {
        try {
            FileHandler fh = new FileHandler("logfile.xml",true);
            XMLFormatter xmlf = new XMLFormatter();
            fh.setFormatter(xmlf);
            Logger logger = Logger.getLogger("test.test1.test2");
            logger.addHandler(fh);
            logger.setLevel(Level.ALL);
            logger.warning("that is a test log message -- Level WARNING");
            logger.info("that is a second test log message -- Level WARNING");
        }
        catch (Exception e) {
            e.printStackTrace();
        }

    }
}


---------- END SOURCE ----------
(Review ID: 138077) 
======================================================================

Comments
SUGGESTED FIX Please, see the contextual diffs of the suggested fix below. % sccs sccsdiff -r1.36 -r1.37 -c FileHandler.java ------- FileHandler.java ------- *** /tmp/geta8572 Wed Aug 30 12:47:59 2006 --- /tmp/getb8572 Wed Aug 30 12:47:59 2006 *************** *** 148,162 **** } } private void open(File fname, boolean append) throws IOException { int len = 0; if (append) { len = (int)fname.length(); } FileOutputStream fout = new FileOutputStream(fname.toString(), append); BufferedOutputStream bout = new BufferedOutputStream(fout); meter = new MeteredStream(bout, len); ! setOutputStream(meter); } // Private method to configure a FileHandler from LogManager --- 148,185 ---- } } + // We are going to merge new XML records with the old ones + // for XMLFormatter in append mode: + // - do not write new header, preserve the old one + // - do not preserve the old tail: </log> + private int getTailShift(File fname, int len) throws IOException { + int tailShift = 0; + Formatter formatter = getFormatter(); + if (len > 0 && formatter instanceof XMLFormatter) { + tailShift = formatter.getTail(this).length(); + RandomAccessFile raf = new RandomAccessFile(fname, "rw"); + // Deleting the tail by updating the file length. + raf.setLength(len - tailShift); + raf.close(); + } + return tailShift; + } + private void open(File fname, boolean append) throws IOException { + boolean doneHeader = false; int len = 0; if (append) { len = (int)fname.length(); + int tailShift = getTailShift(fname, len); + if (tailShift > 0) { // XMLFormatter in append mode + len -= tailShift; + doneHeader = true; + } } FileOutputStream fout = new FileOutputStream(fname.toString(), append); BufferedOutputStream bout = new BufferedOutputStream(fout); meter = new MeteredStream(bout, len); ! setOutputStream(meter, doneHeader); } // Private method to configure a FileHandler from LogManager % sccs sccsdiff -r1.20 -r1.21 -c StreamHandler.java ------- StreamHandler.java ------- *** /tmp/geta8585 Wed Aug 30 12:48:23 2006 --- /tmp/getb8585 Wed Aug 30 12:48:23 2006 *************** *** 105,116 **** * the caller does not have <tt>LoggingPermission("control")</tt>. */ protected synchronized void setOutputStream(OutputStream out) throws SecurityException { if (out == null) { throw new NullPointerException(); } flushAndClose(); output = out; ! doneHeader = false; String encoding = getEncoding(); if (encoding == null) { writer = new OutputStreamWriter(output); --- 105,124 ---- * the caller does not have <tt>LoggingPermission("control")</tt>. */ protected synchronized void setOutputStream(OutputStream out) throws SecurityException { + setOutputStream(out, false); + } + + // Package-private method. + // It is needed for FileHandler with "append" attribute and XMLFormatter. + // We do not write the header if "doneHeader" is true. + synchronized void setOutputStream(OutputStream out, boolean doneHeader) + throws SecurityException { if (out == null) { throw new NullPointerException(); } flushAndClose(); output = out; ! this.doneHeader = doneHeader; String encoding = getEncoding(); if (encoding == null) { writer = new OutputStreamWriter(output);
2006-08-30

EVALUATION It looks like there is a possibility to implement the approach pointed out by the customer in the bug description. In such a case, the following takes place: - The Header must not be written again on append to the FileHandler with the XMLFormatter - The Footer from previous session has to be rewritten on append. It is possible to start writing at: file.length() - footer.length() The current FileHandler spec is not very specific that the append mode writes starting at the end of the file. It does not say also anything about XML format though. Therefore, we may say the spec allows merge kind of implementation for XML format of log files. This bug can be fixed in Mustan update. Priority of this bug must be increased because it has been selected as "Five for fixing in August 2006".
2006-08-25

EVALUATION Tricky. The specification of the append functionality really just says that the records will be appended to the end of the existing file. Re-writing the existing terminator isn't called out. In an ideal world this would be handled by the reader being willing ro reach multiple XML Logfiles from one file. But that can be awakward with current parsers. ###@###.### 2002-05-28
2002-05-28