001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.appender.rolling; 018 019 import org.apache.logging.log4j.core.pattern.ArrayPatternConverter; 020 import org.apache.logging.log4j.core.pattern.DatePatternConverter; 021 import org.apache.logging.log4j.core.pattern.FormattingInfo; 022 import org.apache.logging.log4j.core.pattern.PatternConverter; 023 import org.apache.logging.log4j.core.pattern.PatternParser; 024 025 import java.util.ArrayList; 026 import java.util.Calendar; 027 import java.util.Date; 028 import java.util.List; 029 030 /** 031 * Parse the rollover pattern. 032 */ 033 public class PatternProcessor { 034 035 private static final String KEY = "FileConverter"; 036 037 private static final char YEAR_CHAR = 'y'; 038 private static final char MONTH_CHAR = 'M'; 039 private static final char[] WEEK_CHARS = {'w', 'W'}; 040 private static final char[] DAY_CHARS = {'D', 'd', 'F', 'E'}; 041 private static final char[] HOUR_CHARS = {'H', 'K', 'h', 'k'}; 042 private static final char MINUTE_CHAR = 'm'; 043 private static final char SECOND_CHAR = 's'; 044 private static final char MILLIS_CHAR = 'S'; 045 046 private final ArrayPatternConverter[] patternConverters; 047 private final FormattingInfo[] patternFields; 048 049 private long prevFileTime = 0; 050 private long nextFileTime = 0; 051 052 private RolloverFrequency frequency = null; 053 054 /** 055 * Constructor. 056 * @param pattern The file pattern. 057 */ 058 public PatternProcessor(final String pattern) { 059 final PatternParser parser = createPatternParser(); 060 final List<PatternConverter> converters = new ArrayList<PatternConverter>(); 061 final List<FormattingInfo> fields = new ArrayList<FormattingInfo>(); 062 parser.parse(pattern, converters, fields); 063 final FormattingInfo[] infoArray = new FormattingInfo[fields.size()]; 064 patternFields = fields.toArray(infoArray); 065 final ArrayPatternConverter[] converterArray = new ArrayPatternConverter[converters.size()]; 066 patternConverters = converters.toArray(converterArray); 067 068 for (final ArrayPatternConverter converter : patternConverters) { 069 if (converter instanceof DatePatternConverter) { 070 final DatePatternConverter dateConverter = (DatePatternConverter) converter; 071 frequency = calculateFrequency(dateConverter.getPattern()); 072 } 073 } 074 } 075 076 /** 077 * Returns the next potential rollover time. 078 * @param current The current time. 079 * @param increment The increment to the next time. 080 * @param modulus If true the time will be rounded to occur on a boundary aligned with the increment. 081 * @return the next potential rollover time and the timestamp for the target file. 082 */ 083 public long getNextTime(final long current, final int increment, final boolean modulus) { 084 prevFileTime = nextFileTime; 085 long nextTime; 086 087 if (frequency == null) { 088 throw new IllegalStateException("Pattern does not contain a date"); 089 } 090 final Calendar currentCal = Calendar.getInstance(); 091 currentCal.setTimeInMillis(current); 092 final Calendar cal = Calendar.getInstance(); 093 cal.set(currentCal.get(Calendar.YEAR), 0, 1, 0, 0, 0); 094 cal.set(Calendar.MILLISECOND, 0); 095 if (frequency == RolloverFrequency.ANNUALLY) { 096 increment(cal, Calendar.YEAR, increment, modulus); 097 nextTime = cal.getTimeInMillis(); 098 cal.add(Calendar.YEAR, -1); 099 nextFileTime = cal.getTimeInMillis(); 100 return nextTime; 101 } 102 if (frequency == RolloverFrequency.MONTHLY) { 103 increment(cal, Calendar.MONTH, increment, modulus); 104 nextTime = cal.getTimeInMillis(); 105 cal.add(Calendar.MONTH, -1); 106 nextFileTime = cal.getTimeInMillis(); 107 return nextTime; 108 } 109 if (frequency == RolloverFrequency.WEEKLY) { 110 increment(cal, Calendar.WEEK_OF_YEAR, increment, modulus); 111 nextTime = cal.getTimeInMillis(); 112 cal.add(Calendar.WEEK_OF_YEAR, -1); 113 nextFileTime = cal.getTimeInMillis(); 114 return nextTime; 115 } 116 cal.set(Calendar.DAY_OF_YEAR, currentCal.get(Calendar.DAY_OF_YEAR)); 117 if (frequency == RolloverFrequency.DAILY) { 118 increment(cal, Calendar.DAY_OF_YEAR, increment, modulus); 119 nextTime = cal.getTimeInMillis(); 120 cal.add(Calendar.DAY_OF_YEAR, -1); 121 nextFileTime = cal.getTimeInMillis(); 122 return nextTime; 123 } 124 cal.set(Calendar.HOUR, currentCal.get(Calendar.HOUR)); 125 if (frequency == RolloverFrequency.HOURLY) { 126 increment(cal, Calendar.HOUR, increment, modulus); 127 nextTime = cal.getTimeInMillis(); 128 cal.add(Calendar.HOUR, -1); 129 nextFileTime = cal.getTimeInMillis(); 130 return nextTime; 131 } 132 cal.set(Calendar.MINUTE, currentCal.get(Calendar.MINUTE)); 133 if (frequency == RolloverFrequency.EVERY_MINUTE) { 134 increment(cal, Calendar.MINUTE, increment, modulus); 135 nextTime = cal.getTimeInMillis(); 136 cal.add(Calendar.MINUTE, -1); 137 nextFileTime = cal.getTimeInMillis(); 138 return nextTime; 139 } 140 cal.set(Calendar.SECOND, currentCal.get(Calendar.SECOND)); 141 if (frequency == RolloverFrequency.EVERY_SECOND) { 142 increment(cal, Calendar.SECOND, increment, modulus); 143 nextTime = cal.getTimeInMillis(); 144 cal.add(Calendar.SECOND, -1); 145 nextFileTime = cal.getTimeInMillis(); 146 return nextTime; 147 } 148 increment(cal, Calendar.MILLISECOND, increment, modulus); 149 nextTime = cal.getTimeInMillis(); 150 cal.add(Calendar.MILLISECOND, -1); 151 nextFileTime = cal.getTimeInMillis(); 152 return nextTime; 153 } 154 155 private void increment(final Calendar cal, final int type, final int increment, final boolean modulate) { 156 final int interval = modulate ? increment - (cal.get(type) % increment) : increment; 157 cal.add(type, interval); 158 } 159 160 /** 161 * Format file name. 162 * @param buf string buffer to which formatted file name is appended, may not be null. 163 * @param obj object to be evaluated in formatting, may not be null. 164 */ 165 public final void formatFileName(final StringBuilder buf, final Object obj) { 166 final long time = prevFileTime == 0 ? System.currentTimeMillis() : prevFileTime; 167 formatFileName(buf, new Date(time), obj); 168 } 169 170 /** 171 * Format file name. 172 * @param buf string buffer to which formatted file name is appended, may not be null. 173 * @param objects objects to be evaluated in formatting, may not be null. 174 */ 175 protected final void formatFileName(final StringBuilder buf, final Object... objects) { 176 for (int i = 0; i < patternConverters.length; i++) { 177 final int fieldStart = buf.length(); 178 patternConverters[i].format(buf, objects); 179 180 if (patternFields[i] != null) { 181 patternFields[i].format(fieldStart, buf); 182 } 183 } 184 } 185 186 private RolloverFrequency calculateFrequency(final String pattern) { 187 if (patternContains(pattern, MILLIS_CHAR)) { 188 return RolloverFrequency.EVERY_MILLISECOND; 189 } 190 if (patternContains(pattern, SECOND_CHAR)) { 191 return RolloverFrequency.EVERY_SECOND; 192 } 193 if (patternContains(pattern, MINUTE_CHAR)) { 194 return RolloverFrequency.EVERY_MINUTE; 195 } 196 if (patternContains(pattern, HOUR_CHARS)) { 197 return RolloverFrequency.HOURLY; 198 } 199 if (patternContains(pattern, DAY_CHARS)) { 200 return RolloverFrequency.DAILY; 201 } 202 if (patternContains(pattern, WEEK_CHARS)) { 203 return RolloverFrequency.WEEKLY; 204 } 205 if (patternContains(pattern, MONTH_CHAR)) { 206 return RolloverFrequency.MONTHLY; 207 } 208 if (patternContains(pattern, YEAR_CHAR)) { 209 return RolloverFrequency.ANNUALLY; 210 } 211 return null; 212 } 213 214 private PatternParser createPatternParser() { 215 216 return new PatternParser(null, KEY, null); 217 } 218 219 private boolean patternContains(final String pattern, final char... chars) { 220 for (final char character : chars) { 221 if (patternContains(pattern, character)) { 222 return true; 223 } 224 } 225 return false; 226 } 227 228 private boolean patternContains(final String pattern, final char character) { 229 return pattern.indexOf(character) >= 0; 230 } 231 }