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      private final TriggeringPolicy policy;
45      private final RolloverStrategy strategy;
46  
47      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
48                                   final boolean append, final long size, final long time, final TriggeringPolicy policy,
49                                   final RolloverStrategy strategy) {
50          super(fileName, os, append, false);
51          this.size = size;
52          this.initialTime = time;
53          this.policy = policy;
54          this.strategy = strategy;
55          processor = new PatternProcessor(pattern);
56          policy.initialize(this);
57      }
58  
59      /**
60       * Returns a RollingFileManager.
61       * @param fileName The file name.
62       * @param pattern The pattern for rolling file.
63       * @param append true if the file should be appended to.
64       * @param bufferedIO true if data should be buffered.
65       * @param policy The TriggeringPolicy.
66       * @param strategy The RolloverStrategy.
67       * @return A RollingFileManager.
68       */
69      public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
70                                                      final boolean bufferedIO, final TriggeringPolicy policy,
71                                                      final RolloverStrategy strategy) {
72  
73          return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
74              bufferedIO, policy, strategy), factory);
75      }
76  
77      @Override
78      protected synchronized void write(final byte[] bytes, final int offset, final int length) {
79          size += length;
80          super.write(bytes, offset, length);
81      }
82  
83      /**
84       * Returns the current size of the file.
85       * @return The size of the file in bytes.
86       */
87      public long getFileSize() {
88          return size;
89      }
90  
91      /**
92       * Returns the time the file was created.
93       * @return The time the file was created.
94       */
95      public long getFileTime() {
96          return initialTime;
97      }
98  
99      /**
100      * Determine if a rollover should occur.
101      * @param event The LogEvent.
102      */
103     public synchronized void checkRollover(final LogEvent event) {
104         if (policy.isTriggeringEvent(event) && rollover(strategy)) {
105             try {
106                 size = 0;
107                 initialTime = System.currentTimeMillis();
108                 final OutputStream os = new FileOutputStream(getFileName(), isAppend());
109                 setOutputStream(os);
110             } catch (final FileNotFoundException ex) {
111                 LOGGER.error("FileManager (" + getFileName() + ") " + ex);
112             }
113         }
114     }
115 
116     /**
117      * Returns the pattern processor.
118      * @return The PatternProcessor.
119      */
120     public PatternProcessor getProcessor() {
121         return processor;
122     }
123 
124     private boolean rollover(final RolloverStrategy strategy) {
125 
126         try {
127             // Block until the asynchronous operation is completed.
128             semaphore.acquire();
129         } catch (final InterruptedException ie) {
130             LOGGER.error("Thread interrupted while attempting to check rollover", ie);
131             return false;
132         }
133 
134         boolean success = false;
135         Thread thread = null;
136 
137         try {
138             final RolloverDescription descriptor = strategy.rollover(this);
139 
140             if (descriptor != null) {
141 
142                 close();
143 
144                 if (descriptor.getSynchronous() != null) {
145 
146                     try {
147                         success = descriptor.getSynchronous().execute();
148                     } catch (final Exception ex) {
149                         LOGGER.error("Error in synchronous task", ex);
150                     }
151                 }
152 
153                 if (success && descriptor.getAsynchronous() != null) {
154                     thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this));
155                     thread.start();
156                 }
157                 return true;
158             }
159             return false;
160         } finally {
161             if (thread == null) {
162                 semaphore.release();
163             }
164         }
165 
166     }
167 
168     /**
169      * Performs actions asynchronously.
170      */
171     private static class AsyncAction extends AbstractAction {
172 
173         private final Action action;
174         private final RollingFileManager manager;
175 
176         /**
177          * Constructor.
178          * @param act The action to perform.
179          * @param manager The manager.
180          */
181         public AsyncAction(final Action act, final RollingFileManager manager) {
182             this.action = act;
183             this.manager = manager;
184         }
185 
186         /**
187          * Perform an action.
188          *
189          * @return true if action was successful.  A return value of false will cause
190          *         the rollover to be aborted if possible.
191          * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
192          *                             to be aborted if possible.
193          */
194         @Override
195         public boolean execute() throws IOException {
196             try {
197                 return action.execute();
198             } finally {
199                 manager.semaphore.release();
200             }
201         }
202 
203         /**
204          * Cancels the action if not already initialized or waits till completion.
205          */
206         @Override
207         public void close() {
208             action.close();
209         }
210 
211         /**
212          * Determines if action has been completed.
213          *
214          * @return true if action is complete.
215          */
216         @Override
217         public boolean isComplete() {
218             return action.isComplete();
219         }
220     }
221 
222     /**
223      * Factory data.
224      */
225     private static class FactoryData {
226         private final String pattern;
227         private final boolean append;
228         private final boolean bufferedIO;
229         private final TriggeringPolicy policy;
230         private final RolloverStrategy strategy;
231 
232         /**
233          * Create the data for the factory.
234          * @param pattern The pattern.
235          * @param append The append flag.
236          * @param bufferedIO The bufferedIO flag.
237          */
238         public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
239                            final TriggeringPolicy policy, final RolloverStrategy strategy) {
240             this.pattern = pattern;
241             this.append = append;
242             this.bufferedIO = bufferedIO;
243             this.policy = policy;
244             this.strategy = strategy;
245         }
246     }
247 
248     /**
249      * Factory to create a RollingFileManager.
250      */
251     private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
252 
253         /**
254          * Create the RollingFileManager.
255          * @param name The name of the entity to manage.
256          * @param data The data required to create the entity.
257          * @return a RollingFileManager.
258          */
259         public RollingFileManager createManager(final String name, final FactoryData data) {
260             final File file = new File(name);
261             final File parent = file.getParentFile();
262             if (null != parent && !parent.exists()) {
263                 parent.mkdirs();
264             }
265             try {
266                 file.createNewFile();
267             } catch (final IOException ioe) {
268                 LOGGER.error("Unable to create file " + name, ioe);
269                 return null;
270             }
271             final long size = data.append ? file.length() : 0;
272             final long time = file.lastModified();
273 
274             OutputStream os;
275             try {
276                 os = new FileOutputStream(name, data.append);
277                 if (data.bufferedIO) {
278                     os = new BufferedOutputStream(os);
279                 }
280                 return new RollingFileManager(name, data.pattern, os, data.append, size, time, data.policy,
281                     data.strategy);
282             } catch (final FileNotFoundException ex) {
283                 LOGGER.error("FileManager (" + name + ") " + ex);
284             }
285             return null;
286         }
287     }
288 
289 }