This commit is contained in:
284
java/src/org/apache/log4j/RollingFileAppender.java
Normal file
284
java/src/org/apache/log4j/RollingFileAppender.java
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package org.apache.log4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.io.File;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import org.apache.log4j.helpers.OptionConverter;
|
||||
import org.apache.log4j.helpers.LogLog;
|
||||
import org.apache.log4j.helpers.CountingQuietWriter;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
|
||||
/**
|
||||
RollingFileAppender extends FileAppender to backup the log files when
|
||||
they reach a certain size.
|
||||
|
||||
The log4j extras companion includes alternatives which should be considered
|
||||
for new deployments and which are discussed in the documentation
|
||||
for org.apache.log4j.rolling.RollingFileAppender.
|
||||
|
||||
|
||||
@author Heinz Richter
|
||||
@author Ceki Gülcü
|
||||
|
||||
*/
|
||||
public class RollingFileAppender extends FileAppender {
|
||||
|
||||
/**
|
||||
The default maximum file size is 10MB.
|
||||
*/
|
||||
protected long maxFileSize = 10*1024*1024;
|
||||
|
||||
/**
|
||||
There is one backup file by default.
|
||||
*/
|
||||
protected int maxBackupIndex = 1;
|
||||
|
||||
private long nextRollover = 0;
|
||||
|
||||
/**
|
||||
The default constructor simply calls its {@link
|
||||
FileAppender#FileAppender parents constructor}. */
|
||||
public
|
||||
RollingFileAppender() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
Instantiate a RollingFileAppender and open the file designated by
|
||||
<code>filename</code>. The opened filename will become the ouput
|
||||
destination for this appender.
|
||||
|
||||
<p>If the <code>append</code> parameter is true, the file will be
|
||||
appended to. Otherwise, the file desginated by
|
||||
<code>filename</code> will be truncated before being opened.
|
||||
*/
|
||||
public
|
||||
RollingFileAppender(Layout layout, String filename, boolean append)
|
||||
throws IOException {
|
||||
super(layout, filename, append);
|
||||
}
|
||||
|
||||
/**
|
||||
Instantiate a FileAppender and open the file designated by
|
||||
<code>filename</code>. The opened filename will become the output
|
||||
destination for this appender.
|
||||
|
||||
<p>The file will be appended to. */
|
||||
public
|
||||
RollingFileAppender(Layout layout, String filename) throws IOException {
|
||||
super(layout, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the value of the <b>MaxBackupIndex</b> option.
|
||||
*/
|
||||
public
|
||||
int getMaxBackupIndex() {
|
||||
return maxBackupIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the maximum size that the output file is allowed to reach
|
||||
before being rolled over to backup files.
|
||||
|
||||
@since 1.1
|
||||
*/
|
||||
public
|
||||
long getMaximumFileSize() {
|
||||
return maxFileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
Implements the usual roll over behaviour.
|
||||
|
||||
<p>If <code>MaxBackupIndex</code> is positive, then files
|
||||
{<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>}
|
||||
are renamed to {<code>File.2</code>, ...,
|
||||
<code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is
|
||||
renamed <code>File.1</code> and closed. A new <code>File</code> is
|
||||
created to receive further log output.
|
||||
|
||||
<p>If <code>MaxBackupIndex</code> is equal to zero, then the
|
||||
<code>File</code> is truncated with no backup files created.
|
||||
|
||||
*/
|
||||
public // synchronization not necessary since doAppend is alreasy synched
|
||||
void rollOver() {
|
||||
File target;
|
||||
File file;
|
||||
|
||||
if (qw != null) {
|
||||
long size = ((CountingQuietWriter) qw).getCount();
|
||||
LogLog.debug("rolling over count=" + size);
|
||||
// if operation fails, do not roll again until
|
||||
// maxFileSize more bytes are written
|
||||
nextRollover = size + maxFileSize;
|
||||
}
|
||||
LogLog.debug("maxBackupIndex="+maxBackupIndex);
|
||||
|
||||
boolean renameSucceeded = true;
|
||||
// If maxBackups <= 0, then there is no file renaming to be done.
|
||||
if(maxBackupIndex > 0) {
|
||||
// Delete the oldest file, to keep Windows happy.
|
||||
file = new File(fileName + '.' + maxBackupIndex);
|
||||
if (file.exists())
|
||||
renameSucceeded = file.delete();
|
||||
|
||||
// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
|
||||
for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
|
||||
file = new File(fileName + "." + i);
|
||||
if (file.exists()) {
|
||||
target = new File(fileName + '.' + (i + 1));
|
||||
LogLog.debug("Renaming file " + file + " to " + target);
|
||||
renameSucceeded = file.renameTo(target);
|
||||
}
|
||||
}
|
||||
|
||||
if(renameSucceeded) {
|
||||
// Rename fileName to fileName.1
|
||||
target = new File(fileName + "." + 1);
|
||||
|
||||
this.closeFile(); // keep windows happy.
|
||||
|
||||
file = new File(fileName);
|
||||
LogLog.debug("Renaming file " + file + " to " + target);
|
||||
renameSucceeded = file.renameTo(target);
|
||||
//
|
||||
// if file rename failed, reopen file with append = true
|
||||
//
|
||||
if (!renameSucceeded) {
|
||||
try {
|
||||
this.setFile(fileName, true, bufferedIO, bufferSize);
|
||||
}
|
||||
catch(IOException e) {
|
||||
if (e instanceof InterruptedIOException) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
LogLog.error("setFile("+fileName+", true) call failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// if all renames were successful, then
|
||||
//
|
||||
if (renameSucceeded) {
|
||||
try {
|
||||
// This will also close the file. This is OK since multiple
|
||||
// close operations are safe.
|
||||
this.setFile(fileName, false, bufferedIO, bufferSize);
|
||||
nextRollover = 0;
|
||||
}
|
||||
catch(IOException e) {
|
||||
if (e instanceof InterruptedIOException) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
LogLog.error("setFile("+fileName+", false) call failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public
|
||||
synchronized
|
||||
void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
|
||||
throws IOException {
|
||||
super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
|
||||
if(append) {
|
||||
File f = new File(fileName);
|
||||
((CountingQuietWriter) qw).setCount(f.length());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Set the maximum number of backup files to keep around.
|
||||
|
||||
<p>The <b>MaxBackupIndex</b> option determines how many backup
|
||||
files are kept before the oldest is erased. This option takes
|
||||
a positive integer value. If set to zero, then there will be no
|
||||
backup files and the log file will be truncated when it reaches
|
||||
<code>MaxFileSize</code>.
|
||||
*/
|
||||
public
|
||||
void setMaxBackupIndex(int maxBackups) {
|
||||
this.maxBackupIndex = maxBackups;
|
||||
}
|
||||
|
||||
/**
|
||||
Set the maximum size that the output file is allowed to reach
|
||||
before being rolled over to backup files.
|
||||
|
||||
<p>This method is equivalent to {@link #setMaxFileSize} except
|
||||
that it is required for differentiating the setter taking a
|
||||
<code>long</code> argument from the setter taking a
|
||||
<code>String</code> argument by the JavaBeans {@link
|
||||
java.beans.Introspector Introspector}.
|
||||
|
||||
@see #setMaxFileSize(String)
|
||||
*/
|
||||
public
|
||||
void setMaximumFileSize(long maxFileSize) {
|
||||
this.maxFileSize = maxFileSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Set the maximum size that the output file is allowed to reach
|
||||
before being rolled over to backup files.
|
||||
|
||||
<p>In configuration files, the <b>MaxFileSize</b> option takes an
|
||||
long integer in the range 0 - 2^63. You can specify the value
|
||||
with the suffixes "KB", "MB" or "GB" so that the integer is
|
||||
interpreted being expressed respectively in kilobytes, megabytes
|
||||
or gigabytes. For example, the value "10KB" will be interpreted
|
||||
as 10240.
|
||||
*/
|
||||
public
|
||||
void setMaxFileSize(String value) {
|
||||
maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
|
||||
}
|
||||
|
||||
protected
|
||||
void setQWForFiles(Writer writer) {
|
||||
this.qw = new CountingQuietWriter(writer, errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
This method differentiates RollingFileAppender from its super
|
||||
class.
|
||||
|
||||
@since 0.9.0
|
||||
*/
|
||||
protected
|
||||
void subAppend(LoggingEvent event) {
|
||||
super.subAppend(event);
|
||||
if(fileName != null && qw != null) {
|
||||
long size = ((CountingQuietWriter) qw).getCount();
|
||||
if (size >= maxFileSize && size >= nextRollover) {
|
||||
rollOver();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user