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