1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.rolling;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.io.RandomAccessFile;
23 import java.io.Serializable;
24 import java.nio.ByteBuffer;
25
26 import org.apache.logging.log4j.core.Layout;
27 import org.apache.logging.log4j.core.appender.AppenderLoggingException;
28 import org.apache.logging.log4j.core.appender.ManagerFactory;
29 import org.apache.logging.log4j.core.util.NullOutputStream;
30
31
32
33
34
35
36 public class RollingRandomAccessFileManager extends RollingFileManager {
37
38
39
40 public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
41
42 private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
43
44 private final boolean isImmediateFlush;
45 private RandomAccessFile randomAccessFile;
46 private final ByteBuffer buffer;
47 private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
48
49 public RollingRandomAccessFileManager(final RandomAccessFile raf, final String fileName,
50 final String pattern, final OutputStream os, final boolean append,
51 final boolean immediateFlush, final int bufferSize, final long size, final long time,
52 final TriggeringPolicy policy, final RolloverStrategy strategy,
53 final String advertiseURI, final Layout<? extends Serializable> layout) {
54 super(fileName, pattern, os, append, size, time, policy, strategy, advertiseURI, layout, bufferSize);
55 this.isImmediateFlush = immediateFlush;
56 this.randomAccessFile = raf;
57 isEndOfBatch.set(Boolean.FALSE);
58 this.buffer = ByteBuffer.allocate(bufferSize);
59 writeHeader();
60 }
61
62
63
64
65 private void writeHeader() {
66 if (layout == null) {
67 return;
68 }
69 final byte[] header = layout.getHeader();
70 if (header == null) {
71 return;
72 }
73 try {
74
75 randomAccessFile.write(header, 0, header.length);
76 } catch (final IOException ioe) {
77 LOGGER.error("Unable to write header", ioe);
78 }
79 }
80
81 public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
82 final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
83 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
84 final Layout<? extends Serializable> layout) {
85 return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
86 immediateFlush, bufferSize, policy, strategy, advertiseURI, layout), FACTORY);
87 }
88
89 public Boolean isEndOfBatch() {
90 return isEndOfBatch.get();
91 }
92
93 public void setEndOfBatch(final boolean isEndOfBatch) {
94 this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
95 }
96
97 @Override
98 protected synchronized void write(final byte[] bytes, int offset, int length) {
99 super.write(bytes, offset, length);
100
101 int chunk = 0;
102 do {
103 if (length > buffer.remaining()) {
104 flush();
105 }
106 chunk = Math.min(length, buffer.remaining());
107 buffer.put(bytes, offset, chunk);
108 offset += chunk;
109 length -= chunk;
110 } while (length > 0);
111
112 if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
113 flush();
114 }
115 }
116
117 @Override
118 protected void createFileAfterRollover() throws IOException {
119 this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
120 if (isAppend()) {
121 randomAccessFile.seek(randomAccessFile.length());
122 }
123 writeHeader();
124 }
125
126 @Override
127 public synchronized void flush() {
128 buffer.flip();
129 try {
130 randomAccessFile.write(buffer.array(), 0, buffer.limit());
131 } catch (final IOException ex) {
132 final String msg = "Error writing to RandomAccessFile " + getName();
133 throw new AppenderLoggingException(msg, ex);
134 }
135 buffer.clear();
136 }
137
138 @Override
139 public synchronized void close() {
140 flush();
141 try {
142 randomAccessFile.close();
143 } catch (final IOException ex) {
144 LOGGER.error("Unable to close RandomAccessFile " + getName() + ". "
145 + ex);
146 }
147 }
148
149
150
151
152
153 @Override
154 public int getBufferSize() {
155 return buffer.capacity();
156 }
157
158
159
160
161 private static class RollingRandomAccessFileManagerFactory implements ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
162
163
164
165
166
167
168
169
170 @Override
171 public RollingRandomAccessFileManager createManager(final String name, final FactoryData data) {
172 final File file = new File(name);
173 final File parent = file.getParentFile();
174 if (null != parent && !parent.exists()) {
175 parent.mkdirs();
176 }
177
178 if (!data.append) {
179 file.delete();
180 }
181 final long size = data.append ? file.length() : 0;
182 final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
183
184 RandomAccessFile raf = null;
185 try {
186 raf = new RandomAccessFile(name, "rw");
187 if (data.append) {
188 final long length = raf.length();
189 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
190 raf.seek(length);
191 } else {
192 LOGGER.trace("RandomAccessFile {} set length to 0", name);
193 raf.setLength(0);
194 }
195 return new RollingRandomAccessFileManager(raf, name, data.pattern, NullOutputStream.NULL_OUTPUT_STREAM,
196 data.append, data.immediateFlush, data.bufferSize, size, time, data.policy, data.strategy,
197 data.advertiseURI, data.layout);
198 } catch (final IOException ex) {
199 LOGGER.error("Cannot access RandomAccessFile {}) " + ex);
200 if (raf != null) {
201 try {
202 raf.close();
203 } catch (final IOException e) {
204 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
205 }
206 }
207 }
208 return null;
209 }
210 }
211
212
213
214
215 private static class FactoryData {
216 private final String pattern;
217 private final boolean append;
218 private final boolean immediateFlush;
219 private final int bufferSize;
220 private final TriggeringPolicy policy;
221 private final RolloverStrategy strategy;
222 private final String advertiseURI;
223 private final Layout<? extends Serializable> layout;
224
225
226
227
228
229
230
231
232
233
234
235
236
237 public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
238 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
239 final String advertiseURI, final Layout<? extends Serializable> layout) {
240 this.pattern = pattern;
241 this.append = append;
242 this.immediateFlush = immediateFlush;
243 this.bufferSize = bufferSize;
244 this.policy = policy;
245 this.strategy = strategy;
246 this.advertiseURI = advertiseURI;
247 this.layout = layout;
248 }
249 }
250
251 }