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