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