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