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