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