Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, 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.
/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, 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 static ch.qos.logback.core.CoreConstants.CODES_URL; import static ch.qos.logback.core.CoreConstants.MORE_INFO_PREFIX; import java.io.File; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.Map; import java.util.Map.Entry; import ch.qos.logback.core.recovery.ResilientFileOutputStream; import ch.qos.logback.core.util.ContextUtil; import ch.qos.logback.core.util.FileSize; 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ü
/** * 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<E> extends OutputStreamAppender<E> { public static final long DEFAULT_BUFFER_SIZE = 8192; static protected String COLLISION_WITH_EARLIER_APPENDER_URL = CODES_URL + "#earlier_fa_collision";
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.
/** * Append to or truncate the file? The default value for this variable is * <code>true</code>, meaning that by default a <code>FileAppender</code> will * append to an existing file and not truncate it. */
protected boolean append = true;
The name of the active log file.
/** * The name of the active log file. */
protected String fileName = null; private boolean prudent = false; private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE);
The File property takes a string value which should be the name of the file to append to.
/** * The <b>File</b> 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. fileName = file.trim(); } }
Returns the value of the Append property.
/** * Returns the value of the <b>Append</b> 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.
Returns:the value of the file property
/** * 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.

/** * Returns the value of the <b>File</b> property. * * <p> * This method may be overridden by derived classes. * */
public String getFile() { return fileName; }
If the value of File is not null, then openFile is called with the values of File and Append properties.
/** * If the value of <b>File</b> is not <code>null</code>, then * {@link #openFile} is called with the values of <b>File</b> and * <b>Append</b> properties. */
public void start() { int errors = 0; if (getFile() != null) { addInfo("File property is set to [" + fileName + "]"); if (prudent) { if (!isAppend()) { setAppend(true); addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode"); } } if (checkForFileCollisionInPreviousFileAppenders()) { addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting."); addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL); errors++; } else { // file should be opened only if collision free 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(); } } @Override public void stop() { super.stop(); Map<String, String> map = ContextUtil.getFilenameCollisionMap(context); if (map == null || getName() == null) return; map.remove(getName()); } protected boolean checkForFileCollisionInPreviousFileAppenders() { boolean collisionsDetected = false; if (fileName == null) { return false; } @SuppressWarnings("unchecked") Map<String, String> map = (Map<String, String>) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP); if (map == null) { return collisionsDetected; } for (Entry<String, String> entry : map.entrySet()) { if (fileName.equals(entry.getValue())) { addErrorForCollision("File", entry.getValue(), entry.getKey()); collisionsDetected = true; } } if (name != null) { map.put(getName(), fileName); } return collisionsDetected; } protected void addErrorForCollision(String optionName, String optionValue, String appenderName) { addError("'" + optionName + "' option has the same value \"" + optionValue + "\" as that given for appender [" + appenderName + "] defined earlier."); }

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().

Params:
  • file_name – The path to the log file.
/** * <p> * Sets and <i>opens</i> the file where the log output will go. The specified * file must be writable. * * <p> * If there was already an opened file, then the previous file is closed * first. * * <p> * <b>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().</b> * * @param file_name * The path to the log file. */
public void openFile(String file_name) throws IOException { lock.lock(); try { File file = new File(file_name); boolean result = FileUtil.createMissingParentDirectories(file); if (!result) { addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]"); } ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize()); resilientFos.setContext(context); setOutputStream(resilientFos); } finally { lock.unlock(); } }
See Also:
  • setPrudent(boolean)
Returns:true if in prudent mode
/** * @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.
Params:
  • 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; } public void setBufferSize(FileSize bufferSize) { addInfo("Setting bufferSize to ["+bufferSize.toString()+"]"); this.bufferSize = bufferSize; } private void safeWrite(E event) throws IOException { ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream(); FileChannel fileChannel = resilientFOS.getChannel(); if (fileChannel == null) { return; } // Clear any current interrupt (see LOGBACK-875) boolean interrupted = Thread.interrupted(); FileLock fileLock = null; try { fileLock = fileChannel.lock(); long position = fileChannel.position(); long size = fileChannel.size(); if (size != position) { fileChannel.position(size); } super.writeOut(event); } catch (IOException e) { // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875) resilientFOS.postIOFailure(e); } finally { if (fileLock != null && fileLock.isValid()) { fileLock.release(); } // Re-interrupt if we started in an interrupted state (see LOGBACK-875) if (interrupted) { Thread.currentThread().interrupt(); } } } @Override protected void writeOut(E event) throws IOException { if (prudent) { safeWrite(event); } else { super.writeOut(event); } } }