FileDocCategorySizeDatePackage
FileHandler.javaAPI DocJava SE 6 API20396Tue Jun 10 00:25:58 BST 2008java.util.logging

FileHandler

public class FileHandler extends StreamHandler
Simple file logging Handler.

The FileHandler can either write to a specified file, or it can write to a rotating set of files.

For a rotating set of files, as each file reaches a given size limit, it is closed, rotated out, and a new file opened. Successively older files are named by adding "0", "1", "2", etc into the base filename.

By default buffering is enabled in the IO libraries but each log record is flushed out when it is complete.

By default the XMLFormatter class is used for formatting.

Configuration: By default each FileHandler is initialized using the following LogManager configuration properties. If properties are not defined (or have invalid values) then the specified default values are used.

  • java.util.logging.FileHandler.level specifies the default level for the Handler (defaults to Level.ALL).
  • java.util.logging.FileHandler.filter specifies the name of a Filter class to use (defaults to no Filter).
  • java.util.logging.FileHandler.formatter specifies the name of a Formatter class to use (defaults to java.util.logging.XMLFormatter)
  • java.util.logging.FileHandler.encoding the name of the character set encoding to use (defaults to the default platform encoding).
  • java.util.logging.FileHandler.limit specifies an approximate maximum amount to write (in bytes) to any one file. If this is zero, then there is no limit. (Defaults to no limit).
  • java.util.logging.FileHandler.count specifies how many output files to cycle through (defaults to 1).
  • java.util.logging.FileHandler.pattern specifies a pattern for generating the output file name. See below for details. (Defaults to "%h/java%u.log").
  • java.util.logging.FileHandler.append specifies whether the FileHandler should append onto any existing files (defaults to false).

A pattern consists of a string that includes the following special components that will be replaced at runtime:

  • "/" the local pathname separator
  • "%t" the system temporary directory
  • "%h" the value of the "user.home" system property
  • "%g" the generation number to distinguish rotated logs
  • "%u" a unique number to resolve conflicts
  • "%%" translates to a single percent sign "%"
If no "%g" field has been specified and the file count is greater than one, then the generation number will be added to the end of the generated filename, after a dot.

Thus for example a pattern of "%t/java%g.log" with a count of 2 would typically cause log files to be written on Solaris to /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log

Generation numbers follow the sequence 0, 1, 2, etc.

Normally the "%u" unique field is set to 0. However, if the FileHandler tries to open the filename and finds the file is currently in use by another process it will increment the unique number field and try again. This will be repeated until FileHandler finds a file name that is not currently in use. If there is a conflict and no "%u" field has been specified, it will be added at the end of the filename after a dot. (This will be after any automatically added generation number.)

Thus if three processes were all trying to log to fred%u.%g.txt then they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as the first file in their rotating sequences.

Note that the use of unique ids to avoid conflicts is only guaranteed to work reliably when using a local disk file system.

version
1.36, 04/07/06
since
1.4

Fields Summary
private MeteredStream
meter
private boolean
append
private int
limit
private int
count
private String
pattern
private String
lockFileName
private FileOutputStream
lockStream
private File[]
files
private static final int
MAX_LOCKS
private static HashMap
locks
Constructors Summary
public FileHandler()
Construct a default FileHandler. This will be configured entirely from LogManager properties (or their default values).

exception
IOException if there are IO problems opening the files.
exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control")).
exception
NullPointerException if pattern property is an empty String.

	checkAccess();
	configure();
	openFiles();
    
public FileHandler(String pattern)
Initialize a FileHandler to write to the given filename.

The FileHandler is configured based on LogManager properties (or their default values) except that the given pattern argument is used as the filename pattern, the file limit is set to no limit, and the file count is set to one.

There is no limit on the amount of data that may be written, so use this with care.

param
pattern the name of the output file
exception
IOException if there are IO problems opening the files.
exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control").
exception
IllegalArgumentException if pattern is an empty string

	if (pattern.length() < 1 ) {
	    throw new IllegalArgumentException();   
	}
	checkAccess();
	configure();
	this.pattern = pattern;
	this.limit = 0;
	this.count = 1;
	openFiles();
    
public FileHandler(String pattern, boolean append)
Initialize a FileHandler to write to the given filename, with optional append.

The FileHandler is configured based on LogManager properties (or their default values) except that the given pattern argument is used as the filename pattern, the file limit is set to no limit, the file count is set to one, and the append mode is set to the given append argument.

There is no limit on the amount of data that may be written, so use this with care.

param
pattern the name of the output file
param
append specifies append mode
exception
IOException if there are IO problems opening the files.
exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control").
exception
IllegalArgumentException if pattern is an empty string

	if (pattern.length() < 1 ) {
	    throw new IllegalArgumentException();   
	}
	checkAccess();
	configure();
	this.pattern = pattern;
	this.limit = 0;
	this.count = 1;
	this.append = append;
	openFiles();
    
