18 size_t check_interval)
22 , max_files_(max_files)
23 , check_interval_(check_interval)
24 , last_rotation_time_(std::chrono::system_clock::now())
25 , current_period_start_(std::chrono::system_clock::now()) {
28 std::filesystem::path path(filename);
39 size_t check_interval)
41 , rotation_type_(type)
43 , max_files_(max_files)
44 , check_interval_(check_interval)
45 , last_rotation_time_(std::chrono::system_clock::now())
46 , current_period_start_(std::chrono::system_clock::now()) {
49 std::filesystem::path path(filename);
61 size_t check_interval)
63 , rotation_type_(type)
65 , max_files_(max_files)
66 , check_interval_(check_interval)
67 , last_rotation_time_(std::chrono::system_clock::now())
68 , current_period_start_(std::chrono::system_clock::now()) {
71 throw std::invalid_argument(
"This constructor is only for size_and_time rotation");
75 std::filesystem::path path(filename);
84 std::lock_guard<std::mutex> lock(
get_mutex());
116 std::lock_guard<std::mutex> lock(
get_mutex());
152 if (std::filesystem::exists(
filename_)) {
153 std::filesystem::rename(
filename_, rotated_name);
158 if (rename_result.is_err()) {
159 std::cerr <<
"Failed to rotate log file: " << rename_result.error().message << std::endl;
167 std::filesystem::path file_path(
filename_);
168 std::filesystem::path dir = file_path.parent_path();
171 if (dir_result.is_err())
return dir_result;
173 auto mode =
append_mode_ ? std::ios::app : std::ios::trunc;
183 if (open_result.is_err()) {
184 std::cerr <<
"Failed to open new log file: " << open_result.error().message << std::endl;
196 std::ostringstream oss;
197 std::filesystem::path dir = std::filesystem::path(
filename_).parent_path();
200 oss << dir.string() <<
"/";
208 auto now = std::chrono::system_clock::now();
209 auto time_t = std::chrono::system_clock::to_time_t(now);
214 localtime_s(&tm_buf, &time_t);
216 localtime_r(&time_t, &tm_buf);
225 std::string base_with_ext = oss.str();
227 while (std::filesystem::exists(base_with_ext +
"." + std::to_string(next_index))) {
230 oss <<
"." << next_index;
235 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d");
239 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d_%H");
243 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d_%H%M%S");
255 std::sort(backup_files.begin(), backup_files.end(),
256 [](
const std::string& a,
const std::string& b) {
257 return std::filesystem::last_write_time(a) <
258 std::filesystem::last_write_time(b);
262 size_t files_to_remove = backup_files.size() -
max_files_;
263 for (
size_t i = 0; i < files_to_remove; ++i) {
265 std::filesystem::remove(backup_files[i]);
269 if (remove_result.is_err()) {
270 std::cerr <<
"Failed to remove old log file: " << remove_result.error().message << std::endl;
277 std::vector<std::string> files;
278 std::filesystem::path dir = std::filesystem::path(
filename_).parent_path();
286 std::string escaped_ext = std::regex_replace(
file_extension_, std::regex(R
"(\.)"), R"(\\.)");
287 std::string pattern = base_filename_ + escaped_ext + R"(\.(\d+|\d{8}|\d{8}_\d{2}|\d{8}_\d{6}))";
288 std::regex backup_regex(pattern);
292 for (
const auto& entry : std::filesystem::directory_iterator(dir)) {
293 if (entry.is_regular_file()) {
294 std::string filename = entry.path().filename().string();
295 if (std::regex_match(filename, backup_regex)) {
296 files.push_back(entry.path().string());
304 std::cerr <<
"Error listing backup files: " <<
result.error().message << std::endl;
311 auto now = std::chrono::system_clock::now();
317 auto now_time_t = std::chrono::system_clock::to_time_t(now);
324 localtime_s(&now_tm, &now_time_t);
325 localtime_s(&start_tm, &start_time_t);
327 localtime_r(&now_time_t, &now_tm);
328 localtime_r(&start_time_t, &start_tm);
331 return now_tm.tm_year != start_tm.tm_year ||
332 now_tm.tm_mon != start_tm.tm_mon ||
333 now_tm.tm_mday != start_tm.tm_mday;
339 return duration >= std::chrono::hours(1);
366 std::size_t atomic_size =
bytes_written_.load(std::memory_order_relaxed);
370 #ifdef DEBUG_FILE_SIZE_VALIDATION
372 auto fs_size = std::filesystem::file_size(
filename_, ec);
373 if (!ec && fs_size != atomic_size) {
375 std::cerr <<
"File size mismatch: atomic=" << atomic_size
376 <<
" filesystem=" << fs_size << std::endl;
Core file writer for logging to files.
std::atomic< size_t > bytes_written_
std::string format_entry(const log_entry &entry) const
Format a log entry using the current formatter.
std::ofstream file_stream_
std::mutex & get_mutex() const
Access the writer mutex for extended operations.
std::chrono::system_clock::time_point current_period_start_
void rotate()
Manually trigger log rotation (thread-safe)
bool should_rotate_by_time() const
Check if time-based rotation should occur.
bool should_rotate() const
Check if rotation should occur.
rotating_file_writer(const std::string &filename, size_t max_size, size_t max_files, size_t check_interval=100)
Construct with size-based rotation.
size_t writes_since_check_
Counter for writes since last check.
rotation_type rotation_type_
std::vector< std::string > get_backup_files() const
Get list of existing backup files.
void perform_rotation()
Perform the actual rotation operation.
void cleanup_old_files()
Remove old backup files beyond max_files limit.
std::chrono::system_clock::time_point last_rotation_time_
common::VoidResult write(const log_entry &entry) override
Write operation with automatic rotation check.
std::string generate_rotated_filename(int index=-1) const
Generate filename for rotated log.
size_t check_interval_
Number of writes between rotation checks.
std::string base_filename_
std::string file_extension_
std::size_t get_file_size() const
Get current file size from filesystem.
Structured error context for debugging log system failures.
common::VoidResult try_open_operation(F &&operation)
Error handling helper for file open operations.
common::VoidResult try_write_operation(F &&operation, logger_error_code default_error_code=logger_error_code::file_write_failed)
Error handling helper for write operations.
common::VoidResult ensure_directory_exists(const std::filesystem::path &dir)
Directory creation helper.
common::VoidResult make_logger_void_result(logger_error_code code, const std::string &message="")
rotation_type
Determines when log rotation should occur.
@ hourly
Rotate every hour.
@ size_and_time
Rotate based on both size and time.
@ daily
Rotate daily at midnight.
@ size
Rotate based on file size only.
Rotating file writer with size and time-based rotation.
Represents a single log entry with all associated metadata.