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
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<Boolean>();
47
48 public RollingRandomAccessFileManager(final RandomAccessFile raf, final String fileName,
49 final String pattern, final OutputStream os, final boolean append,
50 final boolean immediateFlush, final int bufferSize, final long size, final long time,
51 final TriggeringPolicy policy, final RolloverStrategy strategy,
52 final String advertiseURI, final Layout<? extends Serializable> layout) {
53 super(fileName, pattern, os, append, size, time, policy, strategy, advertiseURI, layout, bufferSize);
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 ioe) {
76 LOGGER.error("Unable to write header", ioe);
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 isEndOfBatch) {
93 this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
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 ex) {
143 LOGGER.error("Unable to close RandomAccessFile " + getName() + ". "
144 + ex);
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 ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
161
162
163
164
165
166
167
168
169 @Override
170 public RollingRandomAccessFileManager createManager(final String name, final FactoryData data) {
171 final File file = new File(name);
172 final File parent = file.getParentFile();
173 if (null != parent && !parent.exists()) {
174 parent.mkdirs();
175 }
176
177 if (!data.append) {
178 file.delete();
179 }
180 final long size = data.append ? file.length() : 0;
181 final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
182
183 RandomAccessFile raf = null;
184 try {
185 raf = new RandomAccessFile(name, "rw");
186 if (data.append) {
187 final long length = raf.length();
188 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
189 raf.seek(length);
190 } else {
191 LOGGER.trace("RandomAccessFile {} set length to 0", name);
192 raf.setLength(0);
193 }
194 return new RollingRandomAccessFileManager(raf, name, data.pattern, new DummyOutputStream(), data.append,
195 data.immediateFlush, data.bufferSize, size, time, data.policy, data.strategy, data.advertiseURI,
196 data.layout);
197 } catch (final IOException ex) {
198 LOGGER.error("Cannot access RandomAccessFile {}) " + ex);
199 if (raf != null) {
200 try {
201 raf.close();
202 } catch (final IOException e) {
203 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
204 }
205 }
206 }
207 return null;
208 }
209 }
210
211
212 static class DummyOutputStream extends OutputStream {
213 @Override
214 public void write(final int b) throws IOException {
215 }
216
217 @Override
218 public void write(final byte[] b, final int off, final int len) throws IOException {
219 }
220 }
221
222
223
224
225 private static class FactoryData {
226 private final String pattern;
227 private final boolean append;
228 private final boolean immediateFlush;
229 private final int bufferSize;
230 private final TriggeringPolicy policy;
231 private final RolloverStrategy strategy;
232 private final String advertiseURI;
233 private final Layout<? extends Serializable> layout;
234
235
236
237
238
239
240
241
242
243
244
245
246
247 public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
248 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
249 final String advertiseURI, final Layout<? extends Serializable> layout) {
250 this.pattern = pattern;
251 this.append = append;
252 this.immediateFlush = immediateFlush;
253 this.bufferSize = bufferSize;
254 this.policy = policy;
255 this.strategy = strategy;
256 this.advertiseURI = advertiseURI;
257 this.layout = layout;
258 }
259 }
260
261 }