View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.nio.channels.FileChannel;
19  import java.nio.channels.FileLock;
20  
21  import ch.qos.logback.core.recovery.ResilientFileOutputStream;
22  import ch.qos.logback.core.util.FileUtil;
23  
24  /**
25   * FileAppender appends log events to a file.
26   * 
27   * For more information about this appender, please refer to the online manual
28   * at http://logback.qos.ch/manual/appenders.html#FileAppender
29   * 
30   * @author Ceki Gülcü
31   */
32  public class FileAppender<E> extends OutputStreamAppender<E> {
33  
34    /**
35     * Append to or truncate the file? The default value for this variable is
36     * <code>true</code>, meaning that by default a <code>FileAppender</code> will
37     * append to an existing file and not truncate it.
38     */
39    protected boolean append = true;
40  
41    /**
42     * The name of the active log file.
43     */
44    protected String fileName = null;
45  
46    private boolean prudent = false;
47  
48    /**
49     * As in most cases, the default constructor does nothing.
50     */
51    public FileAppender() {
52    }
53  
54    /**
55     * The <b>File</b> property takes a string value which should be the name of
56     * the file to append to.
57     */
58    public void setFile(String file) {
59      if (file == null) {
60        fileName = file;
61      } else {
62        // Trim spaces from both ends. The users probably does not want
63        // trailing spaces in file names.
64        String val = file.trim();
65        fileName = val;
66      }
67    }
68  
69    /**
70     * Returns the value of the <b>Append</b> property.
71     */
72    public boolean isAppend() {
73      return append;
74    }
75  
76    /**
77     * This method is used by derived classes to obtain the raw file property.
78     * Regular users should not be calling this method.
79     * 
80     * @return the value of the file property
81     */
82    final public String rawFileProperty() {
83      return fileName;
84    }
85  
86    /**
87     * Returns the value of the <b>File</b> property.
88     * 
89     * <p>
90     * This method may be overridden by derived classes.
91     * 
92     */
93    public String getFile() {
94      return fileName;
95    }
96  
97    /**
98     * If the value of <b>File</b> is not <code>null</code>, then
99     * {@link #openFile} is called with the values of <b>File</b> and
100    * <b>Append</b> properties.
101    */
102   public void start() {
103     int errors = 0;
104     if (getFile() != null) {
105       addInfo("File property is set to [" + fileName + "]");
106 
107       if (prudent) {
108         if (isAppend() == false) {
109           setAppend(true);
110           addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode");
111         }
112       }
113 
114       try {
115         openFile(getFile());
116       } catch (java.io.IOException e) {
117         errors++;
118         addError("openFile(" + fileName + "," + append + ") call failed.", e);
119       }
120     } else {
121       errors++;
122       addError("\"File\" property not set for appender named [" + name + "].");
123     }
124     if (errors == 0) {
125       super.start();
126     }
127   }
128 
129   /**
130    * <p>
131    * Sets and <i>opens</i> the file where the log output will go. The specified
132    * file must be writable.
133    * 
134    * <p>
135    * If there was already an opened file, then the previous file is closed
136    * first.
137    * 
138    * <p>
139    * <b>Do not use this method directly. To configure a FileAppender or one of
140    * its subclasses, set its properties one by one and then call start().</b>
141    * 
142    * @param filename
143    *          The path to the log file.
144    * @param append
145    *          If true will append to fileName. Otherwise will truncate fileName.
146    * @param bufferedIO
147    * @param bufferSize
148    * 
149    * @throws IOException
150    * 
151    */
152   public void openFile(String file_name) throws IOException {
153     synchronized (lock) {
154       File file = new File(file_name);
155       if (FileUtil.isParentDirectoryCreationRequired(file)) {
156         boolean result = FileUtil.createMissingParentDirectories(file);
157         if (!result) {
158           addError("Failed to create parent directories for ["
159               + file.getAbsolutePath() + "]");
160         }
161       }
162 
163       ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(
164           file, append);
165       resilientFos.setContext(context);
166       setOutputStream(resilientFos);
167     }
168   }
169 
170   /**
171    * @see #setPrudent(boolean)
172    * 
173    * @return true if in prudent mode
174    */
175   public boolean isPrudent() {
176     return prudent;
177   }
178 
179   /**
180    * When prudent is set to true, file appenders from multiple JVMs can safely
181    * write to the same file.
182    * 
183    * @param prudent
184    */
185   public void setPrudent(boolean prudent) {
186     this.prudent = prudent;
187   }
188 
189   public void setAppend(boolean append) {
190     this.append = append;
191   }
192 
193   final private void safeWrite(E event) throws IOException {
194     ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
195     FileChannel fileChannel = resilientFOS.getChannel();
196     if (fileChannel == null) {
197       return;
198     }
199     FileLock fileLock = null;
200     try {
201       fileLock = fileChannel.lock();
202       long position = fileChannel.position();
203       long size = fileChannel.size();
204       if (size != position) {
205         fileChannel.position(size);
206       }
207       super.writeOut(event);
208     } finally {
209       if (fileLock != null) {
210         fileLock.release();
211       }
212     }
213   }
214 
215   @Override
216   protected void writeOut(E event) throws IOException {
217     if (prudent) {
218       safeWrite(event);
219     } else {
220       super.writeOut(event);
221     }
222   }
223 }