1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core.rolling;
15
16 import static ch.qos.logback.core.CoreConstants.UNBOUNDED_HISTORY;
17 import static ch.qos.logback.core.CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP;
18
19 import java.io.File;
20 import java.util.Date;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24
25 import ch.qos.logback.core.CoreConstants;
26 import ch.qos.logback.core.rolling.helper.ArchiveRemover;
27 import ch.qos.logback.core.rolling.helper.CompressionMode;
28 import ch.qos.logback.core.rolling.helper.Compressor;
29 import ch.qos.logback.core.rolling.helper.FileFilterUtil;
30 import ch.qos.logback.core.rolling.helper.FileNamePattern;
31 import ch.qos.logback.core.rolling.helper.RenameUtil;
32 import ch.qos.logback.core.util.FileSize;
33
34
35
36
37
38
39
40
41
42
43
44
45 public class TimeBasedRollingPolicy<E> extends RollingPolicyBase implements TriggeringPolicy<E> {
46 static final String FNP_NOT_SET = "The FileNamePattern option must be set before using TimeBasedRollingPolicy. ";
47
48 FileNamePattern fileNamePatternWithoutCompSuffix;
49
50 private Compressor compressor;
51 private RenameUtil renameUtil = new RenameUtil();
52 Future<?> compressionFuture;
53 Future<?> cleanUpFuture;
54
55 private int maxHistory = UNBOUNDED_HISTORY;
56 protected FileSize totalSizeCap = new FileSize(UNBOUNDED_TOTAL_SIZE_CAP);
57
58 private ArchiveRemover archiveRemover;
59
60 TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedFileNamingAndTriggeringPolicy;
61
62 boolean cleanHistoryOnStart = false;
63
64 public void start() {
65
66 renameUtil.setContext(this.context);
67
68
69 if (fileNamePatternStr != null) {
70 fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context);
71 determineCompressionMode();
72 } else {
73 addWarn(FNP_NOT_SET);
74 addWarn(CoreConstants.SEE_FNP_NOT_SET);
75 throw new IllegalStateException(FNP_NOT_SET + CoreConstants.SEE_FNP_NOT_SET);
76 }
77
78 compressor = new Compressor(compressionMode);
79 compressor.setContext(context);
80
81
82 fileNamePatternWithoutCompSuffix = new FileNamePattern(
83 Compressor.computeFileNameStrWithoutCompSuffix(fileNamePatternStr, compressionMode), this.context);
84
85 addInfo("Will use the pattern " + fileNamePatternWithoutCompSuffix + " for the active file");
86
87 if (compressionMode == CompressionMode.ZIP) {
88 String zipEntryFileNamePatternStr = transformFileNamePattern2ZipEntry(fileNamePatternStr);
89 zipEntryFileNamePattern = new FileNamePattern(zipEntryFileNamePatternStr, context);
90 }
91
92 if (timeBasedFileNamingAndTriggeringPolicy == null) {
93 timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy<>();
94 }
95 timeBasedFileNamingAndTriggeringPolicy.setContext(context);
96 timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this);
97 timeBasedFileNamingAndTriggeringPolicy.start();
98
99 if (!timeBasedFileNamingAndTriggeringPolicy.isStarted()) {
100 addWarn("Subcomponent did not start. TimeBasedRollingPolicy will not start.");
101 return;
102 }
103
104
105
106
107 if (maxHistory != UNBOUNDED_HISTORY) {
108 archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover();
109 archiveRemover.setMaxHistory(maxHistory);
110 archiveRemover.setTotalSizeCap(totalSizeCap.getSize());
111 if (cleanHistoryOnStart) {
112 addInfo("Cleaning on start up");
113 Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
114 cleanUpFuture = archiveRemover.cleanAsynchronously(now);
115 }
116 } else if (!isUnboundedTotalSizeCap()) {
117 addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value [" + totalSizeCap + "]");
118 }
119
120 super.start();
121 }
122
123 protected boolean isUnboundedTotalSizeCap() {
124 return totalSizeCap.getSize() == UNBOUNDED_TOTAL_SIZE_CAP;
125 }
126
127 @Override
128 public void stop() {
129 if (!isStarted())
130 return;
131 waitForAsynchronousJobToStop(compressionFuture, "compression");
132 waitForAsynchronousJobToStop(cleanUpFuture, "clean-up");
133 super.stop();
134 }
135
136 private void waitForAsynchronousJobToStop(Future<?> aFuture, String jobDescription) {
137 if (aFuture != null) {
138 try {
139 aFuture.get(CoreConstants.SECONDS_TO_WAIT_FOR_COMPRESSION_JOBS, TimeUnit.SECONDS);
140 } catch (TimeoutException e) {
141 addError("Timeout while waiting for " + jobDescription + " job to finish", e);
142 } catch (Exception e) {
143 addError("Unexpected exception while waiting for " + jobDescription + " job to finish", e);
144 }
145 }
146 }
147
148 private String transformFileNamePattern2ZipEntry(String fileNamePatternStr) {
149 String slashified = FileFilterUtil.slashify(fileNamePatternStr);
150 return FileFilterUtil.afterLastSlash(slashified);
151 }
152
153 public void setTimeBasedFileNamingAndTriggeringPolicy(
154 TimeBasedFileNamingAndTriggeringPolicy<E> timeBasedTriggering) {
155 this.timeBasedFileNamingAndTriggeringPolicy = timeBasedTriggering;
156 }
157
158 public TimeBasedFileNamingAndTriggeringPolicy<E> getTimeBasedFileNamingAndTriggeringPolicy() {
159 return timeBasedFileNamingAndTriggeringPolicy;
160 }
161
162 public void rollover() throws RolloverFailure {
163
164
165
166
167 String elapsedPeriodsFileName = timeBasedFileNamingAndTriggeringPolicy.getElapsedPeriodsFileName();
168
169 String elapsedPeriodStem = FileFilterUtil.afterLastSlash(elapsedPeriodsFileName);
170
171 if (compressionMode == CompressionMode.NONE) {
172 if (getParentsRawFileProperty() != null) {
173 renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName);
174 }
175
176 } else {
177 if (getParentsRawFileProperty() == null) {
178 compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName,
179 elapsedPeriodStem);
180 } else {
181 compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem);
182 }
183 }
184
185 if (archiveRemover != null) {
186 Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
187 this.cleanUpFuture = archiveRemover.cleanAsynchronously(now);
188 }
189 }
190
191 Future<?> renameRawAndAsyncCompress(String nameOfCompressedFile, String innerEntryName) throws RolloverFailure {
192 String parentsRawFile = getParentsRawFileProperty();
193 String tmpTarget = nameOfCompressedFile + System.nanoTime() + ".tmp";
194 renameUtil.rename(parentsRawFile, tmpTarget);
195 return compressor.asyncCompress(tmpTarget, nameOfCompressedFile, innerEntryName);
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 public String getActiveFileName() {
221 String parentsRawFileProperty = getParentsRawFileProperty();
222 if (parentsRawFileProperty != null) {
223 return parentsRawFileProperty;
224 } else {
225 return timeBasedFileNamingAndTriggeringPolicy.getCurrentPeriodsFileNameWithoutCompressionSuffix();
226 }
227 }
228
229 public boolean isTriggeringEvent(File activeFile, final E event) {
230 return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event);
231 }
232
233
234
235
236
237
238 public int getMaxHistory() {
239 return maxHistory;
240 }
241
242
243
244
245
246
247 public void setMaxHistory(int maxHistory) {
248 this.maxHistory = maxHistory;
249 }
250
251 public boolean isCleanHistoryOnStart() {
252 return cleanHistoryOnStart;
253 }
254
255
256
257
258
259
260
261
262 public void setCleanHistoryOnStart(boolean cleanHistoryOnStart) {
263 this.cleanHistoryOnStart = cleanHistoryOnStart;
264 }
265
266 @Override
267 public String toString() {
268 return "c.q.l.core.rolling.TimeBasedRollingPolicy@" + this.hashCode();
269 }
270
271 public void setTotalSizeCap(FileSize totalSizeCap) {
272 addInfo("setting totalSizeCap to " + totalSizeCap.toString());
273 this.totalSizeCap = totalSizeCap;
274 }
275 }