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