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 org.apache.logging.log4j.Logger;
20 import org.apache.logging.log4j.core.appender.rolling.helper.Action;
21 import org.apache.logging.log4j.core.appender.rolling.helper.FileRenameAction;
22 import org.apache.logging.log4j.core.appender.rolling.helper.GZCompressAction;
23 import org.apache.logging.log4j.core.appender.rolling.helper.ZipCompressAction;
24 import org.apache.logging.log4j.core.config.Configuration;
25 import org.apache.logging.log4j.core.config.plugins.Plugin;
26 import org.apache.logging.log4j.core.config.plugins.PluginAttr;
27 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
28 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
29 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
30 import org.apache.logging.log4j.status.StatusLogger;
31
32 import java.io.File;
33 import java.util.ArrayList;
34 import java.util.List;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 @Plugin(name = "DefaultRolloverStrategy", type = "Core", printObject = true)
67 public class DefaultRolloverStrategy implements RolloverStrategy {
68
69
70
71 protected static final Logger LOGGER = StatusLogger.getLogger();
72
73 private static final int MIN_WINDOW_SIZE = 1;
74 private static final int DEFAULT_WINDOW_SIZE = 7;
75
76
77
78
79 private final int maxIndex;
80
81
82
83
84 private final int minIndex;
85
86 private final boolean useMax;
87
88 private final StrSubstitutor subst;
89
90
91
92
93
94
95 protected DefaultRolloverStrategy(final int min, final int max, final boolean useMax, final StrSubstitutor subst) {
96 minIndex = min;
97 maxIndex = max;
98 this.subst = subst;
99 this.useMax = useMax;
100 }
101
102
103
104
105
106
107
108 public RolloverDescription rollover(final RollingFileManager manager) throws SecurityException {
109 if (maxIndex >= 0) {
110 int fileIndex;
111
112 if ((fileIndex = purge(minIndex, maxIndex, manager)) < 0) {
113 return null;
114 }
115
116 final StringBuilder buf = new StringBuilder();
117 manager.getProcessor().formatFileName(buf, fileIndex);
118 final String currentFileName = manager.getFileName();
119
120 String renameTo = subst.replace(buf);
121 final String compressedName = renameTo;
122 Action compressAction = null;
123
124 if (renameTo.endsWith(".gz")) {
125 renameTo = renameTo.substring(0, renameTo.length() - 3);
126 compressAction = new GZCompressAction(new File(renameTo), new File(compressedName), true);
127 } else if (renameTo.endsWith(".zip")) {
128 renameTo = renameTo.substring(0, renameTo.length() - 4);
129 compressAction = new ZipCompressAction(new File(renameTo), new File(compressedName), true);
130 }
131
132 final FileRenameAction renameAction =
133 new FileRenameAction(new File(currentFileName), new File(renameTo), false);
134
135 return new RolloverDescriptionImpl(currentFileName, false, renameAction, compressAction);
136 }
137
138 return null;
139 }
140
141 private int purge(final int lowIndex, final int highIndex, final RollingFileManager manager) {
142 return useMax ? purgeAscending(lowIndex, highIndex, manager) :
143 purgeDescending(lowIndex, highIndex, manager);
144 }
145
146
147
148
149
150
151
152
153
154
155 private int purgeDescending(final int lowIndex, final int highIndex, final RollingFileManager manager) {
156 int suffixLength = 0;
157
158 final List<FileRenameAction> renames = new ArrayList<FileRenameAction>();
159 final StringBuilder buf = new StringBuilder();
160 manager.getProcessor().formatFileName(buf, lowIndex);
161
162 String lowFilename = subst.replace(buf);
163
164 if (lowFilename.endsWith(".gz")) {
165 suffixLength = 3;
166 } else if (lowFilename.endsWith(".zip")) {
167 suffixLength = 4;
168 }
169
170 for (int i = lowIndex; i <= highIndex; i++) {
171 File toRename = new File(lowFilename);
172 boolean isBase = false;
173
174 if (suffixLength > 0) {
175 final File toRenameBase =
176 new File(lowFilename.substring(0, lowFilename.length() - suffixLength));
177
178 if (toRename.exists()) {
179 if (toRenameBase.exists()) {
180 toRenameBase.delete();
181 }
182 } else {
183 toRename = toRenameBase;
184 isBase = true;
185 }
186 }
187
188 if (toRename.exists()) {
189
190
191
192
193 if (i == highIndex) {
194 if (!toRename.delete()) {
195 return -1;
196 }
197
198 break;
199 }
200
201
202
203
204 buf.setLength(0);
205 manager.getProcessor().formatFileName(buf, i + 1);
206
207 final String highFilename = subst.replace(buf);
208 String renameTo = highFilename;
209
210 if (isBase) {
211 renameTo = highFilename.substring(0, highFilename.length() - suffixLength);
212 }
213
214 renames.add(new FileRenameAction(toRename, new File(renameTo), true));
215 lowFilename = highFilename;
216 } else {
217 break;
218 }
219 }
220
221
222
223
224 for (int i = renames.size() - 1; i >= 0; i--) {
225 final Action action = renames.get(i);
226
227 try {
228 if (!action.execute()) {
229 return -1;
230 }
231 } catch (final Exception ex) {
232 LOGGER.warn("Exception during purge in RollingFileAppender", ex);
233 return -1;
234 }
235 }
236
237 return lowIndex;
238 }
239
240
241
242
243
244
245
246
247
248
249 private int purgeAscending(final int lowIndex, final int highIndex, final RollingFileManager manager) {
250 int suffixLength = 0;
251
252 final List<FileRenameAction> renames = new ArrayList<FileRenameAction>();
253 final StringBuilder buf = new StringBuilder();
254 manager.getProcessor().formatFileName(buf, highIndex);
255
256 String highFilename = subst.replace(buf);
257
258 if (highFilename.endsWith(".gz")) {
259 suffixLength = 3;
260 } else if (highFilename.endsWith(".zip")) {
261 suffixLength = 4;
262 }
263
264 int maxIndex = 0;
265
266 for (int i = highIndex; i >= lowIndex; i--) {
267 File toRename = new File(highFilename);
268 if (i == highIndex && toRename.exists()) {
269 maxIndex = highIndex;
270 } else if (maxIndex == 0 && toRename.exists()) {
271 maxIndex = i + 1;
272 break;
273 }
274
275 boolean isBase = false;
276
277 if (suffixLength > 0) {
278 final File toRenameBase =
279 new File(highFilename.substring(0, highFilename.length() - suffixLength));
280
281 if (toRename.exists()) {
282 if (toRenameBase.exists()) {
283 toRenameBase.delete();
284 }
285 } else {
286 toRename = toRenameBase;
287 isBase = true;
288 }
289 }
290
291 if (toRename.exists()) {
292
293
294
295
296 if (i == lowIndex) {
297 if (!toRename.delete()) {
298 return -1;
299 }
300
301 break;
302 }
303
304
305
306
307 buf.setLength(0);
308 manager.getProcessor().formatFileName(buf, i - 1);
309
310 final String lowFilename = subst.replace(buf);
311 String renameTo = lowFilename;
312
313 if (isBase) {
314 renameTo = lowFilename.substring(0, lowFilename.length() - suffixLength);
315 }
316
317 renames.add(new FileRenameAction(toRename, new File(renameTo), true));
318 highFilename = lowFilename;
319 } else {
320 buf.setLength(0);
321 manager.getProcessor().formatFileName(buf, i - 1);
322
323 highFilename = subst.replace(buf);
324 }
325 }
326 if (maxIndex == 0) {
327 maxIndex = lowIndex;
328 }
329
330
331
332
333 for (int i = renames.size() - 1; i >= 0; i--) {
334 final Action action = renames.get(i);
335
336 try {
337 if (!action.execute()) {
338 return -1;
339 }
340 } catch (final Exception ex) {
341 LOGGER.warn("Exception during purge in RollingFileAppender", ex);
342 return -1;
343 }
344 }
345 return maxIndex;
346 }
347
348 @Override
349 public String toString() {
350 return "DefaultRolloverStrategy(min=" + minIndex + ", max=" + maxIndex + ")";
351 }
352
353
354
355
356
357
358
359
360
361
362 @PluginFactory
363 public static DefaultRolloverStrategy createStrategy(@PluginAttr("max") final String max,
364 @PluginAttr("min") final String min,
365 @PluginAttr("fileIndex") final String fileIndex,
366 @PluginConfiguration final Configuration config) {
367 final boolean useMax = fileIndex == null ? true : fileIndex.equalsIgnoreCase("max");
368 int minIndex;
369 if (min != null) {
370 minIndex = Integer.parseInt(min);
371 if (minIndex < 1) {
372 LOGGER.error("Minimum window size too small. Limited to " + MIN_WINDOW_SIZE);
373 minIndex = MIN_WINDOW_SIZE;
374 }
375 } else {
376 minIndex = MIN_WINDOW_SIZE;
377 }
378 int maxIndex;
379 if (max != null) {
380 maxIndex = Integer.parseInt(max);
381 if (maxIndex < minIndex) {
382 maxIndex = minIndex < DEFAULT_WINDOW_SIZE ? DEFAULT_WINDOW_SIZE : minIndex;
383 LOGGER.error("Maximum window size must be greater than the minimum windows size. Set to " + maxIndex);
384 }
385 } else {
386 maxIndex = DEFAULT_WINDOW_SIZE;
387 }
388 return new DefaultRolloverStrategy(minIndex, maxIndex, useMax, config.getSubst());
389 }
390
391 }