1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender;
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 import java.util.HashMap;
26 import java.util.Map;
27
28 import org.apache.logging.log4j.core.Layout;
29
30
31
32
33
34
35 public class RandomAccessFileManager extends OutputStreamManager {
36 static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
37
38 private static final RandomAccessFileManagerFactory FACTORY = new RandomAccessFileManagerFactory();
39
40 private final boolean isImmediateFlush;
41 private final String advertiseURI;
42 private final RandomAccessFile randomAccessFile;
43 private final ByteBuffer buffer;
44 private final ThreadLocal<Boolean> isEndOfBatch = new ThreadLocal<Boolean>();
45
46 protected RandomAccessFileManager(final RandomAccessFile file,
47 final String fileName, final OutputStream os,
48 final boolean immediateFlush, final int bufferSize,
49 final String advertiseURI, final Layout<? extends Serializable> layout) {
50 super(os, fileName, layout);
51 this.isImmediateFlush = immediateFlush;
52 this.randomAccessFile = file;
53 this.advertiseURI = advertiseURI;
54 this.isEndOfBatch.set(Boolean.FALSE);
55 this.buffer = ByteBuffer.allocate(bufferSize);
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public static RandomAccessFileManager getFileManager(final String fileName, final boolean append,
72 final boolean isFlush, final int bufferSize, final String advertiseURI,
73 final Layout<? extends Serializable> layout) {
74 return (RandomAccessFileManager) getManager(fileName, new FactoryData(append,
75 isFlush, bufferSize, advertiseURI, layout), FACTORY);
76 }
77
78 public Boolean isEndOfBatch() {
79 return isEndOfBatch.get();
80 }
81
82 public void setEndOfBatch(final boolean isEndOfBatch) {
83 this.isEndOfBatch.set(Boolean.valueOf(isEndOfBatch));
84 }
85
86 @Override
87 protected synchronized void write(final byte[] bytes, int offset, int length) {
88 super.write(bytes, offset, length);
89
90 int chunk = 0;
91 do {
92 if (length > buffer.remaining()) {
93 flush();
94 }
95 chunk = Math.min(length, buffer.remaining());
96 buffer.put(bytes, offset, chunk);
97 offset += chunk;
98 length -= chunk;
99 } while (length > 0);
100
101 if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
102 flush();
103 }
104 }
105
106 @Override
107 public synchronized void flush() {
108 buffer.flip();
109 try {
110 randomAccessFile.write(buffer.array(), 0, buffer.limit());
111 } catch (final IOException ex) {
112 final String msg = "Error writing to RandomAccessFile " + getName();
113 throw new AppenderLoggingException(msg, ex);
114 }
115 buffer.clear();
116 }
117
118 @Override
119 public synchronized void close() {
120 flush();
121 try {
122 randomAccessFile.close();
123 } catch (final IOException ex) {
124 LOGGER.error("Unable to close RandomAccessFile " + getName() + ". "
125 + ex);
126 }
127 }
128
129
130
131
132
133
134 public String getFileName() {
135 return getName();
136 }
137
138
139
140
141
142 public int getBufferSize() {
143 return buffer.capacity();
144 }
145
146
147 static class DummyOutputStream extends OutputStream {
148 @Override
149 public void write(final int b) throws IOException {
150 }
151
152 @Override
153 public void write(final byte[] b, final int off, final int len) throws IOException {
154 }
155 }
156
157
158
159
160
161
162
163
164
165 @Override
166 public Map<String, String> getContentFormat() {
167 final Map<String, String> result = new HashMap<String, String>(
168 super.getContentFormat());
169 result.put("fileURI", advertiseURI);
170 return result;
171 }
172
173
174
175
176 private static class FactoryData {
177 private final boolean append;
178 private final boolean immediateFlush;
179 private final int bufferSize;
180 private final String advertiseURI;
181 private final Layout<? extends Serializable> layout;
182
183
184
185
186
187
188
189 public FactoryData(final boolean append, final boolean immediateFlush,
190 final int bufferSize, final String advertiseURI, final Layout<? extends Serializable> layout) {
191 this.append = append;
192 this.immediateFlush = immediateFlush;
193 this.bufferSize = bufferSize;
194 this.advertiseURI = advertiseURI;
195 this.layout = layout;
196 }
197 }
198
199
200
201
202 private static class RandomAccessFileManagerFactory implements
203 ManagerFactory<RandomAccessFileManager, FactoryData> {
204
205
206
207
208
209
210
211
212 @Override
213 public RandomAccessFileManager createManager(final String name, final FactoryData data) {
214 final File file = new File(name);
215 final File parent = file.getParentFile();
216 if (null != parent && !parent.exists()) {
217 parent.mkdirs();
218 }
219 if (!data.append) {
220 file.delete();
221 }
222
223 final OutputStream os = new DummyOutputStream();
224 RandomAccessFile raf;
225 try {
226 raf = new RandomAccessFile(name, "rw");
227 if (data.append) {
228 raf.seek(raf.length());
229 } else {
230 raf.setLength(0);
231 }
232 return new RandomAccessFileManager(raf, name, os, data.immediateFlush,
233 data.bufferSize, data.advertiseURI, data.layout);
234 } catch (final Exception ex) {
235 LOGGER.error("RandomAccessFileManager (" + name + ") " + ex);
236 }
237 return null;
238 }
239 }
240
241 }