1 #include <QDateTime>
2 #include <QDir>
3 #include <QFileInfo>
4 
5 #include "RollingFileAppender.h"
6 
7 
RollingFileAppender(const QString & fileName)8 RollingFileAppender::RollingFileAppender(const QString& fileName)
9   : FileAppender(fileName),
10     m_logFilesLimit(0)
11 {}
12 
13 
append(const QDateTime & timeStamp,Logger::LogLevel logLevel,const char * file,int line,const char * function,const QString & category,const QString & message)14 void RollingFileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
15     const char* function, const QString& category, const QString& message)
16 {
17   if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
18     rollOver();
19 
20   FileAppender::append(timeStamp, logLevel, file, line, function, category, message);
21 }
22 
23 
datePattern() const24 RollingFileAppender::DatePattern RollingFileAppender::datePattern() const
25 {
26   QMutexLocker locker(&m_rollingMutex);
27   return m_frequency;
28 }
29 
30 
datePatternString() const31 QString RollingFileAppender::datePatternString() const
32 {
33   QMutexLocker locker(&m_rollingMutex);
34   return m_datePatternString;
35 }
36 
37 
setDatePattern(DatePattern datePattern)38 void RollingFileAppender::setDatePattern(DatePattern datePattern)
39 {
40   switch (datePattern)
41   {
42     case MinutelyRollover:
43       setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm"));
44       break;
45     case HourlyRollover:
46       setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh"));
47       break;
48     case HalfDailyRollover:
49       setDatePatternString(QLatin1String("'.'yyyy-MM-dd-a"));
50       break;
51     case DailyRollover:
52       setDatePatternString(QLatin1String("'.'yyyy-MM-dd"));
53       break;
54     case WeeklyRollover:
55       setDatePatternString(QLatin1String("'.'yyyy-ww"));
56       break;
57     case MonthlyRollover:
58       setDatePatternString(QLatin1String("'.'yyyy-MM"));
59       break;
60     default:
61       Q_ASSERT_X(false, "DailyRollingFileAppender::setDatePattern()", "Invalid datePattern constant");
62       setDatePattern(DailyRollover);
63   };
64 
65   QMutexLocker locker(&m_rollingMutex);
66   m_frequency = datePattern;
67 
68   computeRollOverTime();
69 }
70 
71 
setDatePattern(const QString & datePattern)72 void RollingFileAppender::setDatePattern(const QString& datePattern)
73 {
74   setDatePatternString(datePattern);
75   computeFrequency();
76 
77   computeRollOverTime();
78 }
79 
80 
setDatePatternString(const QString & datePatternString)81 void RollingFileAppender::setDatePatternString(const QString& datePatternString)
82 {
83   QMutexLocker locker(&m_rollingMutex);
84   m_datePatternString = datePatternString;
85 }
86 
87 
computeFrequency()88 void RollingFileAppender::computeFrequency()
89 {
90   QMutexLocker locker(&m_rollingMutex);
91 
92   const QDateTime startTime(QDate(1999, 1, 1), QTime(0, 0));
93   const QString startString = startTime.toString(m_datePatternString);
94 
95   if (startString != startTime.addSecs(60).toString(m_datePatternString))
96     m_frequency = MinutelyRollover;
97   else if (startString != startTime.addSecs(60 * 60).toString(m_datePatternString))
98     m_frequency = HourlyRollover;
99   else if (startString != startTime.addSecs(60 * 60 * 12).toString(m_datePatternString))
100     m_frequency = HalfDailyRollover;
101   else if (startString != startTime.addDays(1).toString(m_datePatternString))
102     m_frequency = DailyRollover;
103   else if (startString != startTime.addDays(7).toString(m_datePatternString))
104     m_frequency = WeeklyRollover;
105   else if (startString != startTime.addMonths(1).toString(m_datePatternString))
106     m_frequency = MonthlyRollover;
107   else
108   {
109     Q_ASSERT_X(false, "DailyRollingFileAppender::computeFrequency", "The pattern '%1' does not specify a frequency");
110     return;
111   }
112 }
113 
114 
removeOldFiles()115 void RollingFileAppender::removeOldFiles()
116 {
117   if (m_logFilesLimit <= 1)
118     return;
119 
120   QFileInfo fileInfo(fileName());
121   QDir logDirectory(fileInfo.absoluteDir());
122   logDirectory.setFilter(QDir::Files);
123   logDirectory.setNameFilters(QStringList() << fileInfo.fileName() + "*");
124   QFileInfoList logFiles = logDirectory.entryInfoList();
125 
126   QMap<QDateTime, QString> fileDates;
127   for (int i = 0; i < logFiles.length(); ++i)
128   {
129     QString name = logFiles[i].fileName();
130     QString suffix = name.mid(name.indexOf(fileInfo.fileName()) + fileInfo.fileName().length());
131     QDateTime fileDateTime = QDateTime::fromString(suffix, datePatternString());
132 
133     if (fileDateTime.isValid())
134       fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());
135   }
136 
137   QList<QString> fileDateNames = fileDates.values();
138   for (int i = 0; i < fileDateNames.length() - m_logFilesLimit + 1; ++i)
139     QFile::remove(fileDateNames[i]);
140 }
141 
142 
computeRollOverTime()143 void RollingFileAppender::computeRollOverTime()
144 {
145   Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::computeRollOverTime()", "No active date pattern");
146 
147   QDateTime now = QDateTime::currentDateTime();
148   QDate nowDate = now.date();
149   QTime nowTime = now.time();
150   QDateTime start;
151 
152   switch (m_frequency)
153   {
154     case MinutelyRollover:
155     {
156       start = QDateTime(nowDate, QTime(nowTime.hour(), nowTime.minute(), 0, 0));
157       m_rollOverTime = start.addSecs(60);
158     }
159     break;
160     case HourlyRollover:
161     {
162       start = QDateTime(nowDate, QTime(nowTime.hour(), 0, 0, 0));
163       m_rollOverTime = start.addSecs(60*60);
164     }
165     break;
166     case HalfDailyRollover:
167     {
168       int hour = nowTime.hour();
169       if (hour >=  12)
170         hour = 12;
171       else
172         hour = 0;
173       start = QDateTime(nowDate, QTime(hour, 0, 0, 0));
174       m_rollOverTime = start.addSecs(60*60*12);
175     }
176     break;
177     case DailyRollover:
178     {
179       start = QDateTime(nowDate, QTime(0, 0, 0, 0));
180       m_rollOverTime = start.addDays(1);
181     }
182     break;
183     case WeeklyRollover:
184     {
185       // Qt numbers the week days 1..7. The week starts on Monday.
186       // Change it to being numbered 0..6, starting with Sunday.
187       int day = nowDate.dayOfWeek();
188       if (day == Qt::Sunday)
189         day = 0;
190       start = QDateTime(nowDate, QTime(0, 0, 0, 0)).addDays(-1 * day);
191       m_rollOverTime = start.addDays(7);
192     }
193     break;
194     case MonthlyRollover:
195     {
196       start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), QTime(0, 0, 0, 0));
197       m_rollOverTime = start.addMonths(1);
198     }
199     break;
200     default:
201       Q_ASSERT_X(false, "DailyRollingFileAppender::computeInterval()", "Invalid datePattern constant");
202       m_rollOverTime = QDateTime::fromTime_t(0);
203   }
204 
205   m_rollOverSuffix = start.toString(m_datePatternString);
206   Q_ASSERT_X(now.toString(m_datePatternString) == m_rollOverSuffix,
207       "DailyRollingFileAppender::computeRollOverTime()", "File name changes within interval");
208   Q_ASSERT_X(m_rollOverSuffix != m_rollOverTime.toString(m_datePatternString),
209       "DailyRollingFileAppender::computeRollOverTime()", "File name does not change with rollover");
210 }
211 
212 
rollOver()213 void RollingFileAppender::rollOver()
214 {
215   Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
216 
217   QString rollOverSuffix = m_rollOverSuffix;
218   computeRollOverTime();
219   if (rollOverSuffix == m_rollOverSuffix)
220     return;
221 
222   closeFile();
223 
224   QString targetFileName = fileName() + rollOverSuffix;
225   QFile f(targetFileName);
226   if (f.exists() && !f.remove())
227     return;
228   f.setFileName(fileName());
229   if (!f.rename(targetFileName))
230     return;
231 
232   openFile();
233   removeOldFiles();
234 }
235 
236 
setLogFilesLimit(int limit)237 void RollingFileAppender::setLogFilesLimit(int limit)
238 {
239   QMutexLocker locker(&m_rollingMutex);
240   m_logFilesLimit = limit;
241 }
242 
243 
logFilesLimit() const244 int RollingFileAppender::logFilesLimit() const
245 {
246   QMutexLocker locker(&m_rollingMutex);
247   return m_logFilesLimit;
248 }
249