001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v2.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.rolling; 015 016import static ch.qos.logback.core.CoreConstants.CODES_URL; 017 018import java.io.File; 019import java.time.Instant; 020import java.time.ZoneId; 021import java.util.Locale; 022import java.util.TimeZone; 023import java.util.concurrent.atomic.AtomicLong; 024 025import ch.qos.logback.core.CoreConstants; 026import ch.qos.logback.core.rolling.helper.ArchiveRemover; 027import ch.qos.logback.core.rolling.helper.DateTokenConverter; 028import ch.qos.logback.core.rolling.helper.RollingCalendar; 029import ch.qos.logback.core.spi.ContextAwareBase; 030 031/** 032 * Base implementation of {@link TimeBasedFileNamingAndTriggeringPolicy}. 033 * 034 * <p>See also derived classes {@link DefaultTimeBasedFileNamingAndTriggeringPolicy} and 035 * {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy}. 036 * 037 * </p> 038 * 039 * @param <E> 040 */ 041abstract public class TimeBasedFileNamingAndTriggeringPolicyBase<E> extends ContextAwareBase 042 implements TimeBasedFileNamingAndTriggeringPolicy<E> { 043 044 static private String COLLIDING_DATE_FORMAT_URL = CODES_URL + "#rfa_collision_in_dateFormat"; 045 046 protected TimeBasedRollingPolicy<E> enclosingTBRP; 047 048 protected ArchiveRemover archiveRemover = null; 049 protected String elapsedPeriodsFileName; 050 protected RollingCalendar rc; 051 052 protected long artificialCurrentTime = -1; 053 054 protected AtomicLong atomicNextCheck = new AtomicLong(0); 055 protected Instant dateInCurrentPeriod = null; 056 057 protected boolean started = false; 058 protected boolean errorFree = true; 059 060 protected ZoneId zoneId = ZoneId.systemDefault(); 061 062 public boolean isStarted() { 063 return started; 064 } 065 066 public void start() { 067 DateTokenConverter<Object> dtc = enclosingTBRP.fileNamePattern.getPrimaryDateTokenConverter(); 068 if (dtc == null) { 069 throw new IllegalStateException( 070 "FileNamePattern [" + enclosingTBRP.fileNamePattern.getPattern() + "] does not contain a valid DateToken"); 071 } 072 073 if (dtc.getZoneId() != null) { 074 this.zoneId = dtc.getZoneId(); 075 TimeZone tz = TimeZone.getTimeZone(zoneId); 076 rc = new RollingCalendar(dtc.getDatePattern(), tz, Locale.getDefault()); 077 } else { 078 rc = new RollingCalendar(dtc.getDatePattern()); 079 } 080 addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" 081 + enclosingTBRP.fileNamePattern.getPattern() + "'."); 082 rc.printPeriodicity(this); 083 084 if (!rc.isCollisionFree()) { 085 addError( 086 "The date format in FileNamePattern will result in collisions in the names of archived log files."); 087 addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL); 088 withErrors(); 089 return; 090 } 091 092 long timestamp = getCurrentTime(); 093 setDateInCurrentPeriod(timestamp); 094 095 if (enclosingTBRP.getParentsRawFileProperty() != null) { 096 File currentFile = new File(enclosingTBRP.getParentsRawFileProperty()); 097 if (currentFile.canRead()) { 098 long lastModified = currentFile.lastModified(); 099 if(lastModified < timestamp) { 100 timestamp = lastModified; 101 } else { 102 addWarn("Current time is earlier than last modified of file [" + currentFile.getAbsolutePath() + "]. Ignoring last modified and using current time."); 103 } 104 setDateInCurrentPeriod(timestamp); 105 } 106 } 107 addInfo("Setting initial period to " + dateInCurrentPeriod); 108 long nextCheck = computeNextCheck(timestamp); 109 atomicNextCheck.set(nextCheck); 110 } 111 112 public void stop() { 113 started = false; 114 } 115 116 protected long computeNextCheck(long timestamp) { 117 return rc.getNextTriggeringDate(Instant.ofEpochMilli(timestamp)).toEpochMilli(); 118 } 119 120 public String getElapsedPeriodsFileName() { 121 return elapsedPeriodsFileName; 122 } 123 124 public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { 125 return enclosingTBRP.fileNamePatternWithoutCompSuffix.convert(dateInCurrentPeriod); 126 } 127 128 protected void setDateInCurrentPeriod(long timestamp) { 129 dateInCurrentPeriod = Instant.ofEpochMilli(timestamp); 130 } 131 132 public void setCurrentTime(long timeInMillis) { 133 artificialCurrentTime = timeInMillis; 134 } 135 136 public long getCurrentTime() { 137 // if time is forced return the time set by user 138 if (artificialCurrentTime >= 0) { 139 return artificialCurrentTime; 140 } else { 141 return System.currentTimeMillis(); 142 } 143 } 144 145 public void setTimeBasedRollingPolicy(TimeBasedRollingPolicy<E> _tbrp) { 146 this.enclosingTBRP = _tbrp; 147 148 } 149 150 public ArchiveRemover getArchiveRemover() { 151 return archiveRemover; 152 } 153 154 protected void withErrors() { 155 errorFree = false; 156 } 157 158 protected boolean isErrorFree() { 159 return errorFree; 160 } 161 162}