285 lines
8.5 KiB
Java
285 lines
8.5 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|
|
}
|
|
}
|