Renaming a Log4J log file during the program run
We're recently switched over to Log4J from JUL (java.util.Logging) because I wanted to add additional log files for different logging levels.
We have the option in the program to optionally append a value and a date/time stamp to the log file name at the (for all intents and purposes) end o开发者_如何学Pythonf the program's execution.
Because JUL seemed to open and close the file as needed to write to the file, it wasn't locked and we could simply use .renameTo() to change the filename.
Now, using Log4J, that file is left open and is locked, preventing us from renaming the file(s).
I can't decide the name of the file before I configure the logging because the property file containing the options for renaming is some time after the logging is needed (this is why we renamed it at the end of the program).
Do you have any suggestions as to how this can be achieved?
Would Logback and/or SLF4J help or hinder this?
I have sort of worked around the issue by using a system parameter in the log4j properties file, setting the property and then reloading the property file.
This allows me to change the name of the log file to something else at the end of the run, and then rename the old files.
It's inelegant, and very much of a kludge, so I would like to avoid this as it also leaves these temporary files around after the run.
One surefire approach would be to implement your own log4j Appender, perhaps based on the FileAppender ( http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/FileAppender.html ). Add your own specialized API to request the file be renamed.
I haven't tried this yet, but the tact I would take would be to use the underlying API setFile(...): http://www.jdocs.com/log4j/1.2.13/org/apache/log4j/FileAppender.html#M-setFile%28String,boolean,boolean,int%29
For example:
public class RenamingFileAppender extends FileAppender {
...
/** fix concurrency issue in stock implementation **/
public synchronized void setFile(String file) {
super.setFile(file);
}
public synchronized void renameFile(String newName) {
// whole method is synchronized to avoid losing log messages
// implementation can be smarter in having a short term queue
// for any messages that arrive while file is being renamed
File currentFile = new File(this.fileName);
File newFile = new File(newName);
// do checks to ensure current file exists, can be renamed etc.
...
// create a temp file to use while current log gets renamed
File tempFile = File.createTempFile("renaming-appender", ".log");
tempFile.deleteOnExit();
// tell underlying impl to use temporary file, so current file is flushed and closed
super.setFile(tempFile.getAbsolutePath(), false, this.bufferedIO, this.bufferSize);
// rename the recently closed file
currentFile.renameTo(newFile);
// now go back to the original log contents under the new name. Note append=true
super.setFile(newFile.getAbsolutePath(), true, this.bufferedIO, this.bufferSize);
}
Consider using a shutdown hooks, and renaming the file there...
http://onjava.com/pub/a/onjava/2003/03/26/shutdownhook.html
http://www.developerfeed.com/threads/tutorial/understanding-java-shutdown-hook
http://download.oracle.com/javase/1.4.2/docs/guide/lang/hook-design.html
精彩评论