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