View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.rolling;
18  
19  import org.apache.logging.log4j.core.LogEvent;
20  import org.apache.logging.log4j.core.appender.FileManager;
21  import org.apache.logging.log4j.core.appender.ManagerFactory;
22  import org.apache.logging.log4j.core.appender.rolling.helper.Action;
23  import org.apache.logging.log4j.core.appender.rolling.helper.AbstractAction;
24  
25  import java.io.BufferedOutputStream;
26  import java.io.File;
27  import java.io.FileNotFoundException;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.io.OutputStream;
31  import java.util.concurrent.Semaphore;
32  
33  /**
34   * The Rolling File Manager.
35   */
36  public class RollingFileManager extends FileManager {
37  
38      private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
39  
40      private long size;
41      private long initialTime;
42      private final PatternProcessor processor;
43      private final Semaphore semaphore = new Semaphore(1);
44  
45      protected RollingFileManager(String fileName, String pattern, OutputStream os, boolean append, long size,
46                                   long time) {
47          super(fileName, os, append, false);
48          this.size = size;
49          this.initialTime = time;
50          processor = new PatternProcessor(pattern);
51      }
52  
53      /**
54       * Returns a RollingFileManager.
55       * @param fileName The file name.
56       * @param pattern The pattern for rolling file.
57       * @param append true if the file should be appended to.
58       * @param bufferedIO true if data should be buffered.
59       * @return A RollingFileManager.
60       */
61      public static RollingFileManager getFileManager(String fileName, String pattern, boolean append,
62                                                      boolean bufferedIO) {
63  
64          return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
65              bufferedIO), factory);
66      }
67  
68      @Override
69      protected synchronized void write(byte[] bytes, int offset, int length) {
70          size += length;
71          super.write(bytes, offset, length);
72      }
73  
74      /**
75       * Returns the current size of the file.
76       * @return The size of the file in bytes.
77       */
78      public long getFileSize() {
79          return size;
80      }
81  
82      /**
83       * Returns the time the file was created.
84       * @return The time the file was created.
85       */
86      public long getFileTime() {
87          return initialTime;
88      }
89  
90      /**
91       * Determine if a rollover should occur.
92       * @param event The LogEvent.
93       * @param policy The TriggeringPolicy.
94       * @param strategy The RolloverStrategy.
95       */
96      public synchronized void checkRollover(LogEvent event, TriggeringPolicy policy, RolloverStrategy strategy) {
97          if (policy.isTriggeringEvent(event) && rollover(strategy)) {
98              try {
99                  size = 0;
100                 initialTime = System.currentTimeMillis();
101                 OutputStream os = new FileOutputStream(getFileName(), isAppend());
102                 setOutputStream(os);
103             } catch (FileNotFoundException ex) {
104                 LOGGER.error("FileManager (" + getFileName() + ") " + ex);
105             }
106         }
107     }
108 
109     /**
110      * Returns the pattern processor.
111      * @return The PatternProcessor.
112      */
113     public PatternProcessor getProcessor() {
114         return processor;
115     }
116 
117     private boolean rollover(RolloverStrategy strategy) {
118 
119         try {
120             // Block until the asynchronous operation is completed.
121             semaphore.acquire();
122         } catch (InterruptedException ie) {
123             LOGGER.error("Thread interrupted while attempting to check rollover", ie);
124             return false;
125         }
126 
127         boolean success = false;
128         Thread thread = null;
129 
130         try {
131             RolloverDescription descriptor = strategy.rollover(this);
132 
133             if (descriptor != null) {
134 
135                 close();
136 
137                 if (descriptor.getSynchronous() != null) {
138 
139                     try {
140                         success = descriptor.getSynchronous().execute();
141                     } catch (Exception ex) {
142                         LOGGER.error("Error in synchronous task", ex);
143                     }
144                 }
145 
146                 if (success && descriptor.getAsynchronous() != null) {
147                     thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this));
148                     thread.start();
149                 }
150                 return true;
151             }
152             return false;
153         } finally {
154             if (thread == null) {
155                 semaphore.release();
156             }
157         }
158 
159     }
160 
161     /**
162      * Performs actions asynchronously.
163      */
164     private static class AsyncAction extends AbstractAction {
165 
166         private final Action action;
167         private final RollingFileManager manager;
168 
169         /**
170          * Constructor.
171          * @param act The action to perform.
172          * @param manager The manager.
173          */
174         public AsyncAction(Action act, RollingFileManager manager) {
175             this.action = act;
176             this.manager = manager;
177         }
178 
179         /**
180          * Perform an action.
181          *
182          * @return true if action was successful.  A return value of false will cause
183          *         the rollover to be aborted if possible.
184          * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
185          *                             to be aborted if possible.
186          */
187         @Override
188         public boolean execute() throws IOException {
189             try {
190                 return action.execute();
191             } finally {
192                 manager.semaphore.release();
193             }
194         }
195 
196         /**
197          * Cancels the action if not already initialized or waits till completion.
198          */
199         @Override
200         public void close() {
201             action.close();
202         }
203 
204         /**
205          * Determines if action has been completed.
206          *
207          * @return true if action is complete.
208          */
209         @Override
210         public boolean isComplete() {
211             return action.isComplete();
212         }
213     }
214 
215     /**
216      * Factory data.
217      */
218     private static class FactoryData {
219         private String pattern;
220         private boolean append;
221         private boolean bufferedIO;
222 
223         /**
224          * Create the data for the factory.
225          * @param pattern The pattern.
226          * @param append The append flag.
227          * @param bufferedIO The bufferedIO flag.
228          */
229         public FactoryData(String pattern, boolean append, boolean bufferedIO) {
230             this.pattern = pattern;
231             this.append = append;
232             this.bufferedIO = bufferedIO;
233         }
234     }
235 
236     /**
237      * Factory to create a RollingFileManager.
238      */
239     private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
240 
241         /**
242          * Create the RollingFileManager.
243          * @param name The name of the entity to manage.
244          * @param data The data required to create the entity.
245          * @return a RollingFileManager.
246          */
247         public RollingFileManager createManager(String name, FactoryData data) {
248             File file = new File(name);
249             final File parent = file.getParentFile();
250             if (null != parent && !parent.exists()) {
251                 parent.mkdirs();
252             }
253             try {
254                 file.createNewFile();
255             } catch (IOException ioe) {
256                 LOGGER.error("Unable to create file " + name, ioe);
257                 return null;
258             }
259             long size = data.append ? file.length() : 0;
260             long time = file.lastModified();
261 
262             OutputStream os;
263             try {
264                 os = new FileOutputStream(name, data.append);
265                 if (data.bufferedIO) {
266                     os = new BufferedOutputStream(os);
267                 }
268                 return new RollingFileManager(name, data.pattern, os, data.append, size, time);
269             } catch (FileNotFoundException ex) {
270                 LOGGER.error("FileManager (" + name + ") " + ex);
271             }
272             return null;
273         }
274     }
275 
276 }