delete old files on init

pull/1567/head
Ali Alamiri 5 years ago
parent 1a1a7e05eb
commit 4539b16633

@ -17,6 +17,7 @@
#include <ctime> #include <ctime>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <map>
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
@ -58,6 +59,26 @@ struct daily_filename_calculator
return SPDLOG_FILENAME_T(""); return SPDLOG_FILENAME_T("");
} }
static std::map<filename_t, filename_t> calc_dates_to_filenames(const filename_t &base_filename)
{
const filename_t dir = details::os::dir_name(base_filename);
const std::vector<filename_t> dir_files = details::os::get_directory_files(dir);
// lexicographical order ensures files are sorted by created date.
std::map<filename_t, filename_t> dates_to_filenames;
for(const filename_t& dir_file : dir_files)
{
const filename_t date_suffix = daily_filename_calculator::extract_date_suffix(base_filename, dir_file);
if(!date_suffix.empty())
{
dates_to_filenames[date_suffix] = dir_file;
}
}
return dates_to_filenames;
}
static filename_t filename_prefix_symbol() static filename_t filename_prefix_symbol()
{ {
return SPDLOG_FILENAME_T("_"); return SPDLOG_FILENAME_T("_");
@ -74,7 +95,9 @@ class daily_file_sink final : public base_sink<Mutex>
{ {
public: public:
// create daily file sink which rotates on given time // create daily file sink which rotates on given time
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0) // initial_file_tp - useful for testing especially when we want to verify the delete_old_files_on_init behaviour
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, bool delete_old_files_on_init = false,
log_clock::time_point initial_file_tp = log_clock::now())
: base_filename_(std::move(base_filename)) : base_filename_(std::move(base_filename))
, rotation_h_(rotation_hour) , rotation_h_(rotation_hour)
, rotation_m_(rotation_minute) , rotation_m_(rotation_minute)
@ -87,14 +110,13 @@ public:
throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
} }
auto now = log_clock::now(); auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(initial_file_tp));
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
file_helper_.open(filename, truncate_); file_helper_.open(filename, truncate_);
rotation_tp_ = next_rotation_tp_(); rotation_tp_ = next_rotation_tp_();
if (max_files_ > 0) if (max_files_ > 0)
{ {
init_filenames_q_(); init_filenames_q_(delete_old_files_on_init);
} }
} }
@ -132,26 +154,45 @@ protected:
} }
private: private:
void init_filenames_q_() void init_filenames_q_(bool delete_old_file_on_init)
{ {
using details::os::path_exists; using details::os::path_exists;
using details::os::remove_if_exists;
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_)); filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
std::vector<filename_t> filenames;
auto now = log_clock::now(); // Because the map key is the date in yyyy-mm--dd, it is sorted by lexicographical order.
while (filenames.size() < max_files_) const std::map<filename_t, filename_t> dates_to_filenames = daily_filename_calculator::calc_dates_to_filenames(base_filename_);
const auto first_valid_file_pos = max_files_ > 0 && dates_to_filenames.size() > max_files_ ? dates_to_filenames.size() - max_files_ : 0;
auto first_valid_file_iter = dates_to_filenames.begin();
if(first_valid_file_pos > 0)
{
std::advance(first_valid_file_iter, first_valid_file_pos);
}
std::vector<filename_t> recent_files;
for(auto iter = first_valid_file_iter; iter != dates_to_filenames.end(); ++iter)
{ {
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now)); recent_files.emplace_back(iter->second);
if (!path_exists(filename)) }
for (auto iter = recent_files.begin(); iter != recent_files.end(); ++iter)
{
if(path_exists(*iter))
{ {
break; filenames_q_.push_back(std::move(*iter));
} }
filenames.emplace_back(filename);
now -= std::chrono::hours(24);
} }
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
if(max_files_ > 0 && delete_old_file_on_init)
{ {
filenames_q_.push_back(std::move(*iter)); for(auto iter = dates_to_filenames.begin(); iter != first_valid_file_iter; ++iter)
{
remove_if_exists(iter->second);
}
} }
} }
@ -218,15 +259,17 @@ using daily_file_sink_st = daily_file_sink<details::null_mutex>;
// //
template<typename Factory = spdlog::synchronous_factory> template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_mt( inline std::shared_ptr<logger> daily_logger_mt(
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, bool delete_old_files_on_init = false,
log_clock::time_point initial_file_tp = log_clock::now())
{ {
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files); return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files, delete_old_files_on_init, initial_file_tp);
} }
template<typename Factory = spdlog::synchronous_factory> template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_st( inline std::shared_ptr<logger> daily_logger_st(
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, bool delete_old_files_on_init = false,
log_clock::time_point initial_file_tp = log_clock::now())
{ {
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files); return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, delete_old_files_on_init, initial_file_tp);
} }
} // namespace spdlog } // namespace spdlog

@ -168,4 +168,34 @@ TEST_CASE("daily_logger rotate", "[daily_file_sink]")
test_rotate(days_to_run, 10, 10); test_rotate(days_to_run, 10, 10);
test_rotate(days_to_run, 11, 10); test_rotate(days_to_run, 11, 10);
test_rotate(days_to_run, 20, 10); test_rotate(days_to_run, 20, 10);
}
TEST_CASE("daily_logger should delete oldest file on init", "[daily_file_sink]")
{
using spdlog::log_clock;
using spdlog::details::log_msg;
using spdlog::sinks::daily_file_sink_st;
prepare_logdir();
std::string basename = "test_logs/daily_rotate.txt";
daily_file_sink_st sink{basename, 2, 30, false, 8, true};
// simulate messages with 24 intervals. create more than max files of second sink below
for (int i = 0; i < 8; i++)
{
auto offset = std::chrono::seconds{24 * 3600 * i};
sink.log(create_msg(offset));
}
REQUIRE(count_files("test_logs") == static_cast<size_t>(8));
// This will create a 9th file in logdir, therefore the ctor wil delete the oldest two files.
auto initial_file_tp = log_clock::now() + std::chrono::seconds{24 * 3600 * 8};
// Second sink should load existing files in the logdir and delete the oldest because the max files count is 7
daily_file_sink_st sink2{basename, 2, 30, false, 7, true, initial_file_tp};
REQUIRE(count_files("test_logs") == static_cast<size_t>(7));
} }
Loading…
Cancel
Save