@@ -383,6 +383,8 @@ class DailyFileSink : public FileSink {
383383protected:
384384 void check_rotation ();
385385 virtual std::string get_date_string ();
386+ void rotate_daily_files ();
387+ void rotate_files_for_date (const std::string& date);
386388
387389 std::filesystem::path get_daily_filename ();
388390 std::filesystem::path get_dated_filename (const std::string& date);
@@ -392,7 +394,6 @@ class DailyFileSink : public FileSink {
392394 std::filesystem::path base_path_;
393395 std::string current_date_;
394396 size_t current_file_size_;
395- size_t current_day_index_;
396397};
397398
398399/* *
@@ -962,23 +963,117 @@ inline std::filesystem::path RotatingFileSink::get_rotated_filename(size_t index
962963inline DailyFileSink::DailyFileSink (const std::filesystem::path& base_path, const RotationConfig& config,
963964 TimestampFormatter::Format timestamp_format, std::string&& name)
964965 : FileSink(base_path, timestamp_format, std::move(name)), config_(config), base_path_(base_path),
965- current_file_size_(0 ), current_day_index_( 0 ) {
966+ current_file_size_(0 ) {
966967 current_date_ = get_date_string ();
967- // Initialize file size if file already exists
968+
969+ // Check if file already exists and is from a previous day
968970 if (std::filesystem::exists (base_path_)) {
969- current_file_size_ = std::filesystem::file_size (base_path_);
971+ auto last_write_time = std::filesystem::last_write_time (base_path_);
972+ auto last_write_time_t = std::chrono::system_clock::to_time_t (
973+ std::chrono::time_point_cast<std::chrono::system_clock::duration>(
974+ last_write_time - std::filesystem::file_time_type::clock::now () + std::chrono::system_clock::now ()
975+ )
976+ );
977+ std::tm* tm_ptr = std::localtime (&last_write_time_t );
978+ if (tm_ptr) {
979+ char date_str[11 ];
980+ std::strftime (date_str, sizeof (date_str), " %Y-%m-%d" , tm_ptr);
981+ std::string file_date = std::string (date_str);
982+
983+ // If the existing file is from a previous day, rotate it
984+ if (file_date != current_date_) {
985+ file_stream_.close ();
986+
987+ // Check if dated file already exists, if so rotate all files for that date
988+ std::filesystem::path dated_file = get_dated_filename (file_date);
989+ if (std::filesystem::exists (dated_file)) {
990+ rotate_files_for_date (file_date);
991+ }
992+
993+ // Now rename current base file to dated filename
994+ std::error_code ec;
995+ std::filesystem::rename (base_path_, dated_file, ec);
996+ if (ec) {
997+ // If rename fails, try copy and remove
998+ std::filesystem::copy_file (base_path_, dated_file, ec);
999+ if (!ec) {
1000+ std::filesystem::remove (base_path_, ec);
1001+ }
1002+ }
1003+
1004+ // Reopen file for today
1005+ file_stream_.open (base_path_, std::ios::out | std::ios::trunc);
1006+ if (!file_stream_) {
1007+ throw std::runtime_error (" Failed to reopen daily log file: " + base_path_.string ());
1008+ }
1009+ current_file_size_ = 0 ;
1010+ } else {
1011+ // File is from today, continue appending
1012+ current_file_size_ = std::filesystem::file_size (base_path_);
1013+ }
1014+ } else {
1015+ // Could not get file date, just continue with current file
1016+ current_file_size_ = std::filesystem::file_size (base_path_);
1017+ }
9701018 }
9711019 // Keep logging to base_path (e.g., daily.log) - FileSink constructor already opened it
9721020}
9731021
9741022inline DailyFileSink::DailyFileSink (const std::filesystem::path& base_path, const RotationConfig& config,
9751023 const std::string& custom_timestamp_format, std::string&& name)
9761024 : FileSink(base_path, custom_timestamp_format, std::move(name)), config_(config), base_path_(base_path),
977- current_file_size_(0 ), current_day_index_( 0 ) {
1025+ current_file_size_(0 ) {
9781026 current_date_ = get_date_string ();
979- // Initialize file size if file already exists
1027+
1028+ // Check if file already exists and is from a previous day
9801029 if (std::filesystem::exists (base_path_)) {
981- current_file_size_ = std::filesystem::file_size (base_path_);
1030+ auto last_write_time = std::filesystem::last_write_time (base_path_);
1031+ auto last_write_time_t = std::chrono::system_clock::to_time_t (
1032+ std::chrono::time_point_cast<std::chrono::system_clock::duration>(
1033+ last_write_time - std::filesystem::file_time_type::clock::now () + std::chrono::system_clock::now ()
1034+ )
1035+ );
1036+ std::tm* tm_ptr = std::localtime (&last_write_time_t );
1037+ if (tm_ptr) {
1038+ char date_str[11 ];
1039+ std::strftime (date_str, sizeof (date_str), " %Y-%m-%d" , tm_ptr);
1040+ std::string file_date = std::string (date_str);
1041+
1042+ // If the existing file is from a previous day, rotate it
1043+ if (file_date != current_date_) {
1044+ file_stream_.close ();
1045+
1046+ // Check if dated file already exists, if so rotate all files for that date
1047+ std::filesystem::path dated_file = get_dated_filename (file_date);
1048+ if (std::filesystem::exists (dated_file)) {
1049+ rotate_files_for_date (file_date);
1050+ }
1051+
1052+ // Now rename current base file to dated filename
1053+ std::error_code ec;
1054+ std::filesystem::rename (base_path_, dated_file, ec);
1055+ if (ec) {
1056+ // If rename fails, try copy and remove
1057+ std::filesystem::copy_file (base_path_, dated_file, ec);
1058+ if (!ec) {
1059+ std::filesystem::remove (base_path_, ec);
1060+ }
1061+ }
1062+
1063+ // Reopen file for today
1064+ file_stream_.open (base_path_, std::ios::out | std::ios::trunc);
1065+ if (!file_stream_) {
1066+ throw std::runtime_error (" Failed to reopen daily log file: " + base_path_.string ());
1067+ }
1068+ current_file_size_ = 0 ;
1069+ } else {
1070+ // File is from today, continue appending
1071+ current_file_size_ = std::filesystem::file_size (base_path_);
1072+ }
1073+ } else {
1074+ // Could not get file date, just continue with current file
1075+ current_file_size_ = std::filesystem::file_size (base_path_);
1076+ }
9821077 }
9831078 // Keep logging to base_path (e.g., daily.log) - FileSink constructor already opened it
9841079}
@@ -1015,9 +1110,6 @@ inline void DailyFileSink::check_rotation() {
10151110 }
10161111 }
10171112
1018- // Reset index for new day
1019- current_day_index_ = 0 ;
1020-
10211113 // Reopen base file for new day's logs
10221114 file_stream_.open (base_path_, std::ios::out | std::ios::trunc); // Start fresh for new day
10231115 if (!file_stream_) {
@@ -1029,36 +1121,81 @@ inline void DailyFileSink::check_rotation() {
10291121 }
10301122
10311123 // Check for size-based rotation
1032- if (current_file_size_ >= config_.max_file_size ) {
1033- // Close current file
1034- file_stream_.close ();
1124+ if (config_.max_file_size && current_file_size_ >= config_.max_file_size ) {
1125+ rotate_daily_files ();
1126+ }
1127+ }
10351128
1036- // Rename current base file to dated indexed filename (e.g., daily.log -> daily_2025-08-24_001.log)
1037- std::filesystem::path old_dated_file = get_dated_indexed_filename (current_date_, current_day_index_);
1038- std::error_code ec;
1039- if (std::filesystem::exists (base_path_)) {
1040- std::filesystem::rename (base_path_, old_dated_file, ec);
1041- if (ec) {
1042- // If rename fails, try copy and remove
1043- std::filesystem::copy_file (base_path_, old_dated_file, ec);
1044- if (!ec) {
1045- std::filesystem::remove (base_path_, ec);
1046- }
1129+ inline void DailyFileSink::rotate_files_for_date (const std::string& date) {
1130+ // Remove the oldest file if it exists and max_files is configured
1131+ // Files are: daily_DATE.log (index 0), daily_DATE_001.log (index 1), ..., daily_DATE_<max_files-1>.log (index max_files-1)
1132+
1133+ auto dated_file = get_dated_filename (date);
1134+
1135+ if (config_.max_files == 1 ) {
1136+ // If max_files is 1, we only keep the dated file (no indexed files)
1137+ // Just remove the old dated file - it will be replaced by the new one
1138+ if (std::filesystem::exists (dated_file)) {
1139+ std::filesystem::remove (dated_file);
1140+ }
1141+ } else if (config_.max_files > 1 ) {
1142+ // Remove the oldest indexed file
1143+ auto oldest_file = get_dated_indexed_filename (date, config_.max_files - 1 );
1144+ if (std::filesystem::exists (oldest_file)) {
1145+ std::filesystem::remove (oldest_file);
1146+ }
1147+
1148+ // Rotate existing indexed files for the given date
1149+ // Start from the highest index and work down to 2
1150+ for (size_t i = config_.max_files - 1 ; i >= 2 ; --i) {
1151+ auto src = get_dated_indexed_filename (date, i - 1 );
1152+ auto dst = get_dated_indexed_filename (date, i);
1153+
1154+ if (std::filesystem::exists (src)) {
1155+ std::filesystem::rename (src, dst);
10471156 }
10481157 }
10491158
1050- // Increment index for next file
1051- ++current_day_index_;
1159+ // Rotate the dated file (without index) to _001.log
1160+ if (std::filesystem::exists (dated_file)) {
1161+ auto indexed_file_1 = get_dated_indexed_filename (date, 1 );
1162+ std::filesystem::rename (dated_file, indexed_file_1);
1163+ }
1164+ }
1165+ }
10521166
1053- // Reopen base file for new logs
1054- file_stream_.open (base_path_, std::ios::out | std::ios::trunc);
1055- if (!file_stream_) {
1056- throw std::runtime_error (" Failed to reopen daily log file: " + base_path_.string ());
1167+ inline void DailyFileSink::rotate_daily_files () {
1168+ file_stream_.close ();
1169+
1170+ // Rotate all existing files for the current date (if any)
1171+ std::filesystem::path dated_file = get_dated_filename (current_date_);
1172+
1173+ // Always rotate if dated file exists, which will shift:
1174+ // daily_2025-10-02.log -> _001.log
1175+ // _001.log -> _002.log, etc.
1176+ if (std::filesystem::exists (dated_file)) {
1177+ rotate_files_for_date (current_date_);
1178+ }
1179+
1180+ // Rename current base file to dated filename (e.g., daily.log -> daily_2025-10-02.log)
1181+ std::error_code ec;
1182+ if (std::filesystem::exists (base_path_)) {
1183+ std::filesystem::rename (base_path_, dated_file, ec);
1184+ if (ec) {
1185+ // If rename fails, try copy and remove
1186+ std::filesystem::copy_file (base_path_, dated_file, ec);
1187+ if (!ec) {
1188+ std::filesystem::remove (base_path_, ec);
1189+ }
10571190 }
1191+ }
10581192
1059- current_file_size_ = 0 ;
1060- return ; // Don't check date rotation if we just did size rotation
1193+ // Create new current file
1194+ file_stream_.open (base_path_, std::ios::out | std::ios::trunc);
1195+ if (!file_stream_) {
1196+ throw std::runtime_error (" Failed to reopen daily log file: " + base_path_.string ());
10611197 }
1198+ current_file_size_ = 0 ;
10621199}
10631200
10641201inline std::filesystem::path DailyFileSink::get_daily_filename () {
0 commit comments