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