public FileHandler(String pattern, int limit, int count)
Initialize a FileHandler to write to a set of files. When (approximately) the given limit has been written to one file, another file will be opened. The output will cycle through a set of count files.

The FileHandler is configured based on LogManager properties (or their default values) except that the given pattern argument is used as the filename pattern, the file limit is set to the limit argument, and the file count is set to the given count argument.

The count must be at least 1.

param
pattern the pattern for naming the output file
param
limit the maximum number of bytes to write to any one file
param
count the number of files to use
exception
IOException if there are IO problems opening the files.
exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control").
exception
IllegalArgumentException if limit < 0, or count < 1.
exception
IllegalArgumentException if pattern is an empty string

	if (limit < 0 || count < 1 || pattern.length() < 1) {
	    throw new IllegalArgumentException();
	}
	checkAccess();
	configure();
	this.pattern = pattern;
	this.limit = limit;
	this.count = count;
	openFiles();
    
public FileHandler(String pattern, int limit, int count, boolean append)
Initialize a FileHandler to write to a set of files with optional append. When (approximately) the given limit has been written to one file, another file will be opened. The output will cycle through a set of count files.

The FileHandler is configured based on LogManager properties (or their default values) except that the given pattern argument is used as the filename pattern, the file limit is set to the limit argument, and the file count is set to the given count argument, and the append mode is set to the given append argument.

The count must be at least 1.

param
pattern the pattern for naming the output file
param
limit the maximum number of bytes to write to any one file
param
count the number of files to use
param
append specifies append mode
exception
IOException if there are IO problems opening the files.
exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control").
exception
IllegalArgumentException if limit < 0, or count < 1.
exception
IllegalArgumentException if pattern is an empty string

	if (limit < 0 || count < 1 || pattern.length() < 1) {
	    throw new IllegalArgumentException();
	}
	checkAccess();
	configure();
	this.pattern = pattern;
	this.limit = limit;
	this.count = count;
	this.append = append;
	openFiles();
    
Methods Summary
public synchronized voidclose()
Close all the files.

exception
SecurityException if a security manager exists and if the caller does not have LoggingPermission("control").

	super.close();
	// Unlock any lock file.
	if (lockFileName == null) {
	    return;
	}
	try {
	    // Closing the lock file's FileOutputStream will close
	    // the underlying channel and free any locks.
	    lockStream.close();
	} catch (Exception ex) {
	    // Problems closing the stream.  Punt.
	}
	synchronized(locks) {
	    locks.remove(lockFileName);
	}
        new File(lockFileName).delete();
	lockFileName = null;
	lockStream = null;
    
private voidconfigure()

        LogManager manager = LogManager.getLogManager();

	String cname = getClass().getName();

	pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
	limit = manager.getIntProperty(cname + ".limit", 0);
	if (limit < 0) {
	    limit = 0;
	}
        count = manager.getIntProperty(cname + ".count", 1);
	if (count <= 0) {
	    count = 1;
	}
        append = manager.getBooleanProperty(cname + ".append", false);
	setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
	setFilter(manager.getFilterProperty(cname + ".filter", null));
	setFormatter(manager.getFormatterProperty(cname + ".formatter",	new XMLFormatter()));
	try {
	    setEncoding(manager.getStringProperty(cname +".encoding", null));
	} catch (Exception ex) {
	    try {
	        setEncoding(null);
	    } catch (Exception ex2) {
		// doing a setEncoding with null should always work.
		// assert false;
	    }
	}
    
private java.io.Filegenerate(java.lang.String pattern, int generation, int unique)

	File file = null;
	String word = "";
	int ix = 0;
  	boolean sawg = false;
  	boolean sawu = false;
	while (ix < pattern.length()) {
	    char ch = pattern.charAt(ix);
	    ix++;
	    char ch2 = 0;
	    if (ix < pattern.length()) {
		ch2 = Character.toLowerCase(pattern.charAt(ix));
	    }
	    if (ch == '/") {
		if (file == null) {
		    file = new File(word);
		} else {
		    file = new File(file, word);
		}
		word = "";
		continue;
	    } else  if (ch == '%") {
		if (ch2 == 't") {
	    	    String tmpDir = System.getProperty("java.io.tmpdir");
		    if (tmpDir == null) {
			tmpDir = System.getProperty("user.home");
		    }
		    file = new File(tmpDir);
		    ix++;
		    word = "";
		    continue;
	        } else if (ch2 == 'h") {
		    file = new File(System.getProperty("user.home"));
		    if (isSetUID()) {
			// Ok, we are in a set UID program.  For safety's sake
			// we disallow attempts to open files relative to %h.
			throw new IOException("can't use %h in set UID program");
		    }
		    ix++;
		    word = "";
		    continue;
	        } else if (ch2 == 'g") {
		    word = word + generation;
		    sawg = true;
		    ix++;
		    continue;
	        } else if (ch2 == 'u") {
		    word = word + unique;
		    sawu = true;
		    ix++;
		    continue;
	        } else if (ch2 == '%") {
		    word = word + "%";
		    ix++;
		    continue;
		}
	    }
	    word = word + ch;
	}
	if (count > 1 && !sawg) {
	    word = word + "." + generation;
	}
	if (unique > 0 && !sawu) {
	    word = word + "." + unique;
	}
	if (word.length() > 0) {
	    if (file == null) {
		file = new File(word);
	    } else {
		file = new File(file, word);
	    }
	}
	return file;
    
