1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.rolling;
15  
16  import static ch.qos.logback.core.CoreConstants.CODES_URL;
17  
18  import java.io.File;
19  import java.time.Instant;
20  import java.time.ZoneId;
21  import java.util.Locale;
22  import java.util.TimeZone;
23  import java.util.concurrent.atomic.AtomicLong;
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.DateTokenConverter;
28  import ch.qos.logback.core.rolling.helper.RollingCalendar;
29  import ch.qos.logback.core.spi.ContextAwareBase;
30  
31  /**
32   * Base implementation of {@link TimeBasedFileNamingAndTriggeringPolicy}.
33   *
34   * <p>See also derived classes {@link DefaultTimeBasedFileNamingAndTriggeringPolicy} and
35   * {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy}.
36   *
37   * </p>
38   *
39   * @param <E>
40   */
41  abstract public class TimeBasedFileNamingAndTriggeringPolicyBase<E> extends ContextAwareBase
42          implements TimeBasedFileNamingAndTriggeringPolicy<E> {
43  
44      static private String COLLIDING_DATE_FORMAT_URL = CODES_URL + "#rfa_collision_in_dateFormat";
45  
46      protected TimeBasedRollingPolicy<E> tbrp;
47  
48      protected ArchiveRemover archiveRemover = null;
49      protected String elapsedPeriodsFileName;
50      protected RollingCalendar rc;
51  
52      protected long artificialCurrentTime = -1;
53  
54      protected AtomicLong atomicNextCheck = new AtomicLong(0);
55      protected Instant dateInCurrentPeriod = null;
56  
57      protected boolean started = false;
58      protected boolean errorFree = true;
59  
60      protected ZoneId zoneId = ZoneId.systemDefault();
61  
62      public boolean isStarted() {
63          return started;
64      }
65  
66      public void start() {
67          DateTokenConverter<Object> dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter();
68          if (dtc == null) {
69              throw new IllegalStateException(
70                      "FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken");
71          }
72  
73          if (dtc.getZoneId() != null) {
74              this.zoneId = dtc.getZoneId();
75              TimeZone tz = TimeZone.getTimeZone(zoneId);
76              rc = new RollingCalendar(dtc.getDatePattern(), tz, Locale.getDefault());
77          } else {
78              rc = new RollingCalendar(dtc.getDatePattern());
79          }
80          addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '"
81                  + tbrp.fileNamePattern.getPattern() + "'.");
82          rc.printPeriodicity(this);
83  
84          if (!rc.isCollisionFree()) {
85              addError(
86                      "The date format in FileNamePattern will result in collisions in the names of archived log files.");
87              addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL);
88              withErrors();
89              return;
90          }
91  
92          long timestamp = getCurrentTime();
93          setDateInCurrentPeriod(timestamp);
94  
95          if (tbrp.getParentsRawFileProperty() != null) {
96              File currentFile = new File(tbrp.getParentsRawFileProperty());
97              if (currentFile.canRead()) {
98                  timestamp = currentFile.lastModified();
99                  setDateInCurrentPeriod(timestamp);
100             }
101         }
102         addInfo("Setting initial period to " + dateInCurrentPeriod);
103         long nextCheck = computeNextCheck(timestamp);
104         atomicNextCheck.set(nextCheck);
105     }
106 
107     public void stop() {
108         started = false;
109     }
110 
111     protected long computeNextCheck(long timestamp) {
112         return rc.getNextTriggeringDate(Instant.ofEpochMilli(timestamp)).toEpochMilli();
113     }
114 
115     public String getElapsedPeriodsFileName() {
116         return elapsedPeriodsFileName;
117     }
118 
119     public String getCurrentPeriodsFileNameWithoutCompressionSuffix() {
120         return tbrp.fileNamePatternWithoutCompSuffix.convert(dateInCurrentPeriod);
121     }
122 
123     protected void setDateInCurrentPeriod(long timestamp) {
124         dateInCurrentPeriod = Instant.ofEpochMilli(timestamp);
125     }
126 
127     public void setCurrentTime(long timeInMillis) {
128         artificialCurrentTime = timeInMillis;
129     }
130 
131     public long getCurrentTime() {
132         // if time is forced return the time set by user
133         if (artificialCurrentTime >= 0) {
134             return artificialCurrentTime;
135         } else {
136             return System.currentTimeMillis();
137         }
138     }
139 
140     public void setTimeBasedRollingPolicy(TimeBasedRollingPolicy<E> _tbrp) {
141         this.tbrp = _tbrp;
142 
143     }
144 
145     public ArchiveRemover getArchiveRemover() {
146         return archiveRemover;
147     }
148 
149     protected void withErrors() {
150         errorFree = false;
151     }
152 
153     protected boolean isErrorFree() {
154         return errorFree;
155     }
156 
157 }