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 */ 017 018package org.apache.logging.log4j.core.appender.rolling.action; 019 020import java.io.IOException; 021import java.nio.file.FileVisitResult; 022import java.nio.file.Files; 023import java.nio.file.NoSuchFileException; 024import java.nio.file.Path; 025import java.nio.file.SimpleFileVisitor; 026import java.nio.file.attribute.BasicFileAttributes; 027import java.util.List; 028import java.util.Objects; 029 030import org.apache.logging.log4j.Logger; 031import org.apache.logging.log4j.status.StatusLogger; 032 033/** 034 * FileVisitor that deletes files that are accepted by all PathFilters. Directories are ignored. 035 */ 036public class DeletingVisitor extends SimpleFileVisitor<Path> { 037 private static final Logger LOGGER = StatusLogger.getLogger(); 038 039 private final Path basePath; 040 private final boolean testMode; 041 private final List<? extends PathCondition> pathConditions; 042 043 /** 044 * Constructs a new DeletingVisitor. 045 * 046 * @param basePath used to relativize paths 047 * @param pathConditions objects that need to confirm whether a file can be deleted 048 * @param testMode if true, files are not deleted but instead a message is printed to the <a 049 * href="http://logging.apache.org/log4j/2.x/manual/configuration.html#StatusMessages">status logger</a> 050 * at INFO level. Users can use this to do a dry run to test if their configuration works as expected. 051 */ 052 public DeletingVisitor(final Path basePath, final List<? extends PathCondition> pathConditions, 053 final boolean testMode) { 054 this.testMode = testMode; 055 this.basePath = Objects.requireNonNull(basePath, "basePath"); 056 this.pathConditions = Objects.requireNonNull(pathConditions, "pathConditions"); 057 for (final PathCondition condition : pathConditions) { 058 condition.beforeFileTreeWalk(); 059 } 060 } 061 062 @Override 063 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { 064 for (final PathCondition pathFilter : pathConditions) { 065 final Path relative = basePath.relativize(file); 066 if (!pathFilter.accept(basePath, relative, attrs)) { 067 LOGGER.trace("Not deleting base={}, relative={}", basePath, relative); 068 return FileVisitResult.CONTINUE; 069 } 070 } 071 if (isTestMode()) { 072 LOGGER.info("Deleting {} (TEST MODE: file not actually deleted)", file); 073 } else { 074 delete(file); 075 } 076 return FileVisitResult.CONTINUE; 077 } 078 079 @Override 080 public FileVisitResult visitFileFailed(Path file, IOException ioException) throws IOException { 081 // LOG4J2-2677: Appenders may rollover and purge in parallel. SimpleVisitor rethrows exceptions from 082 // failed attempts to load file attributes. 083 if (ioException instanceof NoSuchFileException) { 084 LOGGER.info("File {} could not be accessed, it has likely already been deleted", file, ioException); 085 return FileVisitResult.CONTINUE; 086 } else { 087 return super.visitFileFailed(file, ioException); 088 } 089 } 090 091 /** 092 * Deletes the specified file. 093 * 094 * @param file the file to delete 095 * @throws IOException if a problem occurred deleting the file 096 */ 097 protected void delete(final Path file) throws IOException { 098 LOGGER.trace("Deleting {}", file); 099 Files.deleteIfExists(file); 100 } 101 102 /** 103 * Returns {@code true} if files are not deleted even when all conditions accept a path, {@code false} otherwise. 104 * 105 * @return {@code true} if files are not deleted even when all conditions accept a path, {@code false} otherwise 106 */ 107 public boolean isTestMode() { 108 return testMode; 109 } 110}