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.IOException;
21  import java.nio.file.DirectoryStream;
22  import java.nio.file.Files;
23  import java.nio.file.Path;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.SortedMap;
27  import java.util.TreeMap;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  import org.apache.logging.log4j.Logger;
32  import org.apache.logging.log4j.LoggingException;
33  import org.apache.logging.log4j.core.appender.rolling.action.Action;
34  import org.apache.logging.log4j.core.appender.rolling.action.CompositeAction;
35  import org.apache.logging.log4j.core.lookup.StrSubstitutor;
36  import org.apache.logging.log4j.core.pattern.NotANumber;
37  import org.apache.logging.log4j.status.StatusLogger;
38  
39  /**
40   *
41   */
42  public abstract class AbstractRolloverStrategy implements RolloverStrategy {
43  
44      /**
45       * Allow subclasses access to the status logger without creating another instance.
46       */
47      protected static final Logger LOGGER = StatusLogger.getLogger();
48  
49      public static final Pattern PATTERN_COUNTER= Pattern.compile(".*%((?<ZEROPAD>0)?(?<PADDING>\\d+))?i.*");
50  
51      protected final StrSubstitutor strSubstitutor;
52  
53      protected AbstractRolloverStrategy(final StrSubstitutor strSubstitutor) {
54          this.strSubstitutor = strSubstitutor;
55      }
56  
57  
58      public StrSubstitutor getStrSubstitutor() {
59          return strSubstitutor;
60      }
61  
62      protected Action merge(final Action compressAction, final List<Action> custom, final boolean stopOnError) {
63          if (custom.isEmpty()) {
64              return compressAction;
65          }
66          if (compressAction == null) {
67              return new CompositeAction(custom, stopOnError);
68          }
69          final List<Action> all = new ArrayList<>();
70          all.add(compressAction);
71          all.addAll(custom);
72          return new CompositeAction(all, stopOnError);
73      }
74  
75      protected int suffixLength(final String lowFilename) {
76          for (final FileExtension extension : FileExtension.values()) {
77              if (extension.isExtensionFor(lowFilename)) {
78                  return extension.length();
79              }
80          }
81          return 0;
82      }
83  
84  
85      protected SortedMap<Integer, Path> getEligibleFiles(final RollingFileManager manager) {
86          return getEligibleFiles(manager, true);
87      }
88  
89      protected SortedMap<Integer, Path> getEligibleFiles(final RollingFileManager manager,
90                                                          final boolean isAscending) {
91          final StringBuilder buf = new StringBuilder();
92          final String pattern = manager.getPatternProcessor().getPattern();
93          manager.getPatternProcessor().formatFileName(strSubstitutor, buf, NotANumber.NAN);
94          return getEligibleFiles(buf.toString(), pattern, isAscending);
95      }
96  
97      protected SortedMap<Integer, Path> getEligibleFiles(final String path, final String pattern) {
98          return getEligibleFiles(path, pattern, true);
99      }
100 
101     protected SortedMap<Integer, Path> getEligibleFiles(final String path, final String logfilePattern, final boolean isAscending) {
102         final TreeMap<Integer, Path> eligibleFiles = new TreeMap<>();
103         final File file = new File(path);
104         File parent = file.getParentFile();
105         if (parent == null) {
106             parent = new File(".");
107         } else {
108             parent.mkdirs();
109         }
110         if (!PATTERN_COUNTER.matcher(logfilePattern).matches()) {
111             return eligibleFiles;
112         }
113         final Path dir = parent.toPath();
114         String fileName = file.getName();
115         final int suffixLength = suffixLength(fileName);
116         if (suffixLength > 0) {
117             fileName = fileName.substring(0, fileName.length() - suffixLength) + ".*";
118         }
119         final String filePattern = fileName.replace(NotANumber.VALUE, "(\\d+)");
120         final Pattern pattern = Pattern.compile(filePattern);
121 
122         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
123             for (final Path entry: stream) {
124                 final Matcher matcher = pattern.matcher(entry.toFile().getName());
125                 if (matcher.matches()) {
126                     final Integer index = Integer.parseInt(matcher.group(1));
127                     eligibleFiles.put(index, entry);
128                 }
129             }
130         } catch (final IOException ioe) {
131             throw new LoggingException("Error reading folder " + dir + " " + ioe.getMessage(), ioe);
132         }
133         return isAscending? eligibleFiles : eligibleFiles.descendingMap();
134     }
135 }