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