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.LoggerContext;
28 import org.apache.logging.log4j.core.appender.AppenderLoggingException;
29 import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
30 import org.apache.logging.log4j.core.appender.ManagerFactory;
31 import org.apache.logging.log4j.core.config.Configuration;
32 import org.apache.logging.log4j.core.util.NullOutputStream;
33
34
35
36
37
38 public class RollingRandomAccessFileManager extends RollingFileManager {
39
40
41
42 public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
43
44 private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
45
46 private RandomAccessFile randomAccessFile;
47 private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
48
49 public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
50 final String fileName, 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, final String advertiseURI,
53 final Layout<? extends Serializable> layout, final boolean writeHeader) {
54 super(loggerContext, fileName, pattern, os, append, false, size, time, policy, strategy, advertiseURI, layout,
55 writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
56 this.randomAccessFile = raf;
57 isEndOfBatch.set(Boolean.FALSE);
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 if (randomAccessFile.length() == 0) {
74
75 randomAccessFile.write(header, 0, header.length);
76 }
77 } catch (final IOException e) {
78 logError("Unable to write header", e);
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, final Configuration configuration) {
86 return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
87 immediateFlush, bufferSize, policy, strategy, advertiseURI, layout, configuration), FACTORY);
88 }
89
90 public Boolean isEndOfBatch() {
91 return isEndOfBatch.get();
92 }
93
94 public void setEndOfBatch(final boolean endOfBatch) {
95 this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
96 }
97
98
99 @Override
100 protected synchronized void write(final byte[] bytes, final int offset, final int length,
101 final boolean immediateFlush) {
102 super.write(bytes, offset, length, immediateFlush);
103 }
104
105 @Override
106 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
107 try {
108 randomAccessFile.write(bytes, offset, length);
109 size += length;
110 } catch (final IOException ex) {
111 final String msg = "Error writing to RandomAccessFile " + getName();
112 throw new AppenderLoggingException(msg, ex);
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 flushBuffer(byteBuffer);
128 }
129
130 @Override
131 public synchronized boolean closeOutputStream() {
132 flush();
133 try {
134 randomAccessFile.close();
135 return true;
136 } catch (final IOException e) {
137 logError("Unable to close RandomAccessFile", e);
138 return false;
139 }
140 }
141
142
143
144
145
146
147 @Override
148 public int getBufferSize() {
149 return byteBuffer.capacity();
150 }
151
152
153
154
155 private static class RollingRandomAccessFileManagerFactory implements
156 ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
157
158
159
160
161
162
163
164
165 @Override
166 public RollingRandomAccessFileManager createManager(final String name, final FactoryData data) {
167 final File file = new File(name);
168 final File parent = file.getParentFile();
169 if (null != parent && !parent.exists()) {
170 parent.mkdirs();
171 }
172
173 if (!data.append) {
174 file.delete();
175 }
176 final long size = data.append ? file.length() : 0;
177 final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
178
179 final boolean writeHeader = !data.append || !file.exists();
180 RandomAccessFile raf = null;
181 try {
182 raf = new RandomAccessFile(name, "rw");
183 if (data.append) {
184 final long length = raf.length();
185 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
186 raf.seek(length);
187 } else {
188 LOGGER.trace("RandomAccessFile {} set length to 0", name);
189 raf.setLength(0);
190 }
191 return new RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
192 NullOutputStream.getInstance(), data.append, data.immediateFlush, data.bufferSize, size, time, data.policy,
193 data.strategy, data.advertiseURI, data.layout, writeHeader);
194 } catch (final IOException ex) {
195 LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
196 if (raf != null) {
197 try {
198 raf.close();
199 } catch (final IOException e) {
200 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
201 }
202 }
203 }
204 return null;
205 }
206 }
207
208
209
210
211 private static class FactoryData extends ConfigurationFactoryData {
212 private final String pattern;
213 private final boolean append;
214 private final boolean immediateFlush;
215 private final int bufferSize;
216 private final TriggeringPolicy policy;
217 private final RolloverStrategy strategy;
218 private final String advertiseURI;
219 private final Layout<? extends Serializable> layout;
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
235 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
236 final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
237 super(configuration);
238 this.pattern = pattern;
239 this.append = append;
240 this.immediateFlush = immediateFlush;
241 this.bufferSize = bufferSize;
242 this.policy = policy;
243 this.strategy = strategy;
244 this.advertiseURI = advertiseURI;
245 this.layout = layout;
246 }
247
248 public TriggeringPolicy getTriggeringPolicy()
249 {
250 return this.policy;
251 }
252
253 public RolloverStrategy getRolloverStrategy()
254 {
255 return this.strategy;
256 }
257 }
258
259 @Override
260 public void updateData(final Object data) {
261 final FactoryData factoryData = (FactoryData) data;
262 setRolloverStrategy(factoryData.getRolloverStrategy());
263 setTriggeringPolicy(factoryData.getTriggeringPolicy());
264 }
265 }