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