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