/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author: root $
* $Revision: 8604 $
* $Date: 2007-09-28 15:16:38 +0200 (Fre, 28. Sep 2007) $
*/
package helma.util;
import org.apache.commons.logging.Log;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.zip.GZIPOutputStream;
/**
* An extended Logger that writes to a file and rotates files each midnight.
*
* @author Stefan Pollach
* @author Daniel Ruthardt
* @author Hannes Wallnoefer
*/
public class FileLogger extends Logger implements Log {
// fields used for logging to files
private String name;
private File logdir;
private File logfile;
// number format for log file rotation
DecimalFormat nformat = new DecimalFormat("000");
DateFormat aformat = new SimpleDateFormat("yyyy-MM-dd");
/**
* Create a file logger. The actual file names do have numbers appended and are
* rotated every x bytes.
* @param directory the directory
* @param name the log file base name
*/
protected FileLogger(String directory, String name) {
this.name = name;
logdir = new File(directory);
// make logdir have an absolute path in case it doesn't already
if (!logdir.isAbsolute())
logdir = logdir.getAbsoluteFile();
logfile = new File(logdir, name + ".log");
if (!logdir.exists()) {
logdir.mkdirs();
}
}
/**
* Open the file and get a writer to it. If the file already exists, this will
* return a writer that appends to an existing file if it is from today, or
* otherwise rotate the old log file and start a new one.
*/
private synchronized void openFile() {
try {
if (logfile.exists() && (logfile.lastModified() < Logging.lastMidnight())) {
// rotate if a log file exists and is NOT from today
File archive = rotateLogFile();
// gzip rotated log file in a separate thread
if (archive != null) {
new GZipper(archive).start();
}
}
// create a new log file, appending to an existing file
writer = new PrintWriter(new FileWriter(logfile.getAbsolutePath(), true),
false);
} catch (IOException iox) {
System.err.println("Error creating log " + name + ": " + iox);
}
}
/**
* Actually closes the file writer of a log.
*/
synchronized void closeFile() {
if (writer != null) {
try {
writer.close();
} catch (Exception ignore) {
// ignore
} finally {
writer = null;
}
}
}
/**
* This is called by the runner thread to to make sure we have an open writer.
*/
protected synchronized void ensureOpen() {
// open a new writer if writer is null or the log file has been deleted
if (writer == null || !logfile.exists()) {
openFile();
}
}
/**
* Rotate log files, closing the file writer and renaming the old
* log file. Returns the renamed log file for zipping, or null if
* the log file couldn't be rotated.
*
* @return the old renamed log file, or null
* @throws IOException if an i/o error occurred
*/
protected synchronized File rotateLogFile() throws IOException {
// if the logger is not file based do nothing.
if (logfile == null) {
return null;
}
closeFile();
// only backup/rotate if the log file is not empty,
if (logfile.exists() && (logfile.length() > 0)) {
String today = aformat.format(new Date());
int ct = 0;
// first append just the date
String archname = name + "-" + today + ".log";
File archive = new File(logdir, archname);
File zipped = new File(logdir, archname + ".gz");
// increase counter until we find an unused log archive name, checking
// both unzipped and zipped file names
while (archive.exists() || zipped.exists()) {
// for the next try we append a counter
String archidx = (ct > 999) ? Integer.toString(ct) : nformat.format(++ct);
archname = name + "-" + today + "-" + archidx + ".log";
archive = new File(logdir, archname);
zipped = new File(logdir, archname + ".gz");
}
if (logfile.renameTo(archive)) {
return archive;
} else {
System.err.println("Error rotating log file " + canonicalName +
". Will append to old file.");
}
}
// no log file rotated
return null;
}
/**
* Return a string representation of this Logger
*/
public String toString() {
return "FileLogger[" + name + "]";
}
/**
* Return an object which identifies this logger.
* @return the logger's name
*/
public String getName() {
return name;
}
/**
* a Thread class that zips up a file, filename will stay the same.
*/
static class GZipper extends Thread {
List files;
final static int BUFFER_SIZE = 8192;
public GZipper(List files) {
this.files = files;
setPriority(MIN_PRIORITY);
}
public GZipper(File file) {
files = new ArrayList(1);
files.add(file);
setPriority(MIN_PRIORITY);
}
public void run() {
Iterator it = files.iterator();
File file = null;
while (it.hasNext()) {
try {
file = (File) it.next();
File zipped = new File(file.getAbsolutePath() + ".gz");
GZIPOutputStream zip = new GZIPOutputStream(new FileOutputStream(zipped));
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
byte[] b = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(b, 0, BUFFER_SIZE)) != -1) {
zip.write(b, 0, len);
}
zip.close();
in.close();
file.delete();
} catch (Exception e) {
System.err.println("Error gzipping " + file);
System.err.println(e.toString());
}
}
}
}
}