diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index d715ebf3..d0d0bd59 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -30,14 +30,38 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( , max_size_(max_size) , max_files_(max_files) { - file_helper_.open(calc_filename(base_filename_, 0)); - current_size_ = file_helper_.size(); // expensive. called only once + init(); + if (rotate_on_open && current_size_ > 0) + { + rotate_(); + } +} + +template +SPDLOG_INLINE rotating_file_sink::rotating_file_sink( + filename_t base_filename, std::size_t max_size, std::size_t max_files, + filename_t compress_extension, std::function compress_callback, bool rotate_on_open) + : base_filename_(std::move(base_filename)) + , max_size_(max_size) + , max_files_(max_files) + , compress_extension_(std::move(compress_extension)) + , compress_callback_(std::move(compress_callback)) +{ + init(); if (rotate_on_open && current_size_ > 0) { rotate_(); + call_compressor_callback(); } } +template +SPDLOG_INLINE void rotating_file_sink::init() +{ + file_helper_.open(calc_filename(base_filename_, 0)); + current_size_ = file_helper_.size(); // expensive. called only once +} + // calc filename according to index and file extension if exists. // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". template @@ -66,12 +90,18 @@ SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &m memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); current_size_ += formatted.size(); - if (current_size_ > max_size_) + const bool needRotation = (current_size_ > max_size_); + if (needRotation) { rotate_(); current_size_ = formatted.size(); } file_helper_.write(formatted); + + if (needRotation) + { + call_compressor_callback(); + } } template @@ -94,11 +124,19 @@ SPDLOG_INLINE void rotating_file_sink::rotate_() for (auto i = max_files_; i > 0; --i) { filename_t src = calc_filename(base_filename_, i - 1); + filename_t target = calc_filename(base_filename_, i); if (!path_exists(src)) { - continue; + src.append(compress_extension_); + if (!path_exists(src)) + { + continue; + } + else + { + target.append(compress_extension_); + } } - filename_t target = calc_filename(base_filename_, i); if (!rename_file_(src, target)) { @@ -127,5 +165,24 @@ SPDLOG_INLINE bool rotating_file_sink::rename_file_(const filename_t &src return details::os::rename(src_filename, target_filename) == 0; } +template +SPDLOG_INLINE void rotating_file_sink::call_compressor_callback() +{ + if (!compress_callback_) + return; + + using details::os::path_exists; + if (max_files_ > 0) + { + // target is log.1.txt + filename_t target = calc_filename(base_filename_, 1); + if (path_exists(target)) + { + // this file has to be compressed + compress_callback_(target); + } + } +} + } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index e1e85a7d..bdcde4f8 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace spdlog { namespace sinks { @@ -23,6 +24,8 @@ class rotating_file_sink final : public base_sink { public: rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false); + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, + filename_t compress_extension, std::function compress_callback, bool rotate_on_open = false); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); @@ -42,11 +45,19 @@ private: // return true on success, false otherwise. bool rename_file_(const filename_t &src_filename, const filename_t &target_filename); + // runs given compress_ + void call_compressor_callback(); + + // Common init called from constructor + void init(); + filename_t base_filename_; std::size_t max_size_; std::size_t max_files_; std::size_t current_size_; details::file_helper file_helper_; + filename_t compress_extension_; + std::function compress_callback_; }; using rotating_file_sink_mt = rotating_file_sink; @@ -65,12 +76,30 @@ inline std::shared_ptr rotating_logger_mt( return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); } +template +inline std::shared_ptr rotating_logger_mt( + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, + const filename_t &compress_extension, std::function compress_callback, bool rotate_on_open = false) +{ + return Factory::template create(logger_name, filename, max_file_size, max_files, + compress_extension, std::move(compress_callback), rotate_on_open); +} + template inline std::shared_ptr rotating_logger_st( const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); } + +template +inline std::shared_ptr rotating_logger_st( + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, + const filename_t &compress_extension, std::function compress_callback, bool rotate_on_open = false) +{ + return Factory::template create(logger_name, filename, max_file_size, max_files, + compress_extension, std::move(compress_callback), rotate_on_open); +} } // namespace spdlog #ifdef SPDLOG_HEADER_ONLY diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index 394b4bb2..4a39a40e 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -96,3 +96,58 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]") auto filename1 = basename + ".1"; REQUIRE(get_filesize(filename1) <= max_size); } + +TEST_CASE("rotating_file_logger_compress_callback", "[rotating_logger]]") +{ + prepare_logdir(); + size_t max_size = 1024 * 10; + std::string basename = "test_logs/rotating_log"; + std::string ext = ".txt"; + std::string logfilename = basename + ext; + std::string compress_ext = ".gz"; + + auto callback = [=](spdlog::filename_t filename){ + // Here could be gzip filename which will rename it to filename.gz + rename(filename.c_str(), std::string(filename + compress_ext).c_str()); + }; + + { + // make an initial logger to create the first output file + auto logger = spdlog::rotating_logger_mt("logger", logfilename, max_size, 2, + compress_ext, callback, true); + for (int i = 0; i < 10; ++i) + { + logger->info("Test message {}", i); + } + // drop causes the logger destructor to be called, which is required so the + // next logger can rename the first output file. + spdlog::drop(logger->name()); + } + + auto logger = spdlog::rotating_logger_mt("logger", logfilename, max_size, 2, + compress_ext, callback, true); + for (int i = 0; i < 10; ++i) + { + logger->info("Test message {}", i); + } + + logger->flush(); + + require_message_count(logfilename, 10); + + for (int i = 0; i < 1024; i++) + { + logger->info("Test message {}", i); + } + + logger->flush(); + REQUIRE(get_filesize(logfilename) <= max_size); + + // test_logs/rotating_log.1.txt.gz + auto filename1 = basename + ".1" + ext + compress_ext; + REQUIRE(get_filesize(filename1) <= max_size); + + // test_logs/rotating_log.2.txt.gz + auto filename2 = basename + ".2" + ext + compress_ext; + REQUIRE(get_filesize(filename2) <= max_size); +}