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