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 public class RollingRandomAccessFileManager extends RollingFileManager {
36
37
38
39 public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
40
41 private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
42
43 private final boolean isImmediateFlush;
44 private RandomAccessFile randomAccessFile;
45 private final ByteBuffer buffer;
46 private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
47
48 public RollingRandomAccessFileManager(final RandomAccessFile raf, final String fileName, final String pattern,
49 final OutputStream os, final boolean append, final boolean immediateFlush, final int bufferSize,
50 final long size, final long time, final TriggeringPolicy policy, final RolloverStrategy strategy,
51 final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader) {
52 super(fileName, pattern, os, append, size, time, policy, strategy, advertiseURI, layout, bufferSize,
53 writeHeader);
54 this.isImmediateFlush = immediateFlush;
55 this.randomAccessFile = raf;
56 isEndOfBatch.set(Boolean.FALSE);
57 this.buffer = ByteBuffer.allocate(bufferSize);
58 writeHeader();
59 }
60
61
62
63
64 private void writeHeader() {
65 if (layout == null) {
66 return;
67 }
68 final byte[] header = layout.getHeader();
69 if (header == null) {
70 return;
71 }
72 try {
73
74 randomAccessFile.write(header, 0, header.length);
75 } catch (final IOException e) {
76 logError("unable to write header", e);
77 }
78 }
79
80 public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
81 final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
82 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
83 final Layout<? extends Serializable> layout) {
84 return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
85 immediateFlush, bufferSize, policy, strategy, advertiseURI, layout), FACTORY);
86 }
87
88 public Boolean isEndOfBatch() {
89 return isEndOfBatch.get();
90 }
91
92 public void setEndOfBatch(final boolean endOfBatch) {
93 this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
94 }
95
96 @Override
97 protected synchronized void write(final byte[] bytes, int offset, int length) {
98 super.write(bytes, offset, length);
99
100 int chunk = 0;
101 do {
102 if (length > buffer.remaining()) {
103 flush();
104 }
105 chunk = Math.min(length, buffer.remaining());
106 buffer.put(bytes, offset, chunk);
107 offset += chunk;
108 length -= chunk;
109 } while (length > 0);
110
111 if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
112 flush();
113 }
114 }
115
116 @Override
117 protected void createFileAfterRollover() throws IOException {
118 this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
119 if (isAppend()) {
120 randomAccessFile.seek(randomAccessFile.length());
121 }
122 writeHeader();
123 }
124
125 @Override
126 public synchronized void flush() {
127 buffer.flip();
128 try {
129 randomAccessFile.write(buffer.array(), 0, buffer.limit());
130 } catch (final IOException ex) {
131 final String msg = "Error writing to RandomAccessFile " + getName();
132 throw new AppenderLoggingException(msg, ex);
133 }
134 buffer.clear();
135 }
136
137 @Override
138 public synchronized void close() {
139 flush();
140 try {
141 randomAccessFile.close();
142 } catch (final IOException e) {
143 logError("unable to close RandomAccessFile", e);
144 }
145 }
146
147
148
149
150
151
152 @Override
153 public int getBufferSize() {
154 return buffer.capacity();
155 }
156
157
158
159
160 private static class RollingRandomAccessFileManagerFactory implements
161 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 final boolean writeHeader = !data.append || !file.exists();
185 RandomAccessFile raf = null;
186 try {
187 raf = new RandomAccessFile(name, "rw");
188 if (data.append) {
189 final long length = raf.length();
190 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
191 raf.seek(length);
192 } else {
193 LOGGER.trace("RandomAccessFile {} set length to 0", name);
194 raf.setLength(0);
195 }
196 return new RollingRandomAccessFileManager(raf, name, data.pattern, NullOutputStream.NULL_OUTPUT_STREAM,
197 data.append, data.immediateFlush, data.bufferSize, size, time, data.policy, data.strategy,
198 data.advertiseURI, data.layout, writeHeader);
199 } catch (final IOException ex) {
200 LOGGER.error("Cannot access RandomAccessFile {}) " + ex);
201 if (raf != null) {
202 try {
203 raf.close();
204 } catch (final IOException e) {
205 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
206 }
207 }
208 }
209 return null;
210 }
211 }
212
213
214
215
216 private static class FactoryData {
217 private final String pattern;
218 private final boolean append;
219 private final boolean immediateFlush;
220 private final int bufferSize;
221 private final TriggeringPolicy policy;
222 private final RolloverStrategy strategy;
223 private final String advertiseURI;
224 private final Layout<? extends Serializable> layout;
225
226
227
228
229
230
231
232
233
234
235
236
237
238 public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
239 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
240 final String advertiseURI, final Layout<? extends Serializable> layout) {
241 this.pattern = pattern;
242 this.append = append;
243 this.immediateFlush = immediateFlush;
244 this.bufferSize = bufferSize;
245 this.policy = policy;
246 this.strategy = strategy;
247 this.advertiseURI = advertiseURI;
248 this.layout = layout;
249 }
250
251 public TriggeringPolicy getTriggeringPolicy()
252 {
253 return this.policy;
254 }
255
256 public RolloverStrategy getRolloverStrategy()
257 {
258 return this.strategy;
259 }
260 }
261
262 @Override
263 public void updateData(final Object data)
264 {
265 final FactoryData factoryData = (FactoryData) data;
266 setRolloverStrategy(factoryData.getRolloverStrategy());
267 setTriggeringPolicy(factoryData.getTriggeringPolicy());
268 }
269 }