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 public RollingRandomAccessFileManager(final LoggerContext loggerContext, final RandomAccessFile raf,
51 final String fileName, final String pattern, final OutputStream os, final boolean append,
52 final boolean immediateFlush, final int bufferSize, final long size, final long time,
53 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
54 final Layout<? extends Serializable> layout, final boolean writeHeader) {
55 super(loggerContext, fileName, pattern, os, append, false, size, time, policy, strategy, advertiseURI, layout,
56 writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
57 this.randomAccessFile = raf;
58 isEndOfBatch.set(Boolean.FALSE);
59 writeHeader();
60 }
61
62
63
64
65 private void writeHeader() {
66 if (layout == null) {
67 return;
68 }
69 final byte[] header = layout.getHeader();
70 if (header == null) {
71 return;
72 }
73 try {
74 if (randomAccessFile.length() == 0) {
75
76 randomAccessFile.write(header, 0, header.length);
77 }
78 } catch (final IOException e) {
79 logError("Unable to write header", e);
80 }
81 }
82
83 public static RollingRandomAccessFileManager getRollingRandomAccessFileManager(final String fileName,
84 final String filePattern, final boolean isAppend, final boolean immediateFlush, final int bufferSize,
85 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
86 final Layout<? extends Serializable> layout, final Configuration configuration) {
87 return (RollingRandomAccessFileManager) getManager(fileName, new FactoryData(filePattern, isAppend,
88 immediateFlush, bufferSize, policy, strategy, advertiseURI, layout, configuration), FACTORY);
89 }
90
91 public Boolean isEndOfBatch() {
92 return isEndOfBatch.get();
93 }
94
95 public void setEndOfBatch(final boolean endOfBatch) {
96 this.isEndOfBatch.set(Boolean.valueOf(endOfBatch));
97 }
98
99
100 @Override
101 protected synchronized void write(final byte[] bytes, final int offset, final int length,
102 final boolean immediateFlush) {
103 super.write(bytes, offset, length, immediateFlush);
104 }
105
106 @Override
107 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
108 try {
109 randomAccessFile.write(bytes, offset, length);
110 size += length;
111 } catch (final IOException ex) {
112 final String msg = "Error writing to RandomAccessFile " + getName();
113 throw new AppenderLoggingException(msg, ex);
114 }
115 }
116
117 @Override
118 protected void createFileAfterRollover() throws IOException {
119 this.randomAccessFile = new RandomAccessFile(getFileName(), "rw");
120 if (isAppend()) {
121 randomAccessFile.seek(randomAccessFile.length());
122 }
123 writeHeader();
124 }
125
126 @Override
127 public synchronized void flush() {
128 flushBuffer(byteBuffer);
129 }
130
131 @Override
132 public synchronized boolean closeOutputStream() {
133 flush();
134 try {
135 randomAccessFile.close();
136 return true;
137 } catch (final IOException e) {
138 logError("Unable to close RandomAccessFile", e);
139 return false;
140 }
141 }
142
143
144
145
146
147
148 @Override
149 public int getBufferSize() {
150 return byteBuffer.capacity();
151 }
152
153
154
155
156 private static class RollingRandomAccessFileManagerFactory implements
157 ManagerFactory<RollingRandomAccessFileManager, FactoryData> {
158
159
160
161
162
163
164
165
166 @Override
167 public RollingRandomAccessFileManager createManager(final String name, final FactoryData data) {
168 final File file = new File(name);
169
170 if (!data.append) {
171 file.delete();
172 }
173 final long size = data.append ? file.length() : 0;
174 final long time = file.exists() ? file.lastModified() : System.currentTimeMillis();
175
176 final boolean writeHeader = !data.append || !file.exists();
177 RandomAccessFile raf = null;
178 try {
179 FileUtils.makeParentDirs(file);
180 raf = new RandomAccessFile(name, "rw");
181 if (data.append) {
182 final long length = raf.length();
183 LOGGER.trace("RandomAccessFile {} seek to {}", name, length);
184 raf.seek(length);
185 } else {
186 LOGGER.trace("RandomAccessFile {} set length to 0", name);
187 raf.setLength(0);
188 }
189 return new RollingRandomAccessFileManager(data.getLoggerContext(), raf, name, data.pattern,
190 NullOutputStream.getInstance(), data.append, data.immediateFlush, data.bufferSize, size, time, data.policy,
191 data.strategy, data.advertiseURI, data.layout, writeHeader);
192 } catch (final IOException ex) {
193 LOGGER.error("Cannot access RandomAccessFile " + ex, ex);
194 if (raf != null) {
195 try {
196 raf.close();
197 } catch (final IOException e) {
198 LOGGER.error("Cannot close RandomAccessFile {}", name, e);
199 }
200 }
201 }
202 return null;
203 }
204 }
205
206
207
208
209 private static class FactoryData extends ConfigurationFactoryData {
210 private final String pattern;
211 private final boolean append;
212 private final boolean immediateFlush;
213 private final int bufferSize;
214 private final TriggeringPolicy policy;
215 private final RolloverStrategy strategy;
216 private final String advertiseURI;
217 private final Layout<? extends Serializable> layout;
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 public FactoryData(final String pattern, final boolean append, final boolean immediateFlush,
233 final int bufferSize, final TriggeringPolicy policy, final RolloverStrategy strategy,
234 final String advertiseURI, final Layout<? extends Serializable> layout, final Configuration configuration) {
235 super(configuration);
236 this.pattern = pattern;
237 this.append = append;
238 this.immediateFlush = immediateFlush;
239 this.bufferSize = bufferSize;
240 this.policy = policy;
241 this.strategy = strategy;
242 this.advertiseURI = advertiseURI;
243 this.layout = layout;
244 }
245
246 public TriggeringPolicy getTriggeringPolicy()
247 {
248 return this.policy;
249 }
250
251 public RolloverStrategy getRolloverStrategy()
252 {
253 return this.strategy;
254 }
255 }
256
257 @Override
258 public void updateData(final Object data) {
259 final FactoryData factoryData = (FactoryData) data;
260 setRolloverStrategy(factoryData.getRolloverStrategy());
261 setTriggeringPolicy(factoryData.getTriggeringPolicy());
262 }
263 }