All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.qos.logback.core.FileAppender Maven / Gradle / Ivy

There is a newer version: 1.5.12
Show newest version
/**
 * 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ü
 */
public class FileAppender extends OutputStreamAppender {

    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.
     */
    protected boolean append = true;

    /**
     * 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.
     */
    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.
     */
    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()) { 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() { if(!isStarted()) return; super.stop(); Map 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 previousFilesMap = (Map) context .getObject(CoreConstants.FA_FILENAME_COLLISION_MAP); if (previousFilesMap == null) { return collisionsDetected; } for (Entry entry : previousFilesMap.entrySet()) { if (fileName.equals(entry.getValue())) { addErrorForCollision("File", entry.getValue(), entry.getKey()); collisionsDetected = true; } } if (name != null) { previousFilesMap.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(). * * @param file_name The path to the log file. */ public void openFile(String file_name) throws IOException { streamWriteLock.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 { streamWriteLock.unlock(); } } /** * @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; } public void setBufferSize(FileSize bufferSize) { addInfo("Setting bufferSize to [" + bufferSize.toString() + "]"); this.bufferSize = bufferSize; } @Override protected void writeOut(E event) throws IOException { if (prudent) { safeWriteOut(event); } else { super.writeOut(event); } } private void safeWriteOut(E event) { byte[] byteArray = this.encoder.encode(event); if (byteArray == null || byteArray.length == 0) return; streamWriteLock.lock(); try { safeWriteBytes(byteArray); } finally { streamWriteLock.unlock(); } } private void safeWriteBytes(byte[] byteArray) { 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); } writeByteArrayToOutputStreamWithPossibleFlush(byteArray); } catch (IOException e) { // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875) resilientFOS.postIOFailure(e); } finally { releaseFileLock(fileLock); // Re-interrupt if we started in an interrupted state (see LOGBACK-875) if (interrupted) { Thread.currentThread().interrupt(); } } } private void releaseFileLock(FileLock fileLock) { if (fileLock != null && fileLock.isValid()) { try { fileLock.release(); } catch (IOException e) { addError("failed to release lock", e); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy