-
Enhancement
-
Resolution: Future Project
-
P4
-
None
-
1.4.2
-
x86
-
windows_2000
FULL PRODUCT VERSION :
java version "1.4.2_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_05-b04)
Java HotSpot(TM) Client VM (build 1.4.2_05-b04, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
Due to some network error, i got the following exception. As it might be okay to throw an IOException if the reference to the log file is lost due to some network error, it's not okay, that subsequent loggins don't work anymore - even though the network error does not exist anymore. So I tried to enclose the code in publish() with a try/catch to somehow create a now outputstream, but i noticed that I can't catch an IOException because the API of StreamHandler.flush() doesn't throw any! ... I can catch an Exception but can't throw it further to the super publish() method after the custom repair code => "unhandled exception type Exception".
-----------------------------------------
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
at de.icomps.logging.RollingFileHandler.publish(Unknown Source)
-----------------------------------------------
Code of RollingFileHandler.publish():
------------------------
/**
* Overwrites super.
*/
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
//check if we need to rotate
if (System.currentTimeMillis() >= nextCycle) { //next cycle?
role();
}
super.publish(record);
flush(); //can throw "java.io.IOException: The specified network name is no longer available"
}//publish()
------------------------------------------
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Custom StreamHandler opens a logfile on the network (network drive). After some network error, an IOException is thrown:
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
And subsequent loggings don't work anymore ...
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If IOExceptions occur during logging, these should be thrown/logged/reported but there should be at least a fallback way to recreate the stream to continue logging. For server applications, this is critical.
ACTUAL -
If there is an IOException during logging (StreamHandler.flush()), subsequent calls of publish() don't work anymore (probably because the OutputStream is broken and is not tried to reopen).
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
REPRODUCIBILITY :
This bug can be reproduced occasionally.
---------- BEGIN SOURCE ----------
/*
* Class: $Workfile: RollingFileHandler.java $
* Version: $Revision: 8 $
* Date: $Date: 8.09.04 11:33 $
* Copyright: iComps InfoComponents Software + Service GmbH
*/
package de.icomps.logging;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.logging.*;
/**
* File handler that supports/ different kind of rolling than java.util.logging.FileHandler.
* Supported rolling methods are: by date (day, week, month, year).
* <p>
* Example of entries in the logging file (system property "java.util.logging.config.file"):
* <p>
<table align="center" bgcolor="#ddddff" border=1 cellpadding="10" cellspacing="0"><tr><td><pre>
de.icomps.logging.RollingFileHandler.level = FINEST
de.icomps.logging.RollingFileHandler.path = /var/log
de.icomps.logging.RollingFileHandler.prefix = MyApp_
de.icomps.logging.RollingFileHandler.dateFormat = yyyyMMdd
de.icomps.logging.RollingFileHandler.suffix = .log
de.icomps.logging.RollingFileHanlder.cycle=day
de.icomps.logging.RollingFileHandler.formatter = de.icomps.logging.TextFormatter
</pre></td></tr></table>
<p>
*
* @version $Revision: 8 $ ($Date: 8.09.04 11:33 $)
* @author $Author: Hilpert $
*/
public class RollingFileHandler extends StreamHandler {
/** Directory path to save log file into. */
static private String path = System.getProperty("user.dir"); //default
/** File prefix. */
protected String prefix = null;
/** Date format to use in file name. */
protected String dateFormat = "yyyy-MM-dd"; //default
/** File suffix. */
protected String suffix = null;
/** Name of log file. */
protected File file = null;
/** Time in milliseconds for the next day (to rotate daily) */
private long nextCycle = 0;
/** Time cycle (for file roling) */
private String cycle = "day"; //default
/**
* Constructor.
*/
public RollingFileHandler() {
super();
LogManager manager = LogManager.getLogManager();
String className = getClass().getName(); //do not use 'RollingFileHandler' for subclasses to work!
String p = manager.getProperty(className + ".path");
prefix = manager.getProperty(className + ".prefix");
String dfs = manager.getProperty(className + ".dateFormat");
suffix = manager.getProperty(className + ".suffix");
String c = manager.getProperty(className + ".cycle");
String formatter = manager.getProperty(className + ".formatter");
if (p != null) {
path = p;
}
if (dfs != null) {
dateFormat = dfs;
}
if (c != null) {
if (c.equalsIgnoreCase("day") || c.equalsIgnoreCase("week") || c.equalsIgnoreCase("month") || c.equalsIgnoreCase("year")) {
cycle = c;
}
}
if (formatter != null) {
try {
setFormatter((Formatter) Class.forName(formatter).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
openFile();
}//RollingFileHandler()
/**
* Set directory to store log files into.
*
* @param logPath Directory to set.
*/
static public void setPath(String logPath) {
if (logPath != null) {
path = logPath;
}
}//setPath
/**
* Get directory to store log files in.
*
* @return Log directory.
*/
static public String getPath() {
return path;
}//getPath()
/**
* Get naem of log file.
*
* @return Name of log file.
*/
public File getFile() {
return file;
}//getFileName()
/**
* Open existing or create new log file.
*/
private synchronized void openFile() {
//create file name:
String dateString = dateFormat; //default (to note error in file name)
Date currentDate= new Date();
try {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, Locale.getDefault());
dateString = sdf.format(currentDate);
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
//compute next cycle:
Date nextDate = null;
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(currentDate);
if (cycle.equalsIgnoreCase("week")) {
gc.add(Calendar.WEEK_OF_YEAR, 1);
nextDate = gc.getTime();
} else if (cycle.equalsIgnoreCase("month")) {
gc.add(Calendar.MONTH, 1);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
GregorianCalendar gc2 = new GregorianCalendar(year, month, 1);
nextDate = gc2.getTime();
} else if (cycle.equalsIgnoreCase("year")) {
gc.add(Calendar.YEAR, 1);
int year = gc.get(Calendar.YEAR);
GregorianCalendar gc2 = new GregorianCalendar(year, 0, 1);
nextDate = gc2.getTime();
} else { //day by default
gc.add(Calendar.DAY_OF_MONTH, 1);
nextDate = gc.getTime();
}
//to zero time:
gc = new GregorianCalendar();
gc.setTime(nextDate);
gc.set(Calendar.HOUR, 0);
gc.set(Calendar.HOUR_OF_DAY, 0);
gc.set(Calendar.MINUTE, 0);
gc.set(Calendar.SECOND, 0);
gc.set(Calendar.MILLISECOND, 0);
nextDate = gc.getTime();
nextCycle = nextDate.getTime();
//create new file:
String fileName = path + File.separatorChar + prefix + dateString + suffix;
file = new File(fileName);
//create file:
if (!file.exists()) {
try {
boolean success = file.createNewFile();
if (!success) {
throw new IOException("openFile(): createNewFile() failed!");
}
} catch (IOException ioe) {
reportError(null, ioe, ErrorManager.OPEN_FAILURE);
ioe.printStackTrace();
}
}
//set log file as OutputStream:
try {
FileOutputStream fos = new FileOutputStream(file, true);
setOutputStream(fos);
} catch (FileNotFoundException fnfe) {
reportError(null, fnfe, ErrorManager.OPEN_FAILURE);
fnfe.printStackTrace();
setOutputStream(System.out); //fallback stream
}
}//openFile()
/**
* Overwrites super.
*/
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
//check if we need to rotate
if (System.currentTimeMillis() >= nextCycle) { //next cycle?
role();
}
super.publish(record);
flush(); //can throw "java.io.IOException: The specified network name is no longer available"
}//publish()
/**
* Role file. Close current file and possibly create new file.
*/
final private synchronized void role() {
Level oldLevel = getLevel();
setLevel(Level.OFF);
super.close();
openFile();
setLevel(oldLevel);
}//rotate()
}//RollingFileHandler
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
So far, I have to re-start the server application.
###@###.### 10/8/04 06:56 GMT
java version "1.4.2_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_05-b04)
Java HotSpot(TM) Client VM (build 1.4.2_05-b04, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
Due to some network error, i got the following exception. As it might be okay to throw an IOException if the reference to the log file is lost due to some network error, it's not okay, that subsequent loggins don't work anymore - even though the network error does not exist anymore. So I tried to enclose the code in publish() with a try/catch to somehow create a now outputstream, but i noticed that I can't catch an IOException because the API of StreamHandler.flush() doesn't throw any! ... I can catch an Exception but can't throw it further to the super publish() method after the custom repair code => "unhandled exception type Exception".
-----------------------------------------
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
at de.icomps.logging.RollingFileHandler.publish(Unknown Source)
-----------------------------------------------
Code of RollingFileHandler.publish():
------------------------
/**
* Overwrites super.
*/
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
//check if we need to rotate
if (System.currentTimeMillis() >= nextCycle) { //next cycle?
role();
}
super.publish(record);
flush(); //can throw "java.io.IOException: The specified network name is no longer available"
}//publish()
------------------------------------------
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Custom StreamHandler opens a logfile on the network (network drive). After some network error, an IOException is thrown:
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
And subsequent loggings don't work anymore ...
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If IOExceptions occur during logging, these should be thrown/logged/reported but there should be at least a fallback way to recreate the stream to continue logging. For server applications, this is critical.
ACTUAL -
If there is an IOException during logging (StreamHandler.flush()), subsequent calls of publish() don't work anymore (probably because the OutputStream is broken and is not tried to reopen).
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.util.logging.ErrorManager: 2
java.io.IOException: The specified network name is no longer available
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder$CharsetSE.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.util.logging.StreamHandler.flush(Unknown Source)
REPRODUCIBILITY :
This bug can be reproduced occasionally.
---------- BEGIN SOURCE ----------
/*
* Class: $Workfile: RollingFileHandler.java $
* Version: $Revision: 8 $
* Date: $Date: 8.09.04 11:33 $
* Copyright: iComps InfoComponents Software + Service GmbH
*/
package de.icomps.logging;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.logging.*;
/**
* File handler that supports/ different kind of rolling than java.util.logging.FileHandler.
* Supported rolling methods are: by date (day, week, month, year).
* <p>
* Example of entries in the logging file (system property "java.util.logging.config.file"):
* <p>
<table align="center" bgcolor="#ddddff" border=1 cellpadding="10" cellspacing="0"><tr><td><pre>
de.icomps.logging.RollingFileHandler.level = FINEST
de.icomps.logging.RollingFileHandler.path = /var/log
de.icomps.logging.RollingFileHandler.prefix = MyApp_
de.icomps.logging.RollingFileHandler.dateFormat = yyyyMMdd
de.icomps.logging.RollingFileHandler.suffix = .log
de.icomps.logging.RollingFileHanlder.cycle=day
de.icomps.logging.RollingFileHandler.formatter = de.icomps.logging.TextFormatter
</pre></td></tr></table>
<p>
*
* @version $Revision: 8 $ ($Date: 8.09.04 11:33 $)
* @author $Author: Hilpert $
*/
public class RollingFileHandler extends StreamHandler {
/** Directory path to save log file into. */
static private String path = System.getProperty("user.dir"); //default
/** File prefix. */
protected String prefix = null;
/** Date format to use in file name. */
protected String dateFormat = "yyyy-MM-dd"; //default
/** File suffix. */
protected String suffix = null;
/** Name of log file. */
protected File file = null;
/** Time in milliseconds for the next day (to rotate daily) */
private long nextCycle = 0;
/** Time cycle (for file roling) */
private String cycle = "day"; //default
/**
* Constructor.
*/
public RollingFileHandler() {
super();
LogManager manager = LogManager.getLogManager();
String className = getClass().getName(); //do not use 'RollingFileHandler' for subclasses to work!
String p = manager.getProperty(className + ".path");
prefix = manager.getProperty(className + ".prefix");
String dfs = manager.getProperty(className + ".dateFormat");
suffix = manager.getProperty(className + ".suffix");
String c = manager.getProperty(className + ".cycle");
String formatter = manager.getProperty(className + ".formatter");
if (p != null) {
path = p;
}
if (dfs != null) {
dateFormat = dfs;
}
if (c != null) {
if (c.equalsIgnoreCase("day") || c.equalsIgnoreCase("week") || c.equalsIgnoreCase("month") || c.equalsIgnoreCase("year")) {
cycle = c;
}
}
if (formatter != null) {
try {
setFormatter((Formatter) Class.forName(formatter).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
openFile();
}//RollingFileHandler()
/**
* Set directory to store log files into.
*
* @param logPath Directory to set.
*/
static public void setPath(String logPath) {
if (logPath != null) {
path = logPath;
}
}//setPath
/**
* Get directory to store log files in.
*
* @return Log directory.
*/
static public String getPath() {
return path;
}//getPath()
/**
* Get naem of log file.
*
* @return Name of log file.
*/
public File getFile() {
return file;
}//getFileName()
/**
* Open existing or create new log file.
*/
private synchronized void openFile() {
//create file name:
String dateString = dateFormat; //default (to note error in file name)
Date currentDate= new Date();
try {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, Locale.getDefault());
dateString = sdf.format(currentDate);
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
//compute next cycle:
Date nextDate = null;
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(currentDate);
if (cycle.equalsIgnoreCase("week")) {
gc.add(Calendar.WEEK_OF_YEAR, 1);
nextDate = gc.getTime();
} else if (cycle.equalsIgnoreCase("month")) {
gc.add(Calendar.MONTH, 1);
int month = gc.get(Calendar.MONTH);
int year = gc.get(Calendar.YEAR);
GregorianCalendar gc2 = new GregorianCalendar(year, month, 1);
nextDate = gc2.getTime();
} else if (cycle.equalsIgnoreCase("year")) {
gc.add(Calendar.YEAR, 1);
int year = gc.get(Calendar.YEAR);
GregorianCalendar gc2 = new GregorianCalendar(year, 0, 1);
nextDate = gc2.getTime();
} else { //day by default
gc.add(Calendar.DAY_OF_MONTH, 1);
nextDate = gc.getTime();
}
//to zero time:
gc = new GregorianCalendar();
gc.setTime(nextDate);
gc.set(Calendar.HOUR, 0);
gc.set(Calendar.HOUR_OF_DAY, 0);
gc.set(Calendar.MINUTE, 0);
gc.set(Calendar.SECOND, 0);
gc.set(Calendar.MILLISECOND, 0);
nextDate = gc.getTime();
nextCycle = nextDate.getTime();
//create new file:
String fileName = path + File.separatorChar + prefix + dateString + suffix;
file = new File(fileName);
//create file:
if (!file.exists()) {
try {
boolean success = file.createNewFile();
if (!success) {
throw new IOException("openFile(): createNewFile() failed!");
}
} catch (IOException ioe) {
reportError(null, ioe, ErrorManager.OPEN_FAILURE);
ioe.printStackTrace();
}
}
//set log file as OutputStream:
try {
FileOutputStream fos = new FileOutputStream(file, true);
setOutputStream(fos);
} catch (FileNotFoundException fnfe) {
reportError(null, fnfe, ErrorManager.OPEN_FAILURE);
fnfe.printStackTrace();
setOutputStream(System.out); //fallback stream
}
}//openFile()
/**
* Overwrites super.
*/
public synchronized void publish(LogRecord record) {
if (!isLoggable(record)) {
return;
}
//check if we need to rotate
if (System.currentTimeMillis() >= nextCycle) { //next cycle?
role();
}
super.publish(record);
flush(); //can throw "java.io.IOException: The specified network name is no longer available"
}//publish()
/**
* Role file. Close current file and possibly create new file.
*/
final private synchronized void role() {
Level oldLevel = getLevel();
setLevel(Level.OFF);
super.close();
openFile();
setLevel(oldLevel);
}//rotate()
}//RollingFileHandler
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
So far, I have to re-start the server application.
###@###.### 10/8/04 06:56 GMT