United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4629315 Appending of XML Logfiles doesn't merge new records
JDK-4629315 : Appending of XML Logfiles doesn't merge new records

Details
Type:
Bug
Submit Date:
2002-01-28
Status:
Open
Updated Date:
2013-09-08
Project Name:
JDK
Resolved Date:
Component:
core-libs
OS:
windows_nt,windows_2000
Sub-Component:
java.util.logging
CPU:
x86
Priority:
P4
Resolution:
Unresolved
Affected Versions:
1.4.0,5.0
Targeted Versions:
tbd_major

Related Reports

Sub Tasks

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
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
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
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



Hardware and Software, Engineered to Work Together