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