View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  package ch.qos.logback.core;
11  
12  import java.io.BufferedWriter;
13  import java.io.File;
14  import java.io.FileOutputStream;
15  import java.io.IOException;
16  import java.io.Writer;
17  import java.nio.channels.FileChannel;
18  import java.nio.channels.FileLock;
19  
20  import ch.qos.logback.core.util.FileUtil;
21  
22  /**
23   * FileAppender appends log events to a file.
24   * 
25   * For more informatio about this appender, please refer to the online manual at
26   * http://logback.qos.ch/manual/appenders.html#FileAppender
27   * 
28   * @author Ceki Gülcü
29   */
30  public class FileAppender<E> extends WriterAppender<E> {
31  
32    /**
33     * Append to or truncate the file? The default value for this variable is
34     * <code>true</code>, meaning that by default a <code>FileAppender</code>
35     * will append to an existing file and not truncate it.
36     */
37    protected boolean append = true;
38  
39    /**
40     * The name of the active log file.
41     */
42    protected String fileName = null;
43  
44    /**
45     * Do we do bufferedIO?
46     */
47    protected boolean bufferedIO = false;
48  
49    /**
50     * The size of the IO buffer. Default is 8K.
51     */
52    protected int bufferSize = 8 * 1024;
53  
54    private boolean prudent = false;
55  
56    private FileChannel fileChannel = null;
57  
58    /**
59     * As in most cases, the default constructor does nothing.
60     */
61    public FileAppender() {
62    }
63  
64    /**
65     * The <b>File</b> property takes a string value which should be the name of
66     * the file to append to.
67     */
68    public void setFile(String file) {
69      if(file == null) {
70        fileName = file;
71      } else {
72        // Trim spaces from both ends. The users probably does not want
73        // trailing spaces in file names.
74        String val = file.trim();
75        fileName = val;      
76  
77      }
78  
79    }
80  
81    /**
82     * @deprecated Use isAppend instead
83     */
84    public boolean getAppend() {
85      return append;
86    }
87  
88    /**
89     * Returns the value of the <b>Append</b> property.
90     */
91    public boolean isAppend() {
92      return append;
93    }
94  
95    
96    /**
97     * This method is used by derived classes to obtain the raw file property.
98     * Regular users should not be using calling method.
99     * 
100    * @return the value of the file property
101    */
102   final public String rawFileProperty() {
103     return fileName;
104   }
105 
106   /**
107    * Returns the value of the <b>File</b> property.
108    * 
109    * <p>This method may be overridden by derived classes.
110    * 
111    */
112   public String getFile() {
113     return fileName;
114   }
115 
116   /**
117    * If the value of <b>File</b> is not <code>null</code>, then
118    * {@link #openFile} is called with the values of <b>File</b> and <b>Append</b>
119    * properties.
120    */
121   public void start() {
122     int errors = 0;
123     if (getFile() != null) {
124       addInfo("File property is set to [" + fileName + "]");
125 
126       if (prudent) {
127         if (isAppend() == false) {
128           setAppend(true);
129           addWarn("Setting \"Append\" property to true on account of \"Prudent\" mode");
130         }
131         if (getImmediateFlush() == false) {
132           setImmediateFlush(true);
133           addWarn("Setting \"ImmediateFlush\" to true on account of \"Prudent\" mode");
134         }
135 
136         if (bufferedIO == true) {
137           setBufferedIO(false);
138           addWarn("Setting \"BufferedIO\" property to false on account of \"Prudent\" mode");
139         }
140       }
141 
142       // In case both bufferedIO and immediateFlush are set, the former
143       // takes priority because 'immediateFlush' is set to true by default.
144       // If the user explicitly set bufferedIO, then we should follow her
145       // directives.
146       if (bufferedIO) {
147         setImmediateFlush(false);
148         addInfo("Setting \"ImmediateFlush\" property to false on account of \"bufferedIO\" property");
149       }
150 
151       try {
152         openFile(getFile());
153       } catch (java.io.IOException e) {
154         errors++;
155         addError("openFile(" + fileName + "," + append + ") call failed.", e);
156       }
157     } else {
158       errors++;
159       addError("\"File\" property not set for appender named [" + name + "].");
160     }
161     if (errors == 0) {
162       super.start();
163     }
164   }
165 
166   /**
167    * <p> Sets and <i>opens</i> the file where the log output will go. The
168    * specified file must be writable.
169    * 
170    * <p> If there was already an opened file, then the previous file is closed
171    * first.
172    * 
173    * <p> <b>Do not use this method directly. To configure a FileAppender or one
174    * of its subclasses, set its properties one by one and then call start().</b>
175    * 
176    * @param filename
177    *                The path to the log file.
178    * @param append
179    *                If true will append to fileName. Otherwise will truncate
180    *                fileName.
181    * @param bufferedIO
182    * @param bufferSize
183    * 
184    * @throws IOException
185    * 
186    */
187   public synchronized void openFile(String file_name) throws IOException {
188     File file = new File(file_name);
189     if (FileUtil.mustCreateParentDirectories(file)) {
190       boolean result = FileUtil.createMissingParentDirectories(file);
191       if (!result) {
192         addError("Failed to create parent directories for ["
193             + file.getAbsolutePath() + "]");
194       }
195     }
196 
197     FileOutputStream fileOutputStream = new FileOutputStream(file_name, append);
198     if (prudent) {
199       fileChannel = fileOutputStream.getChannel();
200     }
201     Writer w = createWriter(fileOutputStream);
202     if (bufferedIO) {
203       w = new BufferedWriter(w, bufferSize);
204     }
205     setWriter(w);
206   }
207 
208   public boolean isBufferedIO() {
209     return bufferedIO;
210   }
211 
212   public void setBufferedIO(boolean bufferedIO) {
213     this.bufferedIO = bufferedIO;
214   }
215 
216   public int getBufferSize() {
217     return bufferSize;
218   }
219 
220   public void setBufferSize(int bufferSize) {
221     this.bufferSize = bufferSize;
222   }
223 
224   /**
225    * @see #setPrudent(boolean)
226    * 
227    * @return true if in prudent mode
228    */
229   public boolean isPrudent() {
230     return prudent;
231   }
232 
233   /**
234    * When prudent is set to true, file appenders from multiple JVMs can safely
235    * write to the same file.
236    * 
237    * @param prudent
238    */
239   public void setPrudent(boolean prudent) {
240     this.prudent = prudent;
241   }
242 
243   public void setAppend(boolean append) {
244     this.append = append;
245   }
246 
247   final private void safeWrite(String s) throws IOException {
248     FileLock fileLock = null;
249     try {
250       fileLock = fileChannel.lock();
251       long position = fileChannel.position();
252       long size = fileChannel.size();
253       if (size != position) {
254         fileChannel.position(size);
255       }
256       super.writerWrite(s, true);
257     } finally {
258       if (fileLock != null) {
259         fileLock.release();
260       }
261     }
262   }
263 
264   @Override
265   protected void writerWrite(String s, boolean flush) throws IOException {
266     if (prudent && fileChannel != null) {
267       safeWrite(s);
268     } else {
269       super.writerWrite(s, flush);
270     }
271   }
272 }