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