/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2013, 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.rolling; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.rolling.helper.CompressionMode; import ch.qos.logback.core.rolling.helper.FileNamePattern; import java.io.File; import java.io.IOException; import static ch.qos.logback.core.CoreConstants.CODES_URL; /** * <code>RollingFileAppender</code> extends {@link FileAppender} to backup the * log files depending on {@link RollingPolicy} and {@link TriggeringPolicy}. * <p> * * For more information about this appender, please refer to the online manual * at http://logback.qos.ch/manual/appenders.html#RollingFileAppender * * @author Heinz Richter * @author Ceki Gülcü */ public class RollingFileAppender<E> extends FileAppender<E> { File currentlyActiveFile; TriggeringPolicy<E> triggeringPolicy; RollingPolicy rollingPolicy; static private String RFA_NO_TP_URL = CODES_URL + "#rfa_no_tp"; static private String RFA_NO_RP_URL = CODES_URL + "#rfa_no_rp"; static private String COLLISION_URL = CODES_URL + "#rfa_collision"; public void start() { if (triggeringPolicy == null) { addWarn("No TriggeringPolicy was set for the RollingFileAppender named " + getName()); addWarn("For more information, please visit " + RFA_NO_TP_URL); return; } // we don't want to void existing log files if (!append) { addWarn("Append mode is mandatory for RollingFileAppender"); append = true; } if (rollingPolicy == null) { addError("No RollingPolicy was set for the RollingFileAppender named " + getName()); addError("For more information, please visit " + RFA_NO_RP_URL); return; } // sanity check for http://jira.qos.ch/browse/LOGBACK-796 if (fileAndPatternCollide()) { addError("File property collides with fileNamePattern. Aborting."); addError("For more information, please visit " + COLLISION_URL); return; } if (isPrudent()) { if (rawFileProperty() != null) { addWarn("Setting \"File\" property to null on account of prudent mode"); setFile(null); } if (rollingPolicy.getCompressionMode() != CompressionMode.NONE) { addError("Compression is not supported in prudent mode. Aborting"); return; } } currentlyActiveFile = new File(getFile()); addInfo("Active log file name: " + getFile()); super.start(); } private boolean fileAndPatternCollide() { if (triggeringPolicy instanceof RollingPolicyBase) { final RollingPolicyBase base = (RollingPolicyBase) triggeringPolicy; final FileNamePattern fileNamePattern = base.fileNamePattern; // no use checking if either fileName or fileNamePattern are null if (fileNamePattern != null && fileName != null) { String regex = fileNamePattern.toRegex(); return fileName.matches(regex); } } return false; } @Override public void stop() { if (rollingPolicy != null) rollingPolicy.stop(); if (triggeringPolicy != null) triggeringPolicy.stop(); super.stop(); } @Override public void setFile(String file) { // http://jira.qos.ch/browse/LBCORE-94 // allow setting the file name to null if mandated by prudent mode if (file != null && ((triggeringPolicy != null) || (rollingPolicy != null))) { addError("File property must be set before any triggeringPolicy or rollingPolicy properties"); addError("Visit " + CODES_URL + "#rfa_file_after for more information"); } super.setFile(file); } @Override public String getFile() { return rollingPolicy.getActiveFileName(); } /** * Implemented by delegating most of the rollover work to a rolling policy. */ public void rollover() { synchronized (lock) { // Note: This method needs to be synchronized because it needs exclusive // access while it closes and then re-opens the target file. // // make sure to close the hereto active log file! Renaming under windows // does not work for open files. this.closeOutputStream(); try { rollingPolicy.rollover(); } catch (RolloverFailure rf) { addWarn("RolloverFailure occurred. Deferring rollover"); // we failed to roll-over, let us not truncate and risk data loss this.append = true; } String filename = rollingPolicy.getActiveFileName(); try { // update the currentlyActiveFile // http://jira.qos.ch/browse/LBCORE-90 currentlyActiveFile = new File(filename); // This will also close the file. This is OK since multiple // close operations are safe. this.openFile(filename); } catch (IOException e) { addError("openFile(" + filename + ") failed", e); } } } /** * This method differentiates RollingFileAppender from its super class. */ @Override protected void subAppend(E event) { // The roll-over check must precede actual writing. This is the // only correct behavior for time driven triggers. // We need to synchronize on triggeringPolicy so that only one rollover // occurs at a time synchronized (triggeringPolicy) { if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) { rollover(); } } super.subAppend(event); } public RollingPolicy getRollingPolicy() { return rollingPolicy; } public TriggeringPolicy<E> getTriggeringPolicy() { return triggeringPolicy; } /** * Sets the rolling policy. In case the 'policy' argument also implements * {@link TriggeringPolicy}, then the triggering policy for this appender is * automatically set to be the policy argument. * * @param policy the desired rolling policy */ @SuppressWarnings("unchecked") public void setRollingPolicy(RollingPolicy policy) { rollingPolicy = policy; if (rollingPolicy instanceof TriggeringPolicy) { triggeringPolicy = (TriggeringPolicy<E>) policy; } } public void setTriggeringPolicy(TriggeringPolicy<E> policy) { triggeringPolicy = policy; if (policy instanceof RollingPolicy) { rollingPolicy = (RollingPolicy) policy; } } }