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