View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
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.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.attribute.BasicFileAttributes;
28  import java.nio.file.attribute.FileTime;
29  import java.util.Collection;
30  import java.util.Date;
31  import java.util.concurrent.ArrayBlockingQueue;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Semaphore;
34  import java.util.concurrent.ThreadPoolExecutor;
35  import java.util.concurrent.TimeUnit;
36  import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
37  
38  import org.apache.logging.log4j.core.Layout;
39  import org.apache.logging.log4j.core.LifeCycle;
40  import org.apache.logging.log4j.core.LifeCycle2;
41  import org.apache.logging.log4j.core.LogEvent;
42  import org.apache.logging.log4j.core.LoggerContext;
43  import org.apache.logging.log4j.core.appender.ConfigurationFactoryData;
44  import org.apache.logging.log4j.core.appender.FileManager;
45  import org.apache.logging.log4j.core.appender.ManagerFactory;
46  import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
47  import org.apache.logging.log4j.core.appender.rolling.action.Action;
48  import org.apache.logging.log4j.core.config.Configuration;
49  import org.apache.logging.log4j.core.util.Constants;
50  import org.apache.logging.log4j.core.util.FileUtils;
51  import org.apache.logging.log4j.core.util.Log4jThreadFactory;
52  
53  /**
54   * The Rolling File Manager.
55   */
56  public class RollingFileManager extends FileManager {
57  
58      private static RollingFileManagerFactory factory = new RollingFileManagerFactory();
59      private static final int MAX_TRIES = 3;
60      private static final int MIN_DURATION = 100;
61      private static final FileTime EPOCH = FileTime.fromMillis(0);
62  
63      protected long size;
64      private long initialTime;
65      private volatile PatternProcessor patternProcessor;
66      private final Semaphore semaphore = new Semaphore(1);
67      private final Log4jThreadFactory threadFactory = Log4jThreadFactory.createThreadFactory("RollingFileManager");
68      private volatile TriggeringPolicy triggeringPolicy;
69      private volatile RolloverStrategy rolloverStrategy;
70      private volatile boolean renameEmptyFiles = false;
71      private volatile boolean initialized = false;
72      private volatile String fileName;
73      private final FileExtension fileExtension;
74  
75      /* This executor pool will create a new Thread for every work async action to be performed. Using it allows
76         us to make sure all the Threads are completed when the Manager is stopped. */
77      private final ExecutorService asyncExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS,
78              new EmptyQueue(), threadFactory);
79  
80      private static final AtomicReferenceFieldUpdater<RollingFileManager, TriggeringPolicy> triggeringPolicyUpdater =
81              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, TriggeringPolicy.class, "triggeringPolicy");
82  
83      private static final AtomicReferenceFieldUpdater<RollingFileManager, RolloverStrategy> rolloverStrategyUpdater =
84              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, RolloverStrategy.class, "rolloverStrategy");
85  
86      private static final AtomicReferenceFieldUpdater<RollingFileManager, PatternProcessor> patternProcessorUpdater =
87              AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, PatternProcessor.class, "patternProcessor");
88  
89      @Deprecated
90      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
91              final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy,
92              final RolloverStrategy rolloverStrategy, final String advertiseURI,
93              final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) {
94          this(fileName, pattern, os, append, size, initialTime, triggeringPolicy, rolloverStrategy, advertiseURI, layout,
95                  writeHeader, ByteBuffer.wrap(new byte[Constants.ENCODER_BYTE_BUFFER_SIZE]));
96      }
97  
98      @Deprecated
99      protected RollingFileManager(final String fileName, final String pattern, final OutputStream os,
100             final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy,
101             final RolloverStrategy rolloverStrategy, final String advertiseURI,
102             final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
103         super(fileName != null ? fileName : pattern, os, append, false, advertiseURI, layout, writeHeader,
104 			buffer);
105         this.size = size;
106         this.initialTime = initialTime;
107         this.triggeringPolicy = triggeringPolicy;
108         this.rolloverStrategy = rolloverStrategy;
109         this.patternProcessor = new PatternProcessor(pattern);
110         this.patternProcessor.setPrevFileTime(initialTime);
111         this.fileName = fileName;
112         this.fileExtension = FileExtension.lookupForFile(pattern);
113     }
114 
115     @Deprecated
116     protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
117             final boolean append, final boolean createOnDemand, final long size, final long initialTime,
118             final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
119             final String advertiseURI, final Layout<? extends Serializable> layout, final boolean writeHeader, final ByteBuffer buffer) {
120         super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
121 			advertiseURI, layout, writeHeader, buffer);
122         this.size = size;
123         this.initialTime = initialTime;
124         this.triggeringPolicy = triggeringPolicy;
125         this.rolloverStrategy = rolloverStrategy;
126         this.patternProcessor = new PatternProcessor(pattern);
127         this.patternProcessor.setPrevFileTime(initialTime);
128         this.fileName = fileName;
129         this.fileExtension = FileExtension.lookupForFile(pattern);
130     }
131 
132     /**
133      * @since 2.9
134      */
135     protected RollingFileManager(final LoggerContext loggerContext, final String fileName, final String pattern, final OutputStream os,
136             final boolean append, final boolean createOnDemand, final long size, final long initialTime,
137             final TriggeringPolicy triggeringPolicy, final RolloverStrategy rolloverStrategy,
138             final String advertiseURI, final Layout<? extends Serializable> layout,
139             final String filePermissions, final String fileOwner, final String fileGroup,
140             final boolean writeHeader, final ByteBuffer buffer) {
141         super(loggerContext, fileName != null ? fileName : pattern, os, append, false, createOnDemand,
142 			advertiseURI, layout, filePermissions, fileOwner, fileGroup, writeHeader, buffer);
143         this.size = size;
144         this.initialTime = initialTime;
145         this.triggeringPolicy = triggeringPolicy;
146         this.rolloverStrategy = rolloverStrategy;
147         this.patternProcessor = new PatternProcessor(pattern);
148         this.patternProcessor.setPrevFileTime(initialTime);
149         this.fileName = fileName;
150         this.fileExtension = FileExtension.lookupForFile(pattern);
151     }
152 
153     public void initialize() {
154 
155         if (!initialized) {
156             LOGGER.debug("Initializing triggering policy {}", triggeringPolicy);
157             initialized = true;
158             triggeringPolicy.initialize(this);
159             if (triggeringPolicy instanceof LifeCycle) {
160                 ((LifeCycle) triggeringPolicy).start();
161             }
162             if (rolloverStrategy instanceof DirectFileRolloverStrategy) {
163                 // LOG4J2-2485: Initialize size from the most recently written file.
164                 File file = new File(getFileName());
165                 if (file.exists()) {
166                     size = file.length();
167                 } else {
168                     ((DirectFileRolloverStrategy) rolloverStrategy).clearCurrentFileName();
169                 }
170             }
171         }
172     }
173 
174     /**
175      * Returns a RollingFileManager.
176      * @param fileName The file name.
177      * @param pattern The pattern for rolling file.
178      * @param append true if the file should be appended to.
179      * @param bufferedIO true if data should be buffered.
180      * @param policy The TriggeringPolicy.
181      * @param strategy The RolloverStrategy.
182      * @param advertiseURI the URI to use when advertising the file
183      * @param layout The Layout.
184      * @param bufferSize buffer size to use if bufferedIO is true
185      * @param immediateFlush flush on every write or not
186      * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
187      * @param filePermissions File permissions
188      * @param fileOwner File owner
189      * @param fileGroup File group
190      * @param configuration The configuration.
191      * @return A RollingFileManager.
192      */
193     public static RollingFileManager getFileManager(final String fileName, final String pattern, final boolean append,
194             final boolean bufferedIO, final TriggeringPolicy policy, final RolloverStrategy strategy,
195             final String advertiseURI, final Layout<? extends Serializable> layout, final int bufferSize,
196             final boolean immediateFlush, final boolean createOnDemand,
197             final String filePermissions, final String fileOwner, final String fileGroup,
198             final Configuration configuration) {
199 
200         if (strategy instanceof DirectWriteRolloverStrategy && fileName != null) {
201             LOGGER.error("The fileName attribute must not be specified with the DirectWriteRolloverStrategy");
202             return null;
203         }
204         final String name = fileName == null ? pattern : fileName;
205         return narrow(RollingFileManager.class, getManager(name, new FactoryData(fileName, pattern, append,
206             bufferedIO, policy, strategy, advertiseURI, layout, bufferSize, immediateFlush, createOnDemand,
207             filePermissions, fileOwner, fileGroup, configuration), factory));
208     }
209 
210     /**
211      * Returns the name of the File being managed.
212      * @return The name of the File being managed.
213      */
214     @Override
215     public String getFileName() {
216         if (rolloverStrategy instanceof DirectFileRolloverStrategy) {
217             fileName = ((DirectFileRolloverStrategy) rolloverStrategy).getCurrentFileName(this);
218         }
219         return fileName;
220     }
221 
222     public FileExtension getFileExtension() {
223         return fileExtension;
224     }
225 
226     // override to make visible for unit tests
227     @Override
228     protected synchronized void write(final byte[] bytes, final int offset, final int length,
229             final boolean immediateFlush) {
230         super.write(bytes, offset, length, immediateFlush);
231     }
232 
233     @Override
234     protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
235         size += length;
236         super.writeToDestination(bytes, offset, length);
237     }
238 
239     public boolean isRenameEmptyFiles() {
240         return renameEmptyFiles;
241     }
242 
243     public void setRenameEmptyFiles(final boolean renameEmptyFiles) {
244         this.renameEmptyFiles = renameEmptyFiles;
245     }
246 
247     /**
248      * Returns the current size of the file.
249      * @return The size of the file in bytes.
250      */
251     public long getFileSize() {
252         return size + byteBuffer.position();
253     }
254 
255     /**
256      * Returns the time the file was created.
257      * @return The time the file was created.
258      */
259     public long getFileTime() {
260         return initialTime;
261     }
262 
263     /**
264      * Determines if a rollover should occur.
265      * @param event The LogEvent.
266      */
267     public synchronized void checkRollover(final LogEvent event) {
268         if (triggeringPolicy.isTriggeringEvent(event)) {
269             rollover();
270         }
271     }
272 
273     @Override
274     public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
275         LOGGER.debug("Shutting down RollingFileManager {}", getName());
276         boolean stopped = true;
277         if (triggeringPolicy instanceof LifeCycle2) {
278             stopped &= ((LifeCycle2) triggeringPolicy).stop(timeout, timeUnit);
279         } else if (triggeringPolicy instanceof LifeCycle) {
280             ((LifeCycle) triggeringPolicy).stop();
281             stopped &= true;
282         }
283         final boolean status = super.releaseSub(timeout, timeUnit) && stopped;
284         asyncExecutor.shutdown();
285         try {
286             // Allow at least the minimum interval to pass so async actions can complete.
287             final long millis = timeUnit.toMillis(timeout);
288             final long waitInterval = MIN_DURATION < millis ? millis : MIN_DURATION;
289 
290             for (int count = 1; count <= MAX_TRIES && !asyncExecutor.isTerminated(); ++count) {
291                 asyncExecutor.awaitTermination(waitInterval * count, TimeUnit.MILLISECONDS);
292             }
293             if (asyncExecutor.isTerminated()) {
294                 LOGGER.debug("All asynchronous threads have terminated");
295             } else {
296                 asyncExecutor.shutdownNow();
297                 try {
298                     asyncExecutor.awaitTermination(timeout, timeUnit);
299                     if (asyncExecutor.isTerminated()) {
300                         LOGGER.debug("All asynchronous threads have terminated");
301                     } else {
302                         LOGGER.debug("RollingFileManager shutting down but some asynchronous services may not have completed");
303                     }
304                 } catch (final InterruptedException inner) {
305                     LOGGER.warn("RollingFileManager stopped but some asynchronous services may not have completed.");
306                 }
307             }
308         } catch (final InterruptedException ie) {
309             asyncExecutor.shutdownNow();
310             try {
311                 asyncExecutor.awaitTermination(timeout, timeUnit);
312                 if (asyncExecutor.isTerminated()) {
313                     LOGGER.debug("All asynchronous threads have terminated");
314                 }
315             } catch (final InterruptedException inner) {
316                 LOGGER.warn("RollingFileManager stopped but some asynchronous services may not have completed.");
317             }
318             // Preserve interrupt status
319             Thread.currentThread().interrupt();
320         }
321         LOGGER.debug("RollingFileManager shutdown completed with status {}", status);
322         return status;
323     }
324 
325     public synchronized void rollover(Date prevFileTime, Date prevRollTime) {
326 		getPatternProcessor().setPrevFileTime(prevFileTime.getTime());
327 		getPatternProcessor().setCurrentFileTime(prevRollTime.getTime());
328 		rollover();
329 	}
330 
331     public synchronized void rollover() {
332         if (!hasOutputStream()) {
333             return;
334         }
335         if (rollover(rolloverStrategy)) {
336             try {
337                 size = 0;
338                 initialTime = System.currentTimeMillis();
339                 createFileAfterRollover();
340             } catch (final IOException e) {
341                 logError("Failed to create file after rollover", e);
342             }
343         }
344     }
345 
346     protected void createFileAfterRollover() throws IOException  {
347         setOutputStream(createOutputStream());
348     }
349 
350     /**
351      * Returns the pattern processor.
352      * @return The PatternProcessor.
353      */
354     public PatternProcessor getPatternProcessor() {
355         return patternProcessor;
356     }
357 
358     public void setTriggeringPolicy(final TriggeringPolicy triggeringPolicy) {
359         triggeringPolicy.initialize(this);
360         final TriggeringPolicy policy = this.triggeringPolicy;
361         int count = 0;
362         boolean policyUpdated = false;
363         do {
364             ++count;
365         } while (!(policyUpdated = triggeringPolicyUpdater.compareAndSet(this, this.triggeringPolicy, triggeringPolicy))
366                 && count < MAX_TRIES);
367         if (policyUpdated) {
368             if (triggeringPolicy instanceof LifeCycle) {
369                 ((LifeCycle) triggeringPolicy).start();
370             }
371             if (policy instanceof LifeCycle) {
372                 ((LifeCycle) policy).stop();
373             }
374         } else {
375             if (triggeringPolicy instanceof LifeCycle) {
376                 ((LifeCycle) triggeringPolicy).stop();
377             }
378         }
379     }
380 
381     public void setRolloverStrategy(final RolloverStrategy rolloverStrategy) {
382         rolloverStrategyUpdater.compareAndSet(this, this.rolloverStrategy, rolloverStrategy);
383     }
384 
385     public void setPatternProcessor(final PatternProcessor patternProcessor) {
386         patternProcessorUpdater.compareAndSet(this, this.patternProcessor, patternProcessor);
387     }
388 
389     /**
390      * Returns the triggering policy.
391      * @param <T> TriggeringPolicy type
392      * @return The TriggeringPolicy
393      */
394     @SuppressWarnings("unchecked")
395     public <T extends TriggeringPolicy> T getTriggeringPolicy() {
396         // TODO We could parameterize this class with a TriggeringPolicy instead of type casting here.
397         return (T) this.triggeringPolicy;
398     }
399 
400     /**
401      * Returns the rollover strategy.
402      * @return The RolloverStrategy
403      */
404     public RolloverStrategy getRolloverStrategy() {
405         return this.rolloverStrategy;
406     }
407 
408     private boolean rollover(final RolloverStrategy strategy) {
409 
410         boolean releaseRequired = false;
411         try {
412             // Block until the asynchronous operation is completed.
413             semaphore.acquire();
414             releaseRequired = true;
415         } catch (final InterruptedException e) {
416             logError("Thread interrupted while attempting to check rollover", e);
417             return false;
418         }
419 
420         boolean success = true;
421 
422         try {
423             final RolloverDescription descriptor = strategy.rollover(this);
424             if (descriptor != null) {
425                 writeFooter();
426                 closeOutputStream();
427                 if (descriptor.getSynchronous() != null) {
428                     LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
429                     try {
430                         success = descriptor.getSynchronous().execute();
431                     } catch (final Exception ex) {
432                         success = false;
433                         logError("Caught error in synchronous task", ex);
434                     }
435                 }
436 
437                 if (success && descriptor.getAsynchronous() != null) {
438                     LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
439                     asyncExecutor.execute(new AsyncAction(descriptor.getAsynchronous(), this));
440                     releaseRequired = false;
441                 }
442                 return true;
443             }
444             return false;
445         } finally {
446             if (releaseRequired) {
447                 semaphore.release();
448             }
449         }
450 
451     }
452 
453     /**
454      * Performs actions asynchronously.
455      */
456     private static class AsyncAction extends AbstractAction {
457 
458         private final Action action;
459         private final RollingFileManager manager;
460 
461         /**
462          * Constructor.
463          * @param act The action to perform.
464          * @param manager The manager.
465          */
466         public AsyncAction(final Action act, final RollingFileManager manager) {
467             this.action = act;
468             this.manager = manager;
469         }
470 
471         /**
472          * Executes an action.
473          *
474          * @return true if action was successful.  A return value of false will cause
475          *         the rollover to be aborted if possible.
476          * @throws java.io.IOException if IO error, a thrown exception will cause the rollover
477          *                             to be aborted if possible.
478          */
479         @Override
480         public boolean execute() throws IOException {
481             try {
482                 return action.execute();
483             } finally {
484                 manager.semaphore.release();
485             }
486         }
487 
488         /**
489          * Cancels the action if not already initialized or waits till completion.
490          */
491         @Override
492         public void close() {
493             action.close();
494         }
495 
496         /**
497          * Determines if action has been completed.
498          *
499          * @return true if action is complete.
500          */
501         @Override
502         public boolean isComplete() {
503             return action.isComplete();
504         }
505 
506         @Override
507         public String toString() {
508             final StringBuilder builder = new StringBuilder();
509             builder.append(super.toString());
510             builder.append("[action=");
511             builder.append(action);
512             builder.append(", manager=");
513             builder.append(manager);
514             builder.append(", isComplete()=");
515             builder.append(isComplete());
516             builder.append(", isInterrupted()=");
517             builder.append(isInterrupted());
518             builder.append("]");
519             return builder.toString();
520         }
521     }
522 
523     /**
524      * Factory data.
525      */
526     private static class FactoryData extends ConfigurationFactoryData {
527         private final String fileName;
528         private final String pattern;
529         private final boolean append;
530         private final boolean bufferedIO;
531         private final int bufferSize;
532         private final boolean immediateFlush;
533         private final boolean createOnDemand;
534         private final TriggeringPolicy policy;
535         private final RolloverStrategy strategy;
536         private final String advertiseURI;
537         private final Layout<? extends Serializable> layout;
538         private final String filePermissions;
539         private final String fileOwner;
540         private final String fileGroup;
541 
542         /**
543          * Creates the data for the factory.
544          * @param pattern The pattern.
545          * @param append The append flag.
546          * @param bufferedIO The bufferedIO flag.
547          * @param advertiseURI
548          * @param layout The Layout.
549          * @param bufferSize the buffer size
550          * @param immediateFlush flush on every write or not
551          * @param createOnDemand true if you want to lazy-create the file (a.k.a. on-demand.)
552          * @param filePermissions File permissions
553          * @param fileOwner File owner
554          * @param fileGroup File group
555          * @param configuration The configuration
556          */
557         public FactoryData(final String fileName, final String pattern, final boolean append, final boolean bufferedIO,
558                 final TriggeringPolicy policy, final RolloverStrategy strategy, final String advertiseURI,
559                 final Layout<? extends Serializable> layout, final int bufferSize, final boolean immediateFlush,
560                 final boolean createOnDemand, final String filePermissions, final String fileOwner, final String fileGroup,
561                 final Configuration configuration) {
562             super(configuration);
563             this.fileName = fileName;
564             this.pattern = pattern;
565             this.append = append;
566             this.bufferedIO = bufferedIO;
567             this.bufferSize = bufferSize;
568             this.policy = policy;
569             this.strategy = strategy;
570             this.advertiseURI = advertiseURI;
571             this.layout = layout;
572             this.immediateFlush = immediateFlush;
573             this.createOnDemand = createOnDemand;
574             this.filePermissions = filePermissions;
575             this.fileOwner = fileOwner;
576             this.fileGroup = fileGroup;
577         }
578 
579         public TriggeringPolicy getTriggeringPolicy() {
580             return this.policy;
581         }
582 
583         public RolloverStrategy getRolloverStrategy() {
584             return this.strategy;
585         }
586 
587         public String getPattern() {
588             return pattern;
589         }
590 
591         @Override
592         public String toString() {
593             final StringBuilder builder = new StringBuilder();
594             builder.append(super.toString());
595             builder.append("[pattern=");
596             builder.append(pattern);
597             builder.append(", append=");
598             builder.append(append);
599             builder.append(", bufferedIO=");
600             builder.append(bufferedIO);
601             builder.append(", bufferSize=");
602             builder.append(bufferSize);
603             builder.append(", policy=");
604             builder.append(policy);
605             builder.append(", strategy=");
606             builder.append(strategy);
607             builder.append(", advertiseURI=");
608             builder.append(advertiseURI);
609             builder.append(", layout=");
610             builder.append(layout);
611             builder.append(", filePermissions=");
612             builder.append(filePermissions);
613             builder.append(", fileOwner=");
614             builder.append(fileOwner);
615             builder.append("]");
616             return builder.toString();
617         }
618     }
619 
620     @Override
621     public void updateData(final Object data) {
622         final FactoryData factoryData = (FactoryData) data;
623         setRolloverStrategy(factoryData.getRolloverStrategy());
624         setTriggeringPolicy(factoryData.getTriggeringPolicy());
625         setPatternProcessor(new PatternProcessor(factoryData.getPattern(), getPatternProcessor()));
626     }
627 
628     /**
629      * Factory to create a RollingFileManager.
630      */
631     private static class RollingFileManagerFactory implements ManagerFactory<RollingFileManager, FactoryData> {
632 
633         /**
634          * Creates a RollingFileManager.
635          * @param name The name of the entity to manage.
636          * @param data The data required to create the entity.
637          * @return a RollingFileManager.
638          */
639         @Override
640         public RollingFileManager createManager(final String name, final FactoryData data) {
641             long size = 0;
642             boolean writeHeader = !data.append;
643             File file = null;
644             if (data.fileName != null) {
645                 file = new File(data.fileName);
646                 // LOG4J2-1140: check writeHeader before creating the file
647                 writeHeader = !data.append || !file.exists();
648 
649                 try {
650                     FileUtils.makeParentDirs(file);
651                     final boolean created = data.createOnDemand ? false : file.createNewFile();
652                     LOGGER.trace("New file '{}' created = {}", name, created);
653                 } catch (final IOException ioe) {
654                     LOGGER.error("Unable to create file " + name, ioe);
655                     return null;
656                 }
657                 size = data.append ? file.length() : 0;
658             }
659 
660             try {
661                 final int actualSize = data.bufferedIO ? data.bufferSize : Constants.ENCODER_BYTE_BUFFER_SIZE;
662                 final ByteBuffer buffer = ByteBuffer.wrap(new byte[actualSize]);
663                 final OutputStream os = data.createOnDemand  || data.fileName == null ? null :
664                         new FileOutputStream(data.fileName, data.append);
665                 final long initialTime = data.createOnDemand || file == null ?
666                         0 : initialFileTime(file); // LOG4J2-531 create file first so time has valid value
667 
668                 final RollingFileManager rm = new RollingFileManager(data.getLoggerContext(), data.fileName, data.pattern, os,
669                     data.append, data.createOnDemand, size, initialTime, data.policy, data.strategy, data.advertiseURI,
670                     data.layout, data.filePermissions, data.fileOwner, data.fileGroup, writeHeader, buffer);
671                 if (os != null && rm.isAttributeViewEnabled()) {
672                     rm.defineAttributeView(file.toPath());
673                 }
674 
675                 return rm;
676             } catch (final IOException ex) {
677                 LOGGER.error("RollingFileManager (" + name + ") " + ex, ex);
678             }
679             return null;
680         }
681     }
682 
683     private static long initialFileTime(final File file) {
684         final Path path = file.toPath();
685         if (Files.exists(path)) {
686             try {
687                 final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
688                 final FileTime fileTime = attrs.creationTime();
689                 if (fileTime.compareTo(EPOCH) > 0) {
690                     LOGGER.debug("Returning file creation time for {}", file.getAbsolutePath());
691                     return fileTime.toMillis();
692                 }
693                 LOGGER.info("Unable to obtain file creation time for " + file.getAbsolutePath());
694             } catch (final Exception ex) {
695                 LOGGER.info("Unable to calculate file creation time for " + file.getAbsolutePath() + ": " + ex.getMessage());
696             }
697         }
698         return file.lastModified();
699     }
700 
701     private static class EmptyQueue extends ArrayBlockingQueue<Runnable> {
702 
703         /**
704          *
705          */
706         private static final long serialVersionUID = 1L;
707 
708         EmptyQueue() {
709             super(1);
710         }
711 
712         @Override
713         public int remainingCapacity() {
714             return 0;
715         }
716 
717         @Override
718         public boolean add(final Runnable runnable) {
719             throw new IllegalStateException("Queue is full");
720         }
721 
722         @Override
723         public void put(final Runnable runnable) throws InterruptedException {
724             /* No point in going into a permanent wait */
725             throw new InterruptedException("Unable to insert into queue");
726         }
727 
728         @Override
729         public boolean offer(final Runnable runnable, final long timeout, final TimeUnit timeUnit) throws InterruptedException {
730             Thread.sleep(timeUnit.toMillis(timeout));
731             return false;
732         }
733 
734         @Override
735         public boolean addAll(final Collection<? extends Runnable> collection) {
736             if (collection.size() > 0) {
737                 throw new IllegalArgumentException("Too many items in collection");
738             }
739             return false;
740         }
741 
742     }
743 
744 }