1//
2// FileChannel.cpp
3//
4// Library: Foundation
5// Package: Logging
6// Module: FileChannel
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/FileChannel.h"
16#include "Poco/ArchiveStrategy.h"
17#include "Poco/RotateStrategy.h"
18#include "Poco/PurgeStrategy.h"
19#include "Poco/Message.h"
20#include "Poco/NumberParser.h"
21#include "Poco/DateTimeFormatter.h"
22#include "Poco/DateTime.h"
23#include "Poco/LocalDateTime.h"
24#include "Poco/String.h"
25#include "Poco/Exception.h"
26#include "Poco/Ascii.h"
27
28
29namespace Poco {
30
31
32const std::string FileChannel::PROP_PATH = "path";
33const std::string FileChannel::PROP_ROTATION = "rotation";
34const std::string FileChannel::PROP_ARCHIVE = "archive";
35const std::string FileChannel::PROP_TIMES = "times";
36const std::string FileChannel::PROP_COMPRESS = "compress";
37const std::string FileChannel::PROP_PURGEAGE = "purgeAge";
38const std::string FileChannel::PROP_PURGECOUNT = "purgeCount";
39const std::string FileChannel::PROP_FLUSH = "flush";
40const std::string FileChannel::PROP_ROTATEONOPEN = "rotateOnOpen";
41
42FileChannel::FileChannel():
43 _times("utc"),
44 _compress(false),
45 _flush(true),
46 _rotateOnOpen(false),
47 _pFile(0),
48 _pRotateStrategy(0),
49 _pArchiveStrategy(new ArchiveByNumberStrategy),
50 _pPurgeStrategy(0)
51{
52}
53
54
55FileChannel::FileChannel(const std::string& rPath):
56 _path(rPath),
57 _times("utc"),
58 _compress(false),
59 _flush(true),
60 _rotateOnOpen(false),
61 _pFile(0),
62 _pRotateStrategy(0),
63 _pArchiveStrategy(new ArchiveByNumberStrategy),
64 _pPurgeStrategy(0)
65{
66}
67
68
69FileChannel::~FileChannel()
70{
71 try
72 {
73 close();
74 delete _pRotateStrategy;
75 delete _pArchiveStrategy;
76 delete _pPurgeStrategy;
77 }
78 catch (...)
79 {
80 poco_unexpected();
81 }
82}
83
84
85void FileChannel::open()
86{
87 FastMutex::ScopedLock lock(_mutex);
88
89 if (!_pFile)
90 {
91 _pFile = new LogFile(_path);
92 if (_rotateOnOpen && _pFile->size() > 0)
93 {
94 try
95 {
96 _pFile = _pArchiveStrategy->archive(_pFile);
97 purge();
98 }
99 catch (...)
100 {
101 _pFile = new LogFile(_path);
102 }
103 }
104 }
105}
106
107
108void FileChannel::close()
109{
110 FastMutex::ScopedLock lock(_mutex);
111
112 delete _pFile;
113 _pFile = 0;
114}
115
116
117void FileChannel::log(const Message& msg)
118{
119 open();
120
121 FastMutex::ScopedLock lock(_mutex);
122
123 if (_pRotateStrategy && _pArchiveStrategy && _pRotateStrategy->mustRotate(_pFile))
124 {
125 try
126 {
127 _pFile = _pArchiveStrategy->archive(_pFile);
128 purge();
129 }
130 catch (...)
131 {
132 _pFile = new LogFile(_path);
133 }
134 // we must call mustRotate() again to give the
135 // RotateByIntervalStrategy a chance to write its timestamp
136 // to the new file.
137 _pRotateStrategy->mustRotate(_pFile);
138 }
139 _pFile->write(msg.getText(), _flush);
140}
141
142
143void FileChannel::setProperty(const std::string& name, const std::string& value)
144{
145 FastMutex::ScopedLock lock(_mutex);
146
147 if (name == PROP_TIMES)
148 {
149 _times = value;
150
151 if (!_rotation.empty())
152 setRotation(_rotation);
153
154 if (!_archive.empty())
155 setArchive(_archive);
156 }
157 else if (name == PROP_PATH)
158 _path = value;
159 else if (name == PROP_ROTATION)
160 setRotation(value);
161 else if (name == PROP_ARCHIVE)
162 setArchive(value);
163 else if (name == PROP_COMPRESS)
164 setCompress(value);
165 else if (name == PROP_PURGEAGE)
166 setPurgeAge(value);
167 else if (name == PROP_PURGECOUNT)
168 setPurgeCount(value);
169 else if (name == PROP_FLUSH)
170 setFlush(value);
171 else if (name == PROP_ROTATEONOPEN)
172 setRotateOnOpen(value);
173 else
174 Channel::setProperty(name, value);
175}
176
177
178std::string FileChannel::getProperty(const std::string& name) const
179{
180 if (name == PROP_TIMES)
181 return _times;
182 else if (name == PROP_PATH)
183 return _path;
184 else if (name == PROP_ROTATION)
185 return _rotation;
186 else if (name == PROP_ARCHIVE)
187 return _archive;
188 else if (name == PROP_COMPRESS)
189 return std::string(_compress ? "true" : "false");
190 else if (name == PROP_PURGEAGE)
191 return _purgeAge;
192 else if (name == PROP_PURGECOUNT)
193 return _purgeCount;
194 else if (name == PROP_FLUSH)
195 return std::string(_flush ? "true" : "false");
196 else if (name == PROP_ROTATEONOPEN)
197 return std::string(_rotateOnOpen ? "true" : "false");
198 else
199 return Channel::getProperty(name);
200}
201
202
203Timestamp FileChannel::creationDate() const
204{
205 if (_pFile)
206 return _pFile->creationDate();
207 else
208 return 0;
209}
210
211
212UInt64 FileChannel::size() const
213{
214 if (_pFile)
215 return _pFile->size();
216 else
217 return 0;
218}
219
220
221const std::string& FileChannel::path() const
222{
223 return _path;
224}
225
226
227void FileChannel::setRotation(const std::string& rotation)
228{
229 std::string::const_iterator it = rotation.begin();
230 std::string::const_iterator end = rotation.end();
231 int n = 0;
232 while (it != end && Ascii::isSpace(*it)) ++it;
233 while (it != end && Ascii::isDigit(*it)) { n *= 10; n += *it++ - '0'; }
234 while (it != end && Ascii::isSpace(*it)) ++it;
235 std::string unit;
236 while (it != end && Ascii::isAlpha(*it)) unit += *it++;
237
238 RotateStrategy* pStrategy = 0;
239 if ((rotation.find(',') != std::string::npos) || (rotation.find(':') != std::string::npos))
240 {
241 if (_times == "utc")
242 pStrategy = new RotateAtTimeStrategy<DateTime>(rotation);
243 else if (_times == "local")
244 pStrategy = new RotateAtTimeStrategy<LocalDateTime>(rotation);
245 else
246 throw PropertyNotSupportedException("times", _times);
247 }
248 else if (unit == "daily")
249 pStrategy = new RotateByIntervalStrategy(Timespan(1*Timespan::DAYS));
250 else if (unit == "weekly")
251 pStrategy = new RotateByIntervalStrategy(Timespan(7*Timespan::DAYS));
252 else if (unit == "monthly")
253 pStrategy = new RotateByIntervalStrategy(Timespan(30*Timespan::DAYS));
254 else if (unit == "seconds") // for testing only
255 pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::SECONDS));
256 else if (unit == "minutes")
257 pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::MINUTES));
258 else if (unit == "hours")
259 pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::HOURS));
260 else if (unit == "days")
261 pStrategy = new RotateByIntervalStrategy(Timespan(n*Timespan::DAYS));
262 else if (unit == "weeks")
263 pStrategy = new RotateByIntervalStrategy(Timespan(n*7*Timespan::DAYS));
264 else if (unit == "months")
265 pStrategy = new RotateByIntervalStrategy(Timespan(n*30*Timespan::DAYS));
266 else if (unit == "K")
267 pStrategy = new RotateBySizeStrategy(n*1024);
268 else if (unit == "M")
269 pStrategy = new RotateBySizeStrategy(n*1024*1024);
270 else if (unit.empty())
271 pStrategy = new RotateBySizeStrategy(n);
272 else if (unit != "never")
273 throw InvalidArgumentException("rotation", rotation);
274 delete _pRotateStrategy;
275 _pRotateStrategy = pStrategy;
276 _rotation = rotation;
277}
278
279
280void FileChannel::setArchive(const std::string& archive)
281{
282 ArchiveStrategy* pStrategy = 0;
283 if (archive == "number")
284 {
285 pStrategy = new ArchiveByNumberStrategy;
286 }
287 else if (archive == "timestamp")
288 {
289 if (_times == "utc")
290 pStrategy = new ArchiveByTimestampStrategy<DateTime>;
291 else if (_times == "local")
292 pStrategy = new ArchiveByTimestampStrategy<LocalDateTime>;
293 else
294 throw PropertyNotSupportedException("times", _times);
295 }
296 else throw InvalidArgumentException("archive", archive);
297 delete _pArchiveStrategy;
298 pStrategy->compress(_compress);
299 _pArchiveStrategy = pStrategy;
300 _archive = archive;
301}
302
303
304void FileChannel::setCompress(const std::string& compress)
305{
306 _compress = icompare(compress, "true") == 0;
307 if (_pArchiveStrategy)
308 _pArchiveStrategy->compress(_compress);
309}
310
311
312void FileChannel::setPurgeAge(const std::string& age)
313{
314 if (setNoPurge(age)) return;
315
316 std::string::const_iterator nextToDigit;
317 int num = extractDigit(age, &nextToDigit);
318 Timespan::TimeDiff factor = extractFactor(age, nextToDigit);
319
320 setPurgeStrategy(new PurgeByAgeStrategy(Timespan(num * factor)));
321 _purgeAge = age;
322}
323
324
325void FileChannel::setPurgeCount(const std::string& count)
326{
327 if (setNoPurge(count)) return;
328
329 setPurgeStrategy(new PurgeByCountStrategy(extractDigit(count)));
330 _purgeCount = count;
331}
332
333
334void FileChannel::setFlush(const std::string& flush)
335{
336 _flush = icompare(flush, "true") == 0;
337}
338
339
340void FileChannel::setRotateOnOpen(const std::string& rotateOnOpen)
341{
342 _rotateOnOpen = icompare(rotateOnOpen, "true") == 0;
343}
344
345
346void FileChannel::purge()
347{
348 if (_pPurgeStrategy)
349 {
350 try
351 {
352 _pPurgeStrategy->purge(_path);
353 }
354 catch (...)
355 {
356 }
357 }
358}
359
360
361bool FileChannel::setNoPurge(const std::string& value)
362{
363 if (value.empty() || 0 == icompare(value, "none"))
364 {
365 delete _pPurgeStrategy;
366 _pPurgeStrategy = 0;
367 _purgeAge = "none";
368 return true;
369 }
370 else return false;
371}
372
373
374int FileChannel::extractDigit(const std::string& value, std::string::const_iterator* nextToDigit) const
375{
376 std::string::const_iterator it = value.begin();
377 std::string::const_iterator end = value.end();
378 int digit = 0;
379
380 while (it != end && Ascii::isSpace(*it)) ++it;
381 while (it != end && Ascii::isDigit(*it))
382 {
383 digit *= 10;
384 digit += *it++ - '0';
385 }
386
387 if (digit == 0)
388 throw InvalidArgumentException("Zero is not valid purge age.");
389
390 if (nextToDigit) *nextToDigit = it;
391 return digit;
392}
393
394
395void FileChannel::setPurgeStrategy(PurgeStrategy* strategy)
396{
397 delete _pPurgeStrategy;
398 _pPurgeStrategy = strategy;
399}
400
401
402Timespan::TimeDiff FileChannel::extractFactor(const std::string& value, std::string::const_iterator start) const
403{
404 while (start != value.end() && Ascii::isSpace(*start)) ++start;
405
406 std::string unit;
407 while (start != value.end() && Ascii::isAlpha(*start)) unit += *start++;
408
409 if (unit == "seconds")
410 return Timespan::SECONDS;
411 if (unit == "minutes")
412 return Timespan::MINUTES;
413 else if (unit == "hours")
414 return Timespan::HOURS;
415 else if (unit == "days")
416 return Timespan::DAYS;
417 else if (unit == "weeks")
418 return 7 * Timespan::DAYS;
419 else if (unit == "months")
420 return 30 * Timespan::DAYS;
421 else throw InvalidArgumentException("purgeAge", value);
422
423 return Timespan::TimeDiff();
424}
425
426
427
428} // namespace Poco
429