1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.rolling.helper;
15
16 import java.io.File;
17 import java.util.Date;
18
19 import ch.qos.logback.core.CoreConstants;
20 import ch.qos.logback.core.pattern.Converter;
21 import ch.qos.logback.core.pattern.LiteralConverter;
22 import ch.qos.logback.core.spi.ContextAwareBase;
23
24 abstract public class DefaultArchiveRemover extends ContextAwareBase implements
25 ArchiveRemover {
26
27 static protected final long UNINITIALIZED = -1;
28
29 static protected final long INACTIVITY_TOLERANCE_IN_MILLIS = 64L * (long) CoreConstants.MILLIS_IN_ONE_DAY;
30 static final int MAX_VALUE_FOR_INACTIVITY_PERIODS = 14 * 24;
31
32 final FileNamePattern fileNamePattern;
33 final RollingCalendar rc;
34 int periodOffsetForDeletionTarget;
35 final boolean parentClean;
36 long lastHeartBeat = UNINITIALIZED;
37
38 public DefaultArchiveRemover(FileNamePattern fileNamePattern,
39 RollingCalendar rc) {
40 this.fileNamePattern = fileNamePattern;
41 this.rc = rc;
42 this.parentClean = computeParentCleaningFlag(fileNamePattern);
43 }
44
45
46 int computeElapsedPeriodsSinceLastClean(long nowInMillis) {
47 long periodsElapsed = 0;
48 if (lastHeartBeat == UNINITIALIZED) {
49 addInfo("first clean up after appender initialization");
50 periodsElapsed = rc.periodsElapsed(nowInMillis, nowInMillis + INACTIVITY_TOLERANCE_IN_MILLIS);
51 if (periodsElapsed > MAX_VALUE_FOR_INACTIVITY_PERIODS)
52 periodsElapsed = MAX_VALUE_FOR_INACTIVITY_PERIODS;
53 } else {
54 periodsElapsed = rc.periodsElapsed(lastHeartBeat, nowInMillis);
55 if (periodsElapsed < 1) {
56 addWarn("Unexpected periodsElapsed value " + periodsElapsed);
57 periodsElapsed = 1;
58 }
59 }
60 return (int) periodsElapsed;
61 }
62
63 public void clean(Date now) {
64 long nowInMillis = now.getTime();
65 int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis);
66 lastHeartBeat = nowInMillis;
67 if (periodsElapsed > 1) {
68 addInfo("periodsElapsed = " + periodsElapsed);
69 }
70 for (int i = 0; i < periodsElapsed; i++) {
71 cleanByPeriodOffset(now, periodOffsetForDeletionTarget - i);
72 }
73 }
74
75 abstract void cleanByPeriodOffset(Date now, int periodOffset);
76
77 boolean computeParentCleaningFlag(FileNamePattern fileNamePattern) {
78 DateTokenConverter dtc = fileNamePattern.getPrimaryDateTokenConverter();
79
80 if (dtc.getDatePattern().indexOf('/') != -1) {
81 return true;
82 }
83
84
85
86 Converter<Object> p = fileNamePattern.headTokenConverter;
87
88
89 while (p != null) {
90 if (p instanceof DateTokenConverter) {
91 break;
92 }
93 p = p.getNext();
94 }
95
96 while (p != null) {
97 if (p instanceof LiteralConverter) {
98 String s = p.convert(null);
99 if (s.indexOf('/') != -1) {
100 return true;
101 }
102 }
103 p = p.getNext();
104 }
105
106
107 return false;
108 }
109
110 void removeFolderIfEmpty(File dir) {
111 removeFolderIfEmpty(dir, 0);
112 }
113
114
115
116
117
118
119
120
121
122 private void removeFolderIfEmpty(File dir, int depth) {
123
124 if (depth >= 3) {
125 return;
126 }
127 if (dir.isDirectory() && FileFilterUtil.isEmptyDirectory(dir)) {
128 addInfo("deleting folder [" + dir + "]");
129 dir.delete();
130 removeFolderIfEmpty(dir.getParentFile(), depth + 1);
131 }
132 }
133
134 public void setMaxHistory(int maxHistory) {
135 this.periodOffsetForDeletionTarget = -maxHistory - 1;
136 }
137
138 }