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.File;
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.io.RandomAccessFile;
24  import java.nio.ByteBuffer;
25  
26  import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
27  import org.apache.logging.log4j.core.appender.ManagerFactory;
28  
29  /**
30   * Extends RollingFileManager but instead of using a buffered output stream,
31   * this class uses a {@code ByteBuffer} and a {@code RandomAccessFile} to do the
32   * I/O.
33   */
34  public class FastRollingFileManager extends RollingFileManager {
35      private static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
36  
37      private static final FastRollingFileManagerFactory FACTORY = new FastRollingFileManagerFactory();
38  
39      private final boolean isImmediateFlush;
40      private RandomAccessFile randomAccessFile;
41      private final ByteBuffer buffer;
42      private ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
43  
44      public FastRollingFileManager(RandomAccessFile raf, String fileName,
45              String pattern, OutputStream os, boolean append,
46              boolean immediateFlush, long size, long time,
47              TriggeringPolicy policy, RolloverStrategy strategy,
48              String advertiseURI) {
49          super(fileName, pattern, os, append, size, time, policy, strategy,
50                  advertiseURI);
51          this.isImmediateFlush = immediateFlush;
52          this.randomAccessFile = raf;
53          isEndOfBatch.set(Boolean.FALSE);
54  
55          // TODO make buffer size configurable?
56          buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
57      }
58  
59      public static FastRollingFileManager getFastRollingFileManager(
60              String fileName, String filePattern, boolean isAppend,
61              boolean immediateFlush, TriggeringPolicy policy,
62              RolloverStrategy strategy, String advertiseURI) {
63          return (FastRollingFileManager) getManager(fileName, new FactoryData(
64                  filePattern, isAppend, immediateFlush, policy, strategy,
65                  advertiseURI), FACTORY);
66      }
67  
68      public Boolean isEndOfBatch() {
69          return isEndOfBatch.get();
70      }
71  
72      public void setEndOfBatch(boolean isEndOfBatch) {
73          this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
74      }
75  
76      @Override
77      protected synchronized void write(byte[] bytes, int offset, int length) {
78          super.write(bytes, offset, length); // writes to dummy output stream
79  
80          if (length > buffer.remaining()) {
81              flush();
82          }
83          buffer.put(bytes, offset, length);
84          if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
85              flush();
86          }
87      }
88  
89      @Override
90      protected void createFileAfterRollover() throws IOException {
91          this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
92          if (isAppend()) {
93              randomAccessFile.seek(randomAccessFile.length());
94          }
95      }
96  
97      @Override
98      public void flush() {
99          buffer.flip();
100         try {
101             randomAccessFile.write(buffer.array(), 0, buffer.limit());
102         } catch (IOException ex) {
103             String msg = "Error writing to RandomAccessFile " + getName();
104             throw new AppenderRuntimeException(msg, ex);
105         }
106         buffer.clear();
107     }
108 
109     @Override
110     public void close() {
111         flush();
112         try {
113             randomAccessFile.close();
114         } catch (final IOException ex) {
115             LOGGER.error("Unable to close RandomAccessFile " + getName() + ". "
116                     + ex);
117         }
118     }
119 
120     /**
121      * Factory to create a FastRollingFileManager.
122      */
123     private static class FastRollingFileManagerFactory implements
124             ManagerFactory<FastRollingFileManager, FactoryData> {
125 
126         /**
127          * Create the FastRollingFileManager.
128          * 
129          * @param name The name of the entity to manage.
130          * @param data The data required to create the entity.
131          * @return a RollingFileManager.
132          */
133         public FastRollingFileManager createManager(String name,
134                 FactoryData data) {
135             File file = new File(name);
136             final File parent = file.getParentFile();
137             if (null != parent && !parent.exists()) {
138                 parent.mkdirs();
139             }
140             if (!data.append) {
141                 file.delete();
142             }
143             long size = data.append ? file.length() : 0;
144             long time = file.lastModified();
145 
146             RandomAccessFile raf;
147             try {
148                 raf = new RandomAccessFile(name, "rw");
149                 return new FastRollingFileManager(raf, name, data.pattern,
150                         new DummyOutputStream(), data.append,
151                         data.immediateFlush, size, time, data.policy,
152                         data.strategy, data.advertiseURI);
153             } catch (FileNotFoundException ex) {
154                 LOGGER.error("FastRollingFileManager (" + name + ") " + ex);
155             }
156             return null;
157         }
158     }
159 
160     /** {@code OutputStream} subclass that does not write anything. */
161     private static class DummyOutputStream extends OutputStream {
162         @Override
163         public void write(int b) throws IOException {
164         }
165 
166         @Override
167         public void write(byte[] b, int off, int len) throws IOException {
168         }
169     }
170 
171     /**
172      * Factory data.
173      */
174     private static class FactoryData {
175         private final String pattern;
176         private final boolean append;
177         private final boolean immediateFlush;
178         private final TriggeringPolicy policy;
179         private final RolloverStrategy strategy;
180         private final String advertiseURI;
181 
182         /**
183          * Create the data for the factory.
184          * 
185          * @param pattern The pattern.
186          * @param append The append flag.
187          * @param immediateFlush
188          */
189         public FactoryData(String pattern, boolean append,
190                 boolean immediateFlush, TriggeringPolicy policy,
191                 RolloverStrategy strategy, String advertiseURI) {
192             this.pattern = pattern;
193             this.append = append;
194             this.immediateFlush = immediateFlush;
195             this.policy = policy;
196             this.strategy = strategy;
197             this.advertiseURI = advertiseURI;
198         }
199     }
200 
201 }