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.FileUtils;
33 import org.apache.logging.log4j.core.util.NullOutputStream;
34
35
36
37
38
39 public class RollingRandomAccessFileManager extends RollingFileManager {
40
41
42
43 public static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
44
45 private static final RollingRandomAccessFileManagerFactory FACTORY = new RollingRandomAccessFileManagerFactory();
46
47 private RandomAccessFile randomAccessFile;
48 private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<>();
49
50 @Deprecated
51 public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
52 final String fileName, final String pattern, final OutputStream os, final boolean append,
53 final boolean immediateFlush, final int bufferSize, final long size, final long time,
54 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
55 final Layout<? extends Serializable> layout, final boolean writeHeader) {
56 this(loggerContext, raf, fileName, pattern, os, append, immediateFlush, bufferSize, size, time, policy, strategy, advertiseURI,
57 layout, null, null, null, writeHeader);
58 }
59
60
61
62
63 public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
64 final String fileName, final String pattern, final OutputStream os, final boolean append,
65 final boolean immediateFlush, final int bufferSize, final long size, final long initialTime,
66 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
67 final Layout<? extends Serializable> layout,
68 final String filePermissions, final String fileOwner, final String fileGroup,
69 final boolean writeHeader) {
70 super(loggerContext, fileName, pattern, os, append, false, size, initialTime, policy, strategy, advertiseURI,
71 layout, filePermissions, fileOwner, fileGroup, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
72 this.randomAccessFile = raf;
73 isEndOfBatch.set(Boolean.FALSE);
74 writeHeader();
75 }
76
77
78
79
80 private void writeHeader() {
81 if (layout == null) {
82 return;
83 }
84 final byte[] header = layout.getHeader();
85 if (header == null) {
86 return;
87 }
88 try {
89 if (randomAccessFile != null && randomAccessFile.length() == 0) {
90
91 randomAccessFile.write(header, 0, header.length);
92 }
93 } catch (final IOException e) {
94 logError("Unable to write header", e);
95 }
96 }
97
98 public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
99 final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
100 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
101 final Layout<? extends Serializable> layout, final String filePermissions, final String fileOwner, final String fileGroup,
102 final Configuration configuration) {
103 if (strategy instanceof DirectWriteRolloverStrategy && fileName != null) {
104 LOGGER.error("The fileName attribute must not be specified with the DirectWriteRolloverStrategy");
105 return null;
106 }
107 final String name = fileName == null ? filePattern : fileName;
108 return narrow(RollingRandomAccessFileManager.class, getManager(name, new FactoryData(fileName, filePattern, isAppend,
109 immediateFlush, bufferSize, policy, strategy, advertiseURI, layout,
110 filePermissions, fileOwner, fileGroup, configuration), FACTORY));
111 }
112
113 public Boolean isEndOfBatch() {
114 return isEndOfBatch.get();
115 }
116
117 public void setEndOfBatch(final boolean endOfBatch) {
118 this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
119 }
120
121
122 @Override
123 protected synchronized void write(final byte[] bytes, final int offset, final int length,
124 final boolean immediateFlush) {
125 super.write(bytes, offset, length, immediateFlush);
126 }
127
128 @Override
129 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
130 try {
131 if (randomAccessFile == null) {
132 final String fileName = getFileName();
133 final File file = new File(fileName);
134 FileUtils.makeParentDirs(file);
135 createFileAfterRollover(fileName);
136 }
137 randomAccessFile.write(bytes, offset, length);
138 size += length;
139 } catch (final IOException ex) {
140 final String msg = "Error writing to RandomAccessFile " + getName();
141 throw new AppenderLoggingException(msg, ex);
142 }
143 }
144
145 @Override
146 protected void createFileAfterRollover() throws IOException {
147 createFileAfterRollover(getFileName());
148 }
149
150 private void createFileAfterRollover(final String fileName) throws IOException {
151 this.randomAccessFile = new RandomAccessFile(fileName, "rw");
152 if (isAppend()) {
153 randomAccessFile.seek(randomAccessFile.length());
154 }
155 writeHeader();
156 }
157
158 @Override
159 public synchronized void flush() {
160 flushBuffer(byteBuffer);
161 }
162
163 @Override
164 public synchronized boolean closeOutputStream() {
165 flush();
166 if (randomAccessFile != null) {
167 try {
168 randomAccessFile.close();
169 return true;
170 } catch (final IOException e) {
171 logError("Unable to close RandomAccessFile", e);
172 return false;
173 }
174 }
175 return true;
176 }
177
178
179
180
181
182
183 @Override
184 public int getBufferSize() {
185 return byteBuffer.capacity();
186 }
187
188
189
190
191 private static class RollingRandomAccessFileManagerFactory implements
192 ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
193
194
195
196
197
198
199
200
201 @Override
202 public RollingRandomAccessFileManager createManager(final String name, final FactoryData data) {
203 File file = null;
204 long size = 0;
205 long time = System.currentTimeMillis();
206 RandomAccessFile raf = null;
207 if (data.fileName != null) {
208 file = new File(name);
209
210 if (!data.append) {
211 file.delete();
212 }
213 size = data.append ? file.length() : 0;
214 if (file.exists()) {
215 time = file.lastModified();
216 }
217 try {
218 FileUtils.makeParentDirs(file);
219 raf = new RandomAccessFile(name, "rw");
220 if (data.append) {
221 final long length = raf.length();
222 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
223 raf.seek(length);
224 } else {
225 LOGGER.trace("RandomAccessFile {} set length to 0", name);
226 raf.setLength(0);
227 }
228 } catch (final IOException ex) {
229 LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
230 if (raf != null) {
231 try {
232 raf.close();
233 } catch (final IOException e) {
234 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
235 }
236 }
237 return null;
238 }
239 }
240 final boolean writeHeader = !data.append || file == null || !file.exists();
241
242 final RollingRandomAccessFileManager rrm = new RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
243 NullOutputStream.getInstance(), data.append, data.immediateFlush, data.bufferSize, size, time, data.policy,
244 data.strategy, data.advertiseURI, data.layout, data.filePermissions, data.fileOwner, data.fileGroup, writeHeader);
245 if (rrm.isAttributeViewEnabled()) {
246 rrm.defineAttributeView(file.toPath());
247 }
248 return rrm;
249 }
250 }
251
252
253
254
255 private static class FactoryData extends ConfigurationFactoryData {
256 private final String fileName;
257 private final String pattern;
258 private final boolean append;
259 private final boolean immediateFlush;
260 private final int bufferSize;
261 private final TriggeringPolicy policy;
262 private final RolloverStrategy strategy;
263 private final String advertiseURI;
264 private final Layout<? extends Serializable> layout;
265 private final String filePermissions;
266 private final String fileOwner;
267 private final String fileGroup;
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 public FactoryData(final String fileName, final String pattern, final boolean append, final boolean immediateFlush,
287 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
288 final String advertiseURI, final Layout<? extends Serializable> layout,
289 final String filePermissions, final String fileOwner, final String fileGroup,
290 final Configuration configuration) {
291 super(configuration);
292 this.fileName = fileName;
293 this.pattern = pattern;
294 this.append = append;
295 this.immediateFlush = immediateFlush;
296 this.bufferSize = bufferSize;
297 this.policy = policy;
298 this.strategy = strategy;
299 this.advertiseURI = advertiseURI;
300 this.layout = layout;
301 this.filePermissions = filePermissions;
302 this.fileOwner = fileOwner;
303 this.fileGroup = fileGroup;
304 }
305
306 public String getPattern() {
307 return pattern;
308 }
309
310 public TriggeringPolicy getTriggeringPolicy() {
311 return this.policy;
312 }
313
314 public RolloverStrategy getRolloverStrategy() {
315 return this.strategy;
316 }
317
318 }
319
320 @Override
321 public void updateData(final Object data) {
322 final FactoryData factoryData = (FactoryData) data;
323 setRolloverStrategy(factoryData.getRolloverStrategy());
324 setTriggeringPolicy(factoryData.getTriggeringPolicy());
325 setPatternProcessor(new PatternProcessor(factoryData.getPattern(), getPatternProcessor()));
326 }
327 }