001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, 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 v1.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.util.Locale;
021import java.util.TimeZone;
022import java.util.concurrent.atomic.AtomicLong;
023
024import ch.qos.logback.core.CoreConstants;
025import ch.qos.logback.core.rolling.helper.ArchiveRemover;
026import ch.qos.logback.core.rolling.helper.DateTokenConverter;
027import ch.qos.logback.core.rolling.helper.RollingCalendar;
028import ch.qos.logback.core.spi.ContextAwareBase;
029
030/**
031 * Base implementation of {@link TimeBasedFileNamingAndTriggeringPolicy}.
032 *
033 * <p>See also derived classes {@link DefaultTimeBasedFileNamingAndTriggeringPolicy} and
034 * {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy}.
035 *
036 * </p>
037 *
038 * @param <E>
039 */
040abstract public class TimeBasedFileNamingAndTriggeringPolicyBase<E> extends ContextAwareBase
041        implements TimeBasedFileNamingAndTriggeringPolicy<E> {
042
043    static private String COLLIDING_DATE_FORMAT_URL = CODES_URL + "#rfa_collision_in_dateFormat";
044
045    protected TimeBasedRollingPolicy<E> tbrp;
046
047    protected ArchiveRemover archiveRemover = null;
048    protected String elapsedPeriodsFileName;
049    protected RollingCalendar rc;
050
051    protected long artificialCurrentTime = -1;
052
053    protected AtomicLong atomicNextCheck = new AtomicLong(0);
054    protected Instant dateInCurrentPeriod = null;
055
056    protected boolean started = false;
057    protected boolean errorFree = true;
058
059    public boolean isStarted() {
060        return started;
061    }
062
063    public void start() {
064        DateTokenConverter<Object> dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter();
065        if (dtc == null) {
066            throw new IllegalStateException(
067                    "FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken");
068        }
069
070        if (dtc.getZoneId() != null) {
071            TimeZone tz = TimeZone.getTimeZone(dtc.getZoneId());
072            rc = new RollingCalendar(dtc.getDatePattern(), tz, Locale.getDefault());
073        } else {
074            rc = new RollingCalendar(dtc.getDatePattern());
075        }
076        addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '"
077                + tbrp.fileNamePattern.getPattern() + "'.");
078        rc.printPeriodicity(this);
079
080        if (!rc.isCollisionFree()) {
081            addError(
082                    "The date format in FileNamePattern will result in collisions in the names of archived log files.");
083            addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL);
084            withErrors();
085            return;
086        }
087
088        long timestamp = getCurrentTime();
089        setDateInCurrentPeriod(timestamp);
090
091        if (tbrp.getParentsRawFileProperty() != null) {
092            File currentFile = new File(tbrp.getParentsRawFileProperty());
093            if (currentFile.exists() && currentFile.canRead()) {
094                timestamp = currentFile.lastModified();
095                setDateInCurrentPeriod(timestamp);
096            }
097        }
098        addInfo("Setting initial period to " + dateInCurrentPeriod);
099        long nextCheck = computeNextCheck(timestamp);
100        atomicNextCheck.set(nextCheck);
101    }
102
103    public void stop() {
104        started = false;
105    }
106
107    protected long computeNextCheck(long timestamp) {
108        return rc.getNextTriggeringDate(Instant.ofEpochMilli(timestamp)).toEpochMilli();
109    }
110
111    public String getElapsedPeriodsFileName() {
112        return elapsedPeriodsFileName;
113    }
114
115    public String getCurrentPeriodsFileNameWithoutCompressionSuffix() {
116        return tbrp.fileNamePatternWithoutCompSuffix.convert(dateInCurrentPeriod);
117    }
118
119    protected void setDateInCurrentPeriod(long timestamp) {
120        dateInCurrentPeriod = Instant.ofEpochMilli(timestamp);
121    }
122
123    public void setCurrentTime(long timeInMillis) {
124        artificialCurrentTime = timeInMillis;
125    }
126
127    public long getCurrentTime() {
128        // if time is forced return the time set by user
129        if (artificialCurrentTime >= 0) {
130            return artificialCurrentTime;
131        } else {
132            return System.currentTimeMillis();
133        }
134    }
135
136    public void setTimeBasedRollingPolicy(TimeBasedRollingPolicy<E> _tbrp) {
137        this.tbrp = _tbrp;
138
139    }
140
141    public ArchiveRemover getArchiveRemover() {
142        return archiveRemover;
143    }
144
145    protected void withErrors() {
146        errorFree = false;
147    }
148
149    protected boolean isErrorFree() {
150        return errorFree;
151    }
152
153}