private static native booleanisSetUID()

private voidopen(java.io.File fname, boolean append)

	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 voidopenFiles()

        LogManager manager = LogManager.getLogManager();
	manager.checkAccess();
	if (count < 1) {
	   throw new IllegalArgumentException("file count = " + count);
	}
	if (limit < 0) {
	    limit = 0;
	}

	// We register our own ErrorManager during initialization
	// so we can record exceptions.
	InitializationErrorManager em = new InitializationErrorManager();
	setErrorManager(em);

	// Create a lock file.  This grants us exclusive access
	// to our set of output files, as long as we are alive.
	int unique = -1;
	for (;;) {
	    unique++;
	    if (unique > MAX_LOCKS) {
		throw new IOException("Couldn't get lock for " + pattern);
	    }
	    // Generate a lock file name from the "unique" int.
	    lockFileName = generate(pattern, 0, unique).toString() + ".lck";
	    // Now try to lock that filename.
	    // Because some systems (e.g. Solaris) can only do file locks
	    // between processes (and not within a process), we first check
	    // if we ourself already have the file locked.
	    synchronized(locks) {
		if (locks.get(lockFileName) != null) {
		    // We already own this lock, for a different FileHandler
		    // object.  Try again.
		    continue;
	        }
		FileChannel fc;
		try {
		    lockStream = new FileOutputStream(lockFileName);
		    fc = lockStream.getChannel();
		} catch (IOException ix) {
		    // We got an IOException while trying to open the file.
		    // Try the next file.
		    continue;
		}
		try {
		    FileLock fl = fc.tryLock();
		    if (fl == null) {
		        // We failed to get the lock.  Try next file.
			continue;
		    }
		    // We got the lock OK.
		} catch (IOException ix) {
		    // We got an IOException while trying to get the lock.
		    // This normally indicates that locking is not supported
		    // on the target directory.  We have to proceed without
		    // getting a lock.   Drop through.
		}
		// We got the lock.  Remember it.
		locks.put(lockFileName, lockFileName);
		break;
	    }
	}

	files = new File[count];
	for (int i = 0; i < count; i++) {
	    files[i] = generate(pattern, i, unique);
	}

	// Create the initial log file.
	if (append) {
	    open(files[0], true);
	} else {
            rotate();
	}

	// Did we detect any exceptions during initialization?
	Exception ex = em.lastException;
	if (ex != null) {
	    if (ex instanceof IOException) {
		throw (IOException) ex;
	    } else if (ex instanceof SecurityException) {
		throw (SecurityException) ex;
	    } else {
		throw new IOException("Exception: " + ex);
	    }
	}

	// Install the normal default ErrorManager.
	setErrorManager(new ErrorManager());
    
public synchronized voidpublish(java.util.logging.LogRecord record)
Format and publish a LogRecord.

param
record description of the log event. A null record is silently ignored and is not published

	if (!isLoggable(record)) {
	    return;
	}
	super.publish(record);
	flush();
	if (limit > 0 && meter.written >= limit) {
            // We performed access checks in the "init" method to make sure
            // we are only initialized from trusted code.  So we assume
	    // it is OK to write the target files, even if we are
	    // currently being called from untrusted code.
            // So it is safe to raise privilege here.
	    AccessController.doPrivileged(new PrivilegedAction() {
		public Object run() {
		    rotate();
		    return null;
		}
	    });
	}
    
private synchronized voidrotate()

	Level oldLevel = getLevel();
	setLevel(Level.OFF);

	super.close();
	for (int i = count-2; i >= 0; i--) {
	    File f1 = files[i];
	    File f2 = files[i+1];
	    if (f1.exists()) {
		if (f2.exists()) {
		    f2.delete();
		}
		f1.renameTo(f2);
	    }
	}
	try {
	    open(files[0], false);
        } catch (IOException ix) {
	    // We don't want to throw an exception here, but we
	    // report the exception to any registered ErrorManager.
	    reportError(null, ix, ErrorManager.OPEN_FAILURE);

	}
	setLevel(oldLevel);