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.BufferedOutputStream;
20 import java.io.File;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.io.Serializable;
26 import java.util.concurrent.Semaphore;
27
28 import org.apache.logging.log4j.core.Layout;
29 import org.apache.logging.log4j.core.LogEvent;
30 import org.apache.logging.log4j.core.appender.FileManager;
31 import org.apache.logging.log4j.core.appender.ManagerFactory;
32 import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
33 import org.apache.logging.log4j.core.appender.rolling.action.Action;
34
35
36
37
38 public class RollingFileManager extends FileManager {
39
40 private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
41
42 private long size;
43 private long initialTime;
44 private final PatternProcessor patternProcessor;
45 private final Semaphore semaphore = new Semaphore(1);
46 private final TriggeringPolicy triggeringPolicy;
47 private final RolloverStrategy rolloverStrategy;
48
49 protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
50 final boolean append, final long size, final long time, final TriggeringPolicy triggeringPolicy,
51 final RolloverStrategy rolloverStrategy, final String advertiseURI,
52 final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) {
53 super(fileName, os, append, false, advertiseURI, layout, bufferSize, writeHeader);
54 this.size = size;
55 this.initialTime = time;
56 this.triggeringPolicy = triggeringPolicy;
57 this.rolloverStrategy = rolloverStrategy;
58 this.patternProcessor = new PatternProcessor(pattern);
59 triggeringPolicy.initialize(this);
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
76 final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
77 final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize) {
78
79 return (RollingFileManager) getManager(fileName, new FactoryData(pattern, append,
80 bufferedIO, policy, strategy, advertiseURI, layout, bufferSize), factory);
81 }
82
83 @Override
84 protected synchronized void write(final byte[] bytes, final int offset, final int length) {
85 size += length;
86 super.write(bytes, offset, length);
87 }
88
89
90
91
92
93 public long getFileSize() {
94 return size;
95 }
96
97
98
99
100
101 public long getFileTime() {
102 return initialTime;
103 }
104
105
106
107
108
109 public synchronized void checkRollover(final LogEvent event) {
110 if (triggeringPolicy.isTriggeringEvent(event) && rollover(rolloverStrategy)) {
111 try {
112 size = 0;
113 initialTime = System.currentTimeMillis();
114 createFileAfterRollover();
115 } catch (final IOException ex) {
116 LOGGER.error("FileManager (" + getFileName() + ") " + ex);
117 }
118 }
119 }
120
121 protected void createFileAfterRollover() throws IOException {
122 final OutputStream os = new FileOutputStream(getFileName(), isAppend());
123 if (getBufferSize() > 0) {
124 setOutputStream(new BufferedOutputStream(os, getBufferSize()));
125 } else {
126 setOutputStream(os);
127 }
128 }
129
130
131
132
133
134 public PatternProcessor getPatternProcessor() {
135 return patternProcessor;
136 }
137
138
139
140
141
142 public <T extends TriggeringPolicy> T getTriggeringPolicy() {
143
144 return (T) this.triggeringPolicy;
145 }
146
147
148
149
150
151 public RolloverStrategy getRolloverStrategy() {
152 return this.rolloverStrategy;
153 }
154
155 private boolean rollover(final RolloverStrategy strategy) {
156
157 try {
158
159 semaphore.acquire();
160 } catch (final InterruptedException ie) {
161 LOGGER.error("Thread interrupted while attempting to check rollover", ie);
162 return false;
163 }
164
165 boolean success = false;
166 Thread thread = null;
167
168 try {
169 final RolloverDescription descriptor = strategy.rollover(this);
170 if (descriptor != null) {
171 writeFooter();
172 close();
173 if (descriptor.getSynchronous() != null) {
174 LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
175 try {
176 success = descriptor.getSynchronous().execute();
177 } catch (final Exception ex) {
178 LOGGER.error("Error in synchronous task", ex);
179 }
180 }
181
182 if (success && descriptor.getAsynchronous() != null) {
183 LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
184 thread = new Thread(new AsyncAction(descriptor.getAsynchronous(), this));
185 thread.start();
186 }
187 return true;
188 }
189 return false;
190 } finally {
191 if (thread == null || !thread.isAlive()) {
192 semaphore.release();
193 }
194 }
195
196 }
197
198
199
200
201 private static class AsyncAction extends AbstractAction {
202
203 private final Action action;
204 private final RollingFileManager manager;
205
206
207
208
209
210
211 public AsyncAction(final Action act, final RollingFileManager manager) {
212 this.action = act;
213 this.manager = manager;
214 }
215
216
217
218
219
220
221
222
223
224 @Override
225 public boolean execute() throws IOException {
226 try {
227 return action.execute();
228 } finally {
229 manager.semaphore.release();
230 }
231 }
232
233
234
235
236 @Override
237 public void close() {
238 action.close();
239 }
240
241
242
243
244
245
246 @Override
247 public boolean isComplete() {
248 return action.isComplete();
249 }
250 }
251
252
253
254
255 private static class FactoryData {
256 private final String pattern;
257 private final boolean append;
258 private final boolean bufferedIO;
259 private final int bufferSize;
260 private final TriggeringPolicy policy;
261 private final RolloverStrategy strategy;
262 private final String advertiseURI;
263 private final Layout<? extends Serializable> layout;
264
265
266
267
268
269
270
271
272
273
274 public FactoryData(final String pattern, final boolean append, final boolean bufferedIO,
275 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
276 final Layout<? extends Serializable> layout, final int bufferSize) {
277 this.pattern = pattern;
278 this.append = append;
279 this.bufferedIO = bufferedIO;
280 this.bufferSize = bufferSize;
281 this.policy = policy;
282 this.strategy = strategy;
283 this.advertiseURI = advertiseURI;
284 this.layout = layout;
285 }
286 }
287
288
289
290
291 private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
292
293
294
295
296
297
298
299 @Override
300 public RollingFileManager createManager(final String name, final FactoryData data) {
301 final File file = new File(name);
302 final File parent = file.getParentFile();
303 if (null != parent && !parent.exists()) {
304 parent.mkdirs();
305 }
306 try {
307 file.createNewFile();
308 } catch (final IOException ioe) {
309 LOGGER.error("Unable to create file " + name, ioe);
310 return null;
311 }
312 final long size = data.append ? file.length() : 0;
313
314 final boolean writeHeader = !data.append || !file.exists();
315 OutputStream os;
316 try {
317 os = new FileOutputStream(name, data.append);
318 int bufferSize = data.bufferSize;
319 if (data.bufferedIO) {
320 os = new BufferedOutputStream(os, bufferSize);
321 } else {
322 bufferSize = -1;
323 }
324 final long time = file.lastModified();
325 return new RollingFileManager(name, data.pattern, os, data.append, size, time, data.policy,
326 data.strategy, data.advertiseURI, data.layout, bufferSize, writeHeader);
327 } catch (final FileNotFoundException ex) {
328 LOGGER.error("FileManager (" + name + ") " + ex);
329 }
330 return null;
331 }
332 }
333
334 }