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