001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.appender.rolling; 018 019import java.io.File; 020import java.io.FileOutputStream; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.Serializable; 024import java.nio.ByteBuffer; 025import java.nio.file.Files; 026import java.nio.file.Path; 027import java.nio.file.attribute.BasicFileAttributes; 028import java.nio.file.attribute.FileTime; 029import java.util.Collection; 030import java.util.Date; 031import java.util.concurrent.ArrayBlockingQueue; 032import java.util.concurrent.ExecutorService; 033import java.util.concurrent.Semaphore; 034import java.util.concurrent.ThreadPoolExecutor; 035import java.util.concurrent.TimeUnit; 036import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; 037 038import org.apache.logging.log4j.core.Layout; 039import org.apache.logging.log4j.core.LifeCycle; 040import org.apache.logging.log4j.core.LifeCycle2; 041import org.apache.logging.log4j.core.LogEvent; 042import org.apache.logging.log4j.core.LoggerContext; 043import org.apache.logging.log4j.core.appender.ConfigurationFactoryData; 044import org.apache.logging.log4j.core.appender.FileManager; 045import org.apache.logging.log4j.core.appender.ManagerFactory; 046import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction; 047import org.apache.logging.log4j.core.appender.rolling.action.Action; 048import org.apache.logging.log4j.core.config.Configuration; 049import org.apache.logging.log4j.core.util.Constants; 050import org.apache.logging.log4j.core.util.FileUtils; 051import org.apache.logging.log4j.core.util.Log4jThreadFactory; 052 053/** 054 * The Rolling File Manager. 055 */ 056public class RollingFileManager extends FileManager { 057 058 private static RollingFileManagerFactory factory = new RollingFileManagerFactory(); 059 private static final int MAX_TRIES = 3; 060 private static final int MIN_DURATION = 100; 061 private static final FileTime EPOCH = FileTime.fromMillis(0); 062 063 protected long size; 064 private long initialTime; 065 private volatile PatternProcessor patternProcessor; 066 private final Semaphore semaphore = new Semaphore(1); 067 private final Log4jThreadFactory threadFactory = Log4jThreadFactory.createThreadFactory("RollingFileManager"); 068 private volatile TriggeringPolicy triggeringPolicy; 069 private volatile RolloverStrategy rolloverStrategy; 070 private volatile boolean renameEmptyFiles = false; 071 private volatile boolean initialized = false; 072 private volatile String fileName; 073 private final FileExtension fileExtension; 074 075 /* This executor pool will create a new Thread for every work async action to be performed. Using it allows 076 us to make sure all the Threads are completed when the Manager is stopped. */ 077 private final ExecutorService asyncExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS, 078 new EmptyQueue(), threadFactory); 079 080 private static final AtomicReferenceFieldUpdater<RollingFileManager, TriggeringPolicy> triggeringPolicyUpdater = 081 AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, TriggeringPolicy.class, "triggeringPolicy"); 082 083 private static final AtomicReferenceFieldUpdater<RollingFileManager, RolloverStrategy> rolloverStrategyUpdater = 084 AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, RolloverStrategy.class, "rolloverStrategy"); 085 086 private static final AtomicReferenceFieldUpdater<RollingFileManager, PatternProcessor> patternProcessorUpdater = 087 AtomicReferenceFieldUpdater.newUpdater(RollingFileManager.class, PatternProcessor.class, "patternProcessor"); 088 089 @Deprecated 090 protected RollingFileManager(final String fileName, final String pattern, final OutputStream os, 091 final boolean append, final long size, final long initialTime, final TriggeringPolicy triggeringPolicy, 092 final RolloverStrategy rolloverStrategy, final String advertiseURI, 093 final Layout<? extends Serializable> layout, final int bufferSize, final boolean writeHeader) { 094 this(fileName, pattern, os, append, size, initialTime, triggeringPolicy, rolloverStrategy, advertiseURI, layout, 095 writeHeader, ByteBuffer.wrap(new byte[Constants.ENCODER_BYTE_BUFFER_SIZE])); 096 } 097 098 @Deprecated 099 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}