From a441c4ce3de7b8dee452315e9295f3860883ebdf Mon Sep 17 00:00:00 2001 From: fegomes Date: Thu, 25 Jan 2018 02:23:50 -0200 Subject: [PATCH 1/9] implementation custom flag for pattern format --- include/spdlog/details/log_msg.h | 7 ++++-- include/spdlog/details/logger_impl.h | 25 ++++++++++++++++--- .../spdlog/details/pattern_formatter_impl.h | 24 ++++++++++++++++-- include/spdlog/details/registry.h | 23 +++++++++++++++++ include/spdlog/details/spdlog_impl.h | 10 ++++++++ include/spdlog/logger.h | 4 +++ include/spdlog/spdlog.h | 9 +++++++ 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index a9fe9201..850b9a81 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -11,6 +11,7 @@ #include #include +#include namespace spdlog { @@ -19,10 +20,11 @@ namespace details struct log_msg { log_msg() = default; - log_msg(const std::string *loggers_name, level::level_enum lvl) : + log_msg(const std::string *loggers_name, level::level_enum lvl, std::unordered_map* flags) : logger_name(loggers_name), level(lvl), - msg_id(0) + msg_id(0), + custom_flags(flags) { #ifndef SPDLOG_NO_DATETIME time = os::now(); @@ -45,6 +47,7 @@ struct log_msg fmt::MemoryWriter raw; fmt::MemoryWriter formatted; size_t msg_id; + std::unordered_map* custom_flags; }; } } diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index 88044747..bfb39333 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -64,7 +64,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar try { - details::log_msg log_msg(&_name, lvl); + details::log_msg log_msg(&_name, lvl, &_custom_flags); #if defined(SPDLOG_FMT_PRINTF) fmt::printf(log_msg.raw, fmt, args...); @@ -90,7 +90,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg) if (!should_log(lvl)) return; try { - details::log_msg log_msg(&_name, lvl); + details::log_msg log_msg(&_name, lvl, &_custom_flags); log_msg.raw << msg; _sink_it(log_msg); } @@ -111,7 +111,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg) if (!should_log(lvl)) return; try { - details::log_msg log_msg(&_name, lvl); + details::log_msg log_msg(&_name, lvl,&_custom_flags); log_msg.raw << msg; _sink_it(log_msg); } @@ -279,6 +279,25 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) _level.store(log_level); } +inline void spdlog::logger::set_custom_flag(char flag,const std::string& value) +{ + _custom_flags[flag] = value; +} + +inline const std::string& spdlog::logger::value_custom_flag(char flag) +{ + auto end = _custom_flags.end(); + auto it = _custom_flags.find(flag); + if (it != end) + { + return it->second; + } + else + { + return ""; + } +} + inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) { _err_handler = err_handler; diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h index a73f5dea..2dd67863 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter_impl.h @@ -414,6 +414,27 @@ private: char _ch; }; +class custom_formatter SPDLOG_FINAL:public flag_formatter +{ +public: + explicit custom_formatter(char flag) : _flag(_flag) + {} + void format(details::log_msg& msg, const std::tm&) override + { + auto end = msg.custom_flags->end(); + auto it = msg.custom_flags->find(_flag); + if (it != end) + { + msg.formatted << it->second; + } + else { + msg.formatted << _flag; + } + } +private: + char _flag; +}; + //aggregate user chars to display as is class aggregate_formatter SPDLOG_FINAL:public flag_formatter @@ -655,8 +676,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) break; default: //Unknown flag appears as is - _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); - _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); + _formatters.push_back(std::unique_ptr(new details::custom_formatter(flag))); break; } } diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index b68b9f5a..9ded577a 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -155,6 +155,28 @@ public: _level = log_level; } + void set_custom_flag(char flag, const std::string& value) + { + std::lock_guard lock(_mutex); + for (auto& l : _loggers) + l.second->set_custom_flag(flag, value); + _custom_flags[flag] = value; + } + + const std::string& value_custom_flag(char flag) + { + auto end = _custom_flags.end(); + auto it = _custom_flags.find(flag); + if (it != end) + { + return it->second; + } + else + { + return std::move(std::string("")); + } + } + void flush_on(level::level_enum log_level) { std::lock_guard lock(_mutex); @@ -215,6 +237,7 @@ private: std::function _worker_warmup_cb = nullptr; std::chrono::milliseconds _flush_interval_ms; std::function _worker_teardown_cb = nullptr; + std::unordered_map _custom_flags; }; #ifdef SPDLOG_NO_REGISTRY_MUTEX typedef registry_t registry; diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 25c7b7e9..bcbf3014 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -236,6 +236,16 @@ inline void spdlog::set_level(level::level_enum log_level) return details::registry::instance().set_level(log_level); } +inline void spdlog::set_custom_flag(char flag, const std::string& value) +{ + return details::registry::instance().set_custom_flag(flag, value); +} + +inline const std::string& spdlog::value_custom_flag(char flag) +{ + return details::registry::instance().value_custom_flag(flag); +} + inline void spdlog::flush_on(level::level_enum log_level) { return details::registry::instance().flush_on(log_level); diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 742f667f..6f0c50ad 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace spdlog { @@ -66,6 +67,8 @@ public: bool should_log(level::level_enum) const; void set_level(level::level_enum); + void set_custom_flag(char flag, const std::string& value); + const std::string& value_custom_flag(char flag); level::level_enum level() const; const std::string& name() const; void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local); @@ -104,6 +107,7 @@ protected: log_err_handler _err_handler; std::atomic _last_err_time; std::atomic _msg_counter; + std::unordered_map _custom_flags; }; } diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index ff79ef6e..7cf14f2a 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -50,6 +50,15 @@ void flush_on(level::level_enum log_level); // void set_error_handler(log_err_handler); +// +// Set custom flag +// +void set_custom_flag(char flag, const std::string& value); + +// +// value custom flag +// +const std::string& value_custom_flag(char flag); // // Turn on async mode (off by default) and set the queue size for each async_logger. // effective only for loggers created after this call. From 71ef6df872d8ecc8102fb11ecf9cfc24878a0604 Mon Sep 17 00:00:00 2001 From: Fernando Gomes Date: Thu, 25 Jan 2018 21:55:41 -0200 Subject: [PATCH 2/9] missing remove underscore --- include/spdlog/details/pattern_formatter_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h index 2dd67863..f252b227 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter_impl.h @@ -417,7 +417,7 @@ private: class custom_formatter SPDLOG_FINAL:public flag_formatter { public: - explicit custom_formatter(char flag) : _flag(_flag) + explicit custom_formatter(char flag) : _flag(flag) {} void format(details::log_msg& msg, const std::tm&) override { From 31ce7ef3a56ff8b9535d767ae6e927c244923ec0 Mon Sep 17 00:00:00 2001 From: Puasonych Date: Tue, 10 Apr 2018 16:57:29 +0300 Subject: [PATCH 3/9] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b1a41919..be48891f 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,5 @@ install_manifest.txt /tests/logs/* # idea -.idea/ \ No newline at end of file +.idea/ +.vscode/ \ No newline at end of file From 11c99892d7ae38966cde9115d9dcc0865fac65d8 Mon Sep 17 00:00:00 2001 From: Puasonych Date: Wed, 11 Apr 2018 11:40:34 +0300 Subject: [PATCH 4/9] Add new logger: step_logger --- include/spdlog/details/spdlog_impl.h | 13 ++++ include/spdlog/sinks/file_sinks.h | 92 ++++++++++++++++++++++++++++ include/spdlog/spdlog.h | 6 ++ tests/file_log.cpp | 65 ++++++++++++++++++++ 4 files changed, 176 insertions(+) diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 4c363834..7d9195bc 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -83,6 +83,19 @@ inline std::shared_ptr spdlog::daily_logger_st( return create(logger_name, filename, hour, minute); } +// Create a file logger that creates new files with a specified increment +inline std::shared_ptr spdlog::step_logger_mt( + const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size) +{ + return create(logger_name, filename_fmt, seconds, max_file_size); +} + +inline std::shared_ptr spdlog::step_logger_st( + const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size) +{ + return create(logger_name, filename_fmt, seconds, max_file_size); +} + // // stdout/stderr loggers // diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index 109c493a..407c5b83 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -251,5 +251,97 @@ private: using daily_file_sink_mt = daily_file_sink; using daily_file_sink_st = daily_file_sink; +/* + * Default generator of step log file names. + */ +struct default_step_file_name_calculator +{ + // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext + static std::tuple calc_filename(const filename_t &filename) + { + std::tm tm = spdlog::details::os::localtime(); + filename_t basename, ext; + std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return std::make_tuple(w.str(), ext); + } +}; + +/* + * Rotating file sink based on size and a specified time step + */ +template +class step_file_sink SPDLOG_FINAL : public base_sink +{ +public: + step_file_sink(filename_t base_filename, unsigned step_seconds, std::size_t max_size) + : _base_filename(std::move(base_filename)) + , _step_seconds(step_seconds) + , _max_size(max_size) + { + _tp = _next_tp(); + std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename); + _file_helper.open(_current_filename); + _current_size = _file_helper.size(); // expensive. called only once + } + +protected: + void _sink_it(const details::log_msg &msg) override + { + _current_size += msg.formatted.size(); + if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size) + { + close_current_file(); + + std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename); + _file_helper.open(_current_filename); + _tp = _next_tp(); + _current_size = msg.formatted.size(); + } + _file_helper.write(msg); + } + + void _flush() override + { + _file_helper.flush(); + close_current_file(); + } + +private: + std::chrono::system_clock::time_point _next_tp() + { + return std::chrono::system_clock::now() + _step_seconds; + } + + void close_current_file() + { + using details::os::filename_to_str; + + filename_t src =_current_filename; + filename_t target = _current_filename + _ext; + + if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) + { + throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); + } + } + + filename_t _base_filename; + std::chrono::seconds _step_seconds; + std::size_t _max_size; + + std::chrono::system_clock::time_point _tp; + filename_t _current_filename; + filename_t _ext; + std::size_t _current_size; + + details::file_helper _file_helper; +}; + +using step_file_sink_mt = step_file_sink; +using step_file_sink_st = step_file_sink; + } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 21f5951b..f65fe57e 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -91,6 +91,12 @@ std::shared_ptr rotating_logger_st( std::shared_ptr daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); std::shared_ptr daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); +// +// Create a file logger which creates new files with a specified time step and fixed file size: +// +std::shared_ptr step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits::max()); +std::shared_ptr step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits::max()); + // // Create and register stdout/stderr loggers // diff --git a/tests/file_log.cpp b/tests/file_log.cpp index e20071a3..afcac255 100644 --- a/tests/file_log.cpp +++ b/tests/file_log.cpp @@ -179,6 +179,71 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]") REQUIRE(count_lines(filename) == 10); } +TEST_CASE("step_logger", "[step_logger]]") +{ + prepare_logdir(); + // calculate filename (time based) + std::string basename = "logs/step_log"; + std::tm tm = spdlog::details::os::localtime(); + fmt::MemoryWriter w; + w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + auto logger = spdlog::step_logger_mt("logger", basename, 60); + logger->flush_on(spdlog::level::info); + for (int i = 0; i < 10; ++i) + { +#if !defined(SPDLOG_FMT_PRINTF) + logger->info("Test message {}", i); +#else + logger->info("Test message %d", i); +#endif + } + + auto filename = w.str(); + REQUIRE(count_lines(filename) == 10); +} + +struct custom_step_file_name_calculator +{ + static std::tuple calc_filename(const spdlog::filename_t &filename) + { + std::tm tm = spdlog::details::os::localtime(); + spdlog::filename_t basename, ext; + std::tie(basename, ext) = spdlog::details::file_helper::split_by_extenstion(filename); + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + w.write(SPDLOG_FILENAME_T("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return std::make_tuple(w.str(), ext); + } +}; + +TEST_CASE("step_logger with custom calculator", "[step_logger_custom]]") +{ + using sink_type = spdlog::sinks::step_file_sink; + + prepare_logdir(); + + std::string basename = "logs/step_log_custom"; + std::tm tm = spdlog::details::os::localtime(); + fmt::MemoryWriter w; + w.write("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + auto logger = spdlog::create("logger", basename, 60, std::numeric_limits::max()); + for (int i = 0; i < 10; ++i) + { +#if !defined(SPDLOG_FMT_PRINTF) + logger->info("Test message {}", i); +#else + logger->info("Test message %d", i); +#endif + } + + logger->flush(); + auto filename = w.str(); + REQUIRE(count_lines(filename) == 10); +} + /* * File name calculations */ From 6f12242a55238c19e12399d2a41f67ea6924f9c7 Mon Sep 17 00:00:00 2001 From: Puasonych Date: Wed, 11 Apr 2018 11:41:48 +0300 Subject: [PATCH 5/9] Update .gitignore --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index be48891f..2b432ca5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Auto generated files -build/* +build/* *.slo *.lo *.o @@ -65,5 +65,7 @@ install_manifest.txt /tests/logs/* # idea -.idea/ +.idea/ + +# vscode .vscode/ \ No newline at end of file From 06580c8333f81f0cd700bf95de1e7c41ede782c4 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 11 Apr 2018 22:15:50 +0500 Subject: [PATCH 6/9] Update file_sinks.h --- include/spdlog/sinks/file_sinks.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index 407c5b83..13282789 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -286,6 +286,11 @@ public: _file_helper.open(_current_filename); _current_size = _file_helper.size(); // expensive. called only once } + + ~step_file_sink() + { + close_current_file(); + } protected: void _sink_it(const details::log_msg &msg) override @@ -306,7 +311,6 @@ protected: void _flush() override { _file_helper.flush(); - close_current_file(); } private: From be685337b12ece5533df65d78a5bb8a6926c5745 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 12 Apr 2018 10:01:02 +0500 Subject: [PATCH 7/9] Update file_sinks.h Removed possible throw of an exception from the destructor --- include/spdlog/sinks/file_sinks.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index 13282789..73724d23 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -289,7 +289,12 @@ public: ~step_file_sink() { - close_current_file(); + using details::os::filename_to_str; + + filename_t src =_current_filename; + filename_t target = _current_filename + _ext; + + details::os::rename(src, target); } protected: From 1b2f6815bf784ac67defb72966511316b0d6458b Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 16 Apr 2018 10:58:50 +0200 Subject: [PATCH 8/9] Silence warning "value stored to rv is never read" --- tests/utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils.cpp b/tests/utils.cpp index 2c50ce36..5afdb4cf 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -8,6 +8,7 @@ void prepare_logdir() system("del /F /Q logs\\*"); #else auto rv = system("mkdir -p logs"); + (void)rv; rv = system("rm -f logs/*"); (void)rv; #endif From 093edc2dc2385a6488cccd1ad8fa422353ed7991 Mon Sep 17 00:00:00 2001 From: Puasonych Date: Mon, 16 Apr 2018 12:56:45 +0300 Subject: [PATCH 9/9] Update step_logger Test release of a logger step_logger --- include/spdlog/contrib/sinks/step_file_sink.h | 161 ++++++++++++++++++ include/spdlog/details/spdlog_impl.h | 13 -- include/spdlog/sinks/file_sinks.h | 101 ----------- include/spdlog/spdlog.h | 6 - tests/file_log.cpp | 65 ------- 5 files changed, 161 insertions(+), 185 deletions(-) create mode 100644 include/spdlog/contrib/sinks/step_file_sink.h diff --git a/include/spdlog/contrib/sinks/step_file_sink.h b/include/spdlog/contrib/sinks/step_file_sink.h new file mode 100644 index 00000000..6be4d8ef --- /dev/null +++ b/include/spdlog/contrib/sinks/step_file_sink.h @@ -0,0 +1,161 @@ +#pragma once + +#include "../../details/file_helper.h" +#include "../../details/null_mutex.h" +#include "../../fmt/fmt.h" +#include "../../sinks/base_sink.h" + +#include +#include +#include +#include +#include +#include +#include + +// Example for spdlog.h +// +// Create a file logger which creates new files with a specified time step and fixed file size: +// +// std::shared_ptr step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits::max()); +// std::shared_ptr step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits::max();; + +// Example for spdlog_impl.h +// Create a file logger that creates new files with a specified increment +// inline std::shared_ptr spdlog::step_logger_mt( +// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size) +// { +// return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size); +// } + +// inline std::shared_ptr spdlog::step_logger_st( +// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size) +// { +// return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size); +// } + +namespace spdlog { +namespace sinks { + +/* + * Default generator of step log file names. + */ +struct default_step_file_name_calculator +{ + // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext + static std::tuple calc_filename(const filename_t &filename, const filename_t &tmp_ext) + { + std::tm tm = spdlog::details::os::localtime(); + filename_t basename, ext; + std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext); + return std::make_tuple(w.str(), ext); + } +}; + +/* + * Rotating file sink based on size and a specified time step + */ +template +class step_file_sink SPDLOG_FINAL : public base_sink +{ +public: + step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size) + : _base_filename(std::move(base_filename)) + , _tmp_ext(std::move(tmp_ext)) + , _step_seconds(step_seconds) + , _max_size(max_size) + { + if (step_seconds == 0) + { + throw spdlog_ex("step_file_sink: Invalid time step in ctor"); + } + if (max_size == 0) + { + throw spdlog_ex("step_file_sink: Invalid max log size in ctor"); + } + + _tp = _next_tp(); + std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); + + if (_tmp_ext == _ext) + { + throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor"); + } + + _file_helper.open(_current_filename); + _current_size = _file_helper.size(); // expensive. called only once + } + + ~step_file_sink() + { + using details::os::filename_to_str; + + filename_t src =_current_filename, target; + std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src); + target += _ext; + + details::os::rename(src, target); + } + +protected: + void _sink_it(const details::log_msg &msg) override + { + _current_size += msg.formatted.size(); + if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size) + { + close_current_file(); + + std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); + _file_helper.open(_current_filename); + _tp = _next_tp(); + _current_size = msg.formatted.size(); + } + _file_helper.write(msg); + } + + void _flush() override + { + _file_helper.flush(); + } + +private: + std::chrono::system_clock::time_point _next_tp() + { + return std::chrono::system_clock::now() + _step_seconds; + } + + void close_current_file() + { + using details::os::filename_to_str; + + filename_t src =_current_filename, target; + std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src); + target += _ext; + + if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) + { + throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); + } + } + + const filename_t _base_filename; + const filename_t _tmp_ext; + const std::chrono::seconds _step_seconds; + const unsigned _max_size; + + std::chrono::system_clock::time_point _tp; + filename_t _current_filename; + filename_t _ext; + unsigned _current_size; + + details::file_helper _file_helper; +}; + +using step_file_sink_mt = step_file_sink; +using step_file_sink_st = step_file_sink; + +} // namespace sinks +} // namespace spdlog diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 7d9195bc..4c363834 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -83,19 +83,6 @@ inline std::shared_ptr spdlog::daily_logger_st( return create(logger_name, filename, hour, minute); } -// Create a file logger that creates new files with a specified increment -inline std::shared_ptr spdlog::step_logger_mt( - const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size) -{ - return create(logger_name, filename_fmt, seconds, max_file_size); -} - -inline std::shared_ptr spdlog::step_logger_st( - const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size) -{ - return create(logger_name, filename_fmt, seconds, max_file_size); -} - // // stdout/stderr loggers // diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index 73724d23..109c493a 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -251,106 +251,5 @@ private: using daily_file_sink_mt = daily_file_sink; using daily_file_sink_st = daily_file_sink; -/* - * Default generator of step log file names. - */ -struct default_step_file_name_calculator -{ - // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext - static std::tuple calc_filename(const filename_t &filename) - { - std::tm tm = spdlog::details::os::localtime(); - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - return std::make_tuple(w.str(), ext); - } -}; - -/* - * Rotating file sink based on size and a specified time step - */ -template -class step_file_sink SPDLOG_FINAL : public base_sink -{ -public: - step_file_sink(filename_t base_filename, unsigned step_seconds, std::size_t max_size) - : _base_filename(std::move(base_filename)) - , _step_seconds(step_seconds) - , _max_size(max_size) - { - _tp = _next_tp(); - std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename); - _file_helper.open(_current_filename); - _current_size = _file_helper.size(); // expensive. called only once - } - - ~step_file_sink() - { - using details::os::filename_to_str; - - filename_t src =_current_filename; - filename_t target = _current_filename + _ext; - - details::os::rename(src, target); - } - -protected: - void _sink_it(const details::log_msg &msg) override - { - _current_size += msg.formatted.size(); - if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size) - { - close_current_file(); - - std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename); - _file_helper.open(_current_filename); - _tp = _next_tp(); - _current_size = msg.formatted.size(); - } - _file_helper.write(msg); - } - - void _flush() override - { - _file_helper.flush(); - } - -private: - std::chrono::system_clock::time_point _next_tp() - { - return std::chrono::system_clock::now() + _step_seconds; - } - - void close_current_file() - { - using details::os::filename_to_str; - - filename_t src =_current_filename; - filename_t target = _current_filename + _ext; - - if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) - { - throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); - } - } - - filename_t _base_filename; - std::chrono::seconds _step_seconds; - std::size_t _max_size; - - std::chrono::system_clock::time_point _tp; - filename_t _current_filename; - filename_t _ext; - std::size_t _current_size; - - details::file_helper _file_helper; -}; - -using step_file_sink_mt = step_file_sink; -using step_file_sink_st = step_file_sink; - } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index f65fe57e..21f5951b 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -91,12 +91,6 @@ std::shared_ptr rotating_logger_st( std::shared_ptr daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); std::shared_ptr daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); -// -// Create a file logger which creates new files with a specified time step and fixed file size: -// -std::shared_ptr step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits::max()); -std::shared_ptr step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits::max()); - // // Create and register stdout/stderr loggers // diff --git a/tests/file_log.cpp b/tests/file_log.cpp index afcac255..e20071a3 100644 --- a/tests/file_log.cpp +++ b/tests/file_log.cpp @@ -179,71 +179,6 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]") REQUIRE(count_lines(filename) == 10); } -TEST_CASE("step_logger", "[step_logger]]") -{ - prepare_logdir(); - // calculate filename (time based) - std::string basename = "logs/step_log"; - std::tm tm = spdlog::details::os::localtime(); - fmt::MemoryWriter w; - w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - - auto logger = spdlog::step_logger_mt("logger", basename, 60); - logger->flush_on(spdlog::level::info); - for (int i = 0; i < 10; ++i) - { -#if !defined(SPDLOG_FMT_PRINTF) - logger->info("Test message {}", i); -#else - logger->info("Test message %d", i); -#endif - } - - auto filename = w.str(); - REQUIRE(count_lines(filename) == 10); -} - -struct custom_step_file_name_calculator -{ - static std::tuple calc_filename(const spdlog::filename_t &filename) - { - std::tm tm = spdlog::details::os::localtime(); - spdlog::filename_t basename, ext; - std::tie(basename, ext) = spdlog::details::file_helper::split_by_extenstion(filename); - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - w.write(SPDLOG_FILENAME_T("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - return std::make_tuple(w.str(), ext); - } -}; - -TEST_CASE("step_logger with custom calculator", "[step_logger_custom]]") -{ - using sink_type = spdlog::sinks::step_file_sink; - - prepare_logdir(); - - std::string basename = "logs/step_log_custom"; - std::tm tm = spdlog::details::os::localtime(); - fmt::MemoryWriter w; - w.write("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - - auto logger = spdlog::create("logger", basename, 60, std::numeric_limits::max()); - for (int i = 0; i < 10; ++i) - { -#if !defined(SPDLOG_FMT_PRINTF) - logger->info("Test message {}", i); -#else - logger->info("Test message %d", i); -#endif - } - - logger->flush(); - auto filename = w.str(); - REQUIRE(count_lines(filename) == 10); -} - /* * File name calculations */