ch.qos.logback.core.FileAppender Maven / Gradle / Ivy
/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2009, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import ch.qos.logback.core.util.FileUtil;
/**
* FileAppender appends log events to a file.
*
* For more information about this appender, please refer to the online manual
* at http://logback.qos.ch/manual/appenders.html#FileAppender
*
* @author Ceki Gülcü
*/
public class FileAppender extends WriterAppender {
/**
* Append to or truncate the file? The default value for this variable is
* true
, meaning that by default a FileAppender
* will append to an existing file and not truncate it.
*/
protected boolean append = true;
/**
* The name of the active log file.
*/
protected String fileName = null;
/**
* Do we do bufferedIO?
*/
protected boolean bufferedIO = false;
/**
* The size of the IO buffer. Default is 8K.
*/
protected int bufferSize = 8 * 1024;
private boolean prudent = false;
private FileChannel fileChannel = null;
/**
* As in most cases, the default constructor does nothing.
*/
public FileAppender() {
}
/**
* The File property takes a string value which should be the name of
* the file to append to.
*/
public void setFile(String file) {
if (file == null) {
fileName = file;
} else {
// Trim spaces from both ends. The users probably does not want
// trailing spaces in file names.
String val = file.trim();
fileName = val;
}
}
/**
* @deprecated Use isAppend instead
*/
public boolean getAppend() {
return append;
}
/**
* Returns the value of the Append property.
*/
public boolean isAppend() {
return append;
}
/**
* This method is used by derived classes to obtain the raw file property.
* Regular users should not be calling this method.
*
* @return the value of the file property
*/
final public String rawFileProperty() {
return fileName;
}
/**
* Returns the value of the File property.
*
* This method may be overridden by derived classes.
*
*/
public String getFile() {
return fileName;
}
/**
* If the value of File is not null
, then
* {@link #openFile} is called with the values of File and Append
* properties.
*/
public void start() {
int errors = 0;
if (getFile() != null) {
addInfo("File property is set to [" + fileName + "]");
if (prudent) {
if (isAppend() == false) {
setAppend(true);
addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode");
}
if (getImmediateFlush() == false) {
setImmediateFlush(true);
addWarn("Setting \"ImmediateFlush\" to true on account of \"Prudent\" mode");
}
if (bufferedIO == true) {
setBufferedIO(false);
addWarn("Setting \"BufferedIO\" property to false on account of \"Prudent\" mode");
}
}
// In case both bufferedIO and immediateFlush are set, the former
// takes priority because 'immediateFlush' is set to true by default.
// If the user explicitly set bufferedIO, then we should follow her
// directives.
if (bufferedIO) {
setImmediateFlush(false);
addInfo("Setting \"ImmediateFlush\" property to false on account of \"bufferedIO\" property");
}
try {
openFile(getFile());
} catch (java.io.IOException e) {
errors++;
addError("openFile(" + fileName + "," + append + ") call failed.", e);
}
} else {
errors++;
addError("\"File\" property not set for appender named [" + name + "].");
}
if (errors == 0) {
super.start();
}
}
/**
*
Sets and opens the file where the log output will go. The
* specified file must be writable.
*
*
If there was already an opened file, then the previous file is closed
* first.
*
*
Do not use this method directly. To configure a FileAppender or one
* of its subclasses, set its properties one by one and then call start().
*
* @param filename
* The path to the log file.
* @param append
* If true will append to fileName. Otherwise will truncate
* fileName.
* @param bufferedIO
* @param bufferSize
*
* @throws IOException
*
*/
public synchronized void openFile(String file_name) throws IOException {
File file = new File(file_name);
if (FileUtil.mustCreateParentDirectories(file)) {
boolean result = FileUtil.createMissingParentDirectories(file);
if (!result) {
addError("Failed to create parent directories for ["
+ file.getAbsolutePath() + "]");
}
}
FileOutputStream fileOutputStream = new FileOutputStream(file_name, append);
if (prudent) {
fileChannel = fileOutputStream.getChannel();
}
Writer w = createWriter(fileOutputStream);
if (bufferedIO) {
w = new BufferedWriter(w, bufferSize);
}
setWriter(w);
}
public boolean isBufferedIO() {
return bufferedIO;
}
public void setBufferedIO(boolean bufferedIO) {
this.bufferedIO = bufferedIO;
}
public int getBufferSize() {
return bufferSize;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
/**
* @see #setPrudent(boolean)
*
* @return true if in prudent mode
*/
public boolean isPrudent() {
return prudent;
}
/**
* When prudent is set to true, file appenders from multiple JVMs can safely
* write to the same file.
*
* @param prudent
*/
public void setPrudent(boolean prudent) {
this.prudent = prudent;
}
public void setAppend(boolean append) {
this.append = append;
}
final private void safeWrite(String s) throws IOException {
FileLock fileLock = null;
try {
fileLock = fileChannel.lock();
long position = fileChannel.position();
long size = fileChannel.size();
if (size != position) {
fileChannel.position(size);
}
super.writerWrite(s, true);
} finally {
if (fileLock != null) {
fileLock.release();
}
}
}
@Override
protected void writerWrite(String s, boolean flush) throws IOException {
if (prudent && fileChannel != null) {
safeWrite(s);
} else {
super.writerWrite(s, flush);
}
}
}