19 size_t check_interval)
23 , max_files_(max_files)
24 , check_interval_(check_interval)
25 , last_rotation_time_(std::chrono::system_clock::now())
26 , current_period_start_(std::chrono::system_clock::now()) {
29 std::filesystem::path path(filename);
40 size_t check_interval)
42 , rotation_type_(type)
44 , max_files_(max_files)
45 , check_interval_(check_interval)
46 , last_rotation_time_(std::chrono::system_clock::now())
47 , current_period_start_(std::chrono::system_clock::now()) {
50 std::filesystem::path path(filename);
62 size_t check_interval)
64 , rotation_type_(type)
66 , max_files_(max_files)
67 , check_interval_(check_interval)
68 , last_rotation_time_(std::chrono::system_clock::now())
69 , current_period_start_(std::chrono::system_clock::now()) {
72 throw std::invalid_argument(
"This constructor is only for size_and_time rotation");
76 std::filesystem::path path(filename);
85 std::lock_guard<std::mutex> lock(
get_mutex());
127 std::lock_guard<std::mutex> lock(
get_mutex());
163 if (std::filesystem::exists(
filename_)) {
164 std::filesystem::rename(
filename_, rotated_name);
169 if (rename_result.is_err()) {
170 std::cerr <<
"Failed to rotate log file: " << rename_result.error().message << std::endl;
178 std::filesystem::path file_path(
filename_);
179 std::filesystem::path dir = file_path.parent_path();
182 if (dir_result.is_err())
return dir_result;
184 auto mode =
append_mode_ ? std::ios::app : std::ios::trunc;
194 if (open_result.is_err()) {
195 std::cerr <<
"Failed to open new log file: " << open_result.error().message << std::endl;
207 std::ostringstream oss;
208 std::filesystem::path dir = std::filesystem::path(
filename_).parent_path();
211 oss << dir.string() <<
"/";
219 auto now = std::chrono::system_clock::now();
220 auto time_t = std::chrono::system_clock::to_time_t(now);
225 localtime_s(&tm_buf, &time_t);
227 localtime_r(&time_t, &tm_buf);
236 std::string base_with_ext = oss.str();
238 while (std::filesystem::exists(base_with_ext +
"." + std::to_string(next_index))) {
241 oss <<
"." << next_index;
246 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d");
250 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d_%H");
254 oss <<
"." << std::put_time(&tm_buf,
"%Y%m%d_%H%M%S");
266 std::sort(backup_files.begin(), backup_files.end(),
267 [](
const std::string& a,
const std::string& b) {
268 return std::filesystem::last_write_time(a) <
269 std::filesystem::last_write_time(b);
273 size_t files_to_remove = backup_files.size() -
max_files_;
274 for (
size_t i = 0; i < files_to_remove; ++i) {
276 std::filesystem::remove(backup_files[i]);
280 if (remove_result.is_err()) {
281 std::cerr <<
"Failed to remove old log file: " << remove_result.error().message << std::endl;
288 std::vector<std::string> files;
289 std::filesystem::path dir = std::filesystem::path(
filename_).parent_path();
297 std::string escaped_ext = std::regex_replace(
file_extension_, std::regex(R
"(\.)"), R"(\\.)");
298 std::string pattern = base_filename_ + escaped_ext + R"(\.(\d+|\d{8}|\d{8}_\d{2}|\d{8}_\d{6}))";
299 std::regex backup_regex(pattern);
303 for (
const auto& entry : std::filesystem::directory_iterator(dir)) {
304 if (entry.is_regular_file()) {
305 std::string filename = entry.path().filename().string();
306 if (std::regex_match(filename, backup_regex)) {
307 files.push_back(entry.path().string());
315 std::cerr <<
"Error listing backup files: " <<
result.error().message << std::endl;
322 auto now = std::chrono::system_clock::now();
328 auto now_time_t = std::chrono::system_clock::to_time_t(now);
335 localtime_s(&now_tm, &now_time_t);
336 localtime_s(&start_tm, &start_time_t);
338 localtime_r(&now_time_t, &now_tm);
339 localtime_r(&start_time_t, &start_tm);
342 return now_tm.tm_year != start_tm.tm_year ||
343 now_tm.tm_mon != start_tm.tm_mon ||
344 now_tm.tm_mday != start_tm.tm_mday;
350 return duration >= std::chrono::hours(1);
377 std::size_t atomic_size =
bytes_written_.load(std::memory_order_relaxed);
381 #ifdef DEBUG_FILE_SIZE_VALIDATION
383 auto fs_size = std::filesystem::file_size(
filename_, ec);
384 if (!ec && fs_size != atomic_size) {
386 std::cerr <<
"File size mismatch: atomic=" << atomic_size
387 <<
" filesystem=" << fs_size << std::endl;
Core file writer for logging to files.
std::shared_ptr< security::integrity_policy > integrity_policy_
Integrity policy shared with derived writers (e.g. rotating_file_writer).
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.
Tamper-evident log signing policies for writers.
std::string format_signature_suffix(const integrity_policy &policy, const std::string &record)
Format a signature line suitable for appending to a text log record.
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.