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