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