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