16
16
#include < trantor/utils/Utilities.h>
17
17
#ifndef _WIN32
18
18
#include < unistd.h>
19
+ #include < dirent.h>
20
+ #include < sys/stat.h>
19
21
#ifdef __linux__
20
22
#include < sys/prctl.h>
21
23
#endif
22
24
#else
23
25
#include < Windows.h>
24
26
#endif
25
27
#include < string.h>
28
+ #include < algorithm>
26
29
#include < iostream>
27
30
#include < functional>
28
31
#include < chrono>
@@ -119,8 +122,12 @@ void AsyncFileLogger::writeLogToFile(const StringPtr buf)
119
122
{
120
123
if (!loggerFilePtr_)
121
124
{
122
- loggerFilePtr_ = std::unique_ptr<LoggerFile>(new LoggerFile (
123
- filePath_, fileBaseName_, fileExtName_, switchOnLimitOnly_));
125
+ loggerFilePtr_ =
126
+ std::unique_ptr<LoggerFile>(new LoggerFile (filePath_,
127
+ fileBaseName_,
128
+ fileExtName_,
129
+ switchOnLimitOnly_,
130
+ maxFiles_));
124
131
}
125
132
loggerFilePtr_->writeLog (buf);
126
133
if (loggerFilePtr_->getLength () > sizeLimit_)
@@ -178,14 +185,21 @@ void AsyncFileLogger::startLogging()
178
185
AsyncFileLogger::LoggerFile::LoggerFile (const std::string &filePath,
179
186
const std::string &fileBaseName,
180
187
const std::string &fileExtName,
181
- bool switchOnLimitOnly)
188
+ bool switchOnLimitOnly,
189
+ size_t maxFiles)
182
190
: creationDate_(Date::date()),
183
191
filePath_(filePath),
184
192
fileBaseName_(fileBaseName),
185
193
fileExtName_(fileExtName),
186
- switchOnLimitOnly_(switchOnLimitOnly)
194
+ switchOnLimitOnly_(switchOnLimitOnly),
195
+ maxFiles_(maxFiles)
187
196
{
188
197
open ();
198
+
199
+ if (maxFiles_ > 0 )
200
+ {
201
+ initFilenameQueue ();
202
+ }
189
203
}
190
204
191
205
/* *
@@ -252,6 +266,7 @@ void AsyncFileLogger::LoggerFile::switchLog(bool openNewOne)
252
266
" .%06llu" ,
253
267
static_cast <long long unsigned int >(fileSeq_ % 1000000 ));
254
268
++fileSeq_;
269
+ // NOTE: Remember to update initFilenameQueue() if name format changes
255
270
std::string newName =
256
271
filePath_ + fileBaseName_ + " ." +
257
272
creationDate_.toCustomedFormattedString (" %y%m%d-%H%M%S" ) +
@@ -264,6 +279,14 @@ void AsyncFileLogger::LoggerFile::switchLog(bool openNewOne)
264
279
auto wNewName{utils::toNativePath (newName)};
265
280
_wrename (wFullName.c_str (), wNewName.c_str ());
266
281
#endif
282
+ if (maxFiles_ > 0 )
283
+ {
284
+ filenameQueue_.push_back (newName);
285
+ if (filenameQueue_.size () > maxFiles_)
286
+ {
287
+ deleteOldFiles ();
288
+ }
289
+ }
267
290
if (openNewOne)
268
291
open (); // continue logging with base name until next renaming will
269
292
// be required
@@ -278,6 +301,100 @@ AsyncFileLogger::LoggerFile::~LoggerFile()
278
301
fclose (fp_);
279
302
}
280
303
304
+ void AsyncFileLogger::LoggerFile::initFilenameQueue ()
305
+ {
306
+ if (maxFiles_ <= 0 )
307
+ {
308
+ return ;
309
+ }
310
+
311
+ // walk through the directory and file all files
312
+ #if !defined(_WIN32) || defined(__MINGW32__)
313
+ DIR *dp;
314
+ struct dirent *dirp;
315
+ struct stat st;
316
+
317
+ if ((dp = opendir (filePath_.c_str ())) == nullptr )
318
+ {
319
+ fprintf (stderr,
320
+ " Can't open dir %s: %s\n " ,
321
+ filePath_.c_str (),
322
+ strerror_tl (errno));
323
+ return ;
324
+ }
325
+
326
+ while ((dirp = readdir (dp)) != nullptr )
327
+ {
328
+ std::string name = dirp->d_name ;
329
+ // <base>.yymmdd-hhmmss.000000<ext>
330
+ // NOTE: magic number 21: the length of middle part of generated name
331
+ if (name.size () != fileBaseName_.size () + 21 + fileExtName_.size () ||
332
+ name.compare (0 , fileBaseName_.size (), fileBaseName_) != 0 ||
333
+ name.compare (name.size () - fileExtName_.size (),
334
+ fileExtName_.size (),
335
+ fileExtName_) != 0 )
336
+ {
337
+ continue ;
338
+ }
339
+ std::string fullname = filePath_ + name;
340
+ if (stat (fullname.c_str (), &st) == -1 )
341
+ {
342
+ fprintf (stderr,
343
+ " Can't stat file %s: %s\n " ,
344
+ fullname.c_str (),
345
+ strerror_tl (errno));
346
+ continue ;
347
+ }
348
+ if (!S_ISREG (st.st_mode ))
349
+ {
350
+ continue ;
351
+ }
352
+ filenameQueue_.push_back (fullname);
353
+ std::push_heap (filenameQueue_.begin (),
354
+ filenameQueue_.end (),
355
+ std::greater<>());
356
+ if (filenameQueue_.size () > maxFiles_)
357
+ {
358
+ std::pop_heap (filenameQueue_.begin (),
359
+ filenameQueue_.end (),
360
+ std::greater<>());
361
+ auto fileToRemove = std::move (filenameQueue_.back ());
362
+ filenameQueue_.pop_back ();
363
+ remove (fileToRemove.c_str ());
364
+ }
365
+ }
366
+ closedir (dp);
367
+ #else
368
+ // TODO: windows implementation
369
+ #endif
370
+
371
+ std::sort (filenameQueue_.begin (), filenameQueue_.end (), std::less<>());
372
+ }
373
+
374
+ void AsyncFileLogger::LoggerFile::deleteOldFiles ()
375
+ {
376
+ while (filenameQueue_.size () > maxFiles_)
377
+ {
378
+ std::string filename = std::move (filenameQueue_.front ());
379
+ filenameQueue_.pop_front ();
380
+
381
+ #if !defined(_WIN32) || defined(__MINGW32__)
382
+ int r = remove (filename.c_str ());
383
+ #else
384
+ // Convert UTF-8 file to UCS-2
385
+ auto wName{utils::toNativePath (filename)};
386
+ int r = _wremove (wName.c_str ());
387
+ #endif
388
+ if (r != 0 )
389
+ {
390
+ fprintf (stderr,
391
+ " Failed to remove file %s: %s\n " ,
392
+ filename.c_str (),
393
+ strerror_tl (errno));
394
+ }
395
+ }
396
+ }
397
+
281
398
void AsyncFileLogger::swapBuffer ()
282
399
{
283
400
writeBuffers_.push (logBufferPtr_);
0 commit comments