19
19
20
20
package org .jboss .logmanager .ext .handlers ;
21
21
22
+ import org .jboss .logmanager .ExtLogRecord ;
23
+ import org .jboss .logmanager .handlers .FileHandler ;
24
+
22
25
import java .io .File ;
23
26
import java .io .FileNotFoundException ;
24
27
import java .io .IOException ;
28
+ import java .nio .file .Files ;
29
+ import java .nio .file .Path ;
25
30
import java .text .SimpleDateFormat ;
26
- import java .util .Calendar ;
27
- import java .util .Date ;
28
- import java .util .TimeZone ;
31
+ import java .util .*;
29
32
import java .util .logging .ErrorManager ;
30
-
31
- import org . jboss . logmanager . ExtLogRecord ;
32
- import org . jboss . logmanager . handlers . FileHandler ;
33
+ import java . util . regex . Pattern ;
34
+ import java . util . stream . Collectors ;
35
+ import java . util . stream . IntStream ;
33
36
34
37
/**
35
38
* A file handler which rotates the log at a preset time interval. The interval is determined by the content of the
@@ -44,6 +47,13 @@ public class PeriodicRotatingFileHandler extends FileHandler {
44
47
private TimeZone timeZone = TimeZone .getDefault ();
45
48
private SuffixRotator suffixRotator = SuffixRotator .EMPTY ;
46
49
50
+ private enum PruningStrategy { NONE , PERIODS , SIZE };
51
+
52
+ private String pruneSize ;
53
+ private long pruneSizeResolved = 0L ;
54
+ private PruningStrategy pruningStrategy = PruningStrategy .NONE ;
55
+
56
+
47
57
/**
48
58
* Construct a new instance with no formatter and no output file.
49
59
*/
@@ -198,9 +208,72 @@ private void rollOver() {
198
208
suffixRotator .rotate (getErrorManager (), file .toPath (), nextSuffix );
199
209
// start new file
200
210
setFile (file );
211
+
212
+ if (pruneSizeResolved > 0 ) {
213
+ pruneFiles (file );
214
+ }
215
+
201
216
} catch (IOException e ) {
202
217
reportError ("Unable to rotate log file" , e , ErrorManager .OPEN_FAILURE );
203
218
}
219
+
220
+ }
221
+
222
+ private void pruneFiles (File baseFile ) throws IOException {
223
+
224
+ synchronized (outputLock ) {
225
+
226
+ String baseName = baseFile .getName ();
227
+ Path parent = baseFile .toPath ().getParent ();
228
+ String suffixPattern = this .getSuffixRotator ().getDatePattern ();
229
+
230
+ Pattern namePattern = Pattern .compile ("^" + Pattern .quote (baseName ) + ".*$" );
231
+
232
+ /*
233
+ * Pruneable files are files in the current file's directory that are:
234
+ *
235
+ * a) regular (not symlinks, dirs, or hidden)
236
+ * b) not the current log
237
+ * c) match the current file syntax
238
+ * d) at a max depth of 1 (files in nested dirs aren't considered)
239
+ */
240
+ List <File > pruneables = Files
241
+ .find (parent , 1 , (p , a ) ->
242
+ a .isRegularFile () &&
243
+ !p .getFileName ().toString ().equals (baseName ) &&
244
+ namePattern .matcher (p .getFileName ().toString ()).matches ()
245
+ )
246
+ .map (Path ::toFile )
247
+ .filter (File ::canWrite )
248
+ .sorted (Comparator .comparingLong (File ::lastModified ).thenComparing (File ::getName ))
249
+ .collect (Collectors .toList ());
250
+
251
+ if (pruningStrategy == PruningStrategy .SIZE ) {
252
+ long total = pruneables .stream ().mapToLong (File ::length ).sum ();
253
+ if (total > pruneSizeResolved ) {
254
+ for (int i = 0 , size = pruneables .size (); i < size && total > pruneSizeResolved ; i ++) {
255
+ long fsize = pruneables .get (i ).length ();
256
+ try {
257
+ Files .deleteIfExists (pruneables .get (i ).toPath ());
258
+ } catch (IOException ex ) {
259
+ reportError ("Unable to prune log file" , ex , ErrorManager .GENERIC_FAILURE );
260
+ }
261
+ total -= fsize ;
262
+ }
263
+ }
264
+ } else if (pruningStrategy == PruningStrategy .PERIODS ) {
265
+ int periods = Long .valueOf (pruneSizeResolved ).intValue ();
266
+ if (pruneables .size () > periods ) {
267
+ for (int i = 0 ; i < periods ; i ++) {
268
+ try {
269
+ Files .deleteIfExists (pruneables .get (i ).toPath ());
270
+ } catch (IOException ex ) {
271
+ reportError ("Unable to prune log file" , ex , ErrorManager .GENERIC_FAILURE );
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
204
277
}
205
278
206
279
private void calcNextRollover (final long fromTime ) {
@@ -293,6 +366,28 @@ public void setTimeZone(final TimeZone timeZone) {
293
366
this .timeZone = timeZone ;
294
367
}
295
368
369
+ public String getPruneSize () {
370
+ return pruneSize ;
371
+ }
372
+
373
+ public void setPruneSize (String pruneSize ) {
374
+ this .pruneSize = pruneSize ;
375
+ try {
376
+ if (pruneSize .endsWith ("p" ) || pruneSize .endsWith ("P" )) {
377
+ pruningStrategy = PruningStrategy .PERIODS ;
378
+ pruneSizeResolved = Long .parseLong (pruneSize .substring (0 , pruneSize .length () - 1 ));
379
+ } else {
380
+ pruningStrategy = PruningStrategy .SIZE ;
381
+ pruneSizeResolved = Long .parseLong (pruneSize );
382
+ }
383
+ } catch (NumberFormatException e ) {
384
+ reportError ("Unable to parse prune size" , e , ErrorManager .FORMAT_FAILURE );
385
+ }
386
+ if (pruneSizeResolved < 0 ) {
387
+ reportError ("Prune size must be positive" , null , ErrorManager .GENERIC_FAILURE );
388
+ }
389
+ }
390
+
296
391
private static <T extends Comparable <? super T >> T min (T a , T b ) {
297
392
return a .compareTo (b ) <= 0 ? a : b ;
298
393
}
0 commit comments