diff --git a/components/logger/include/logger.hpp b/components/logger/include/logger.hpp index 1132721a4..813e249dc 100644 --- a/components/logger/include/logger.hpp +++ b/components/logger/include/logger.hpp @@ -90,6 +90,8 @@ class Logger { NONE, /**< No verbosity - logger will not print anything. */ }; + using clock_t = std::chrono::steady_clock; /**< The clock type used for rate limiting. */ + /** * @brief Configuration struct for the logger. */ @@ -110,7 +112,7 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit */ explicit Logger(const Config &config) : tag_(config.tag) - , rate_limit_(config.rate_limit) + , rate_limit_(std::chrono::duration_cast(config.rate_limit)) , include_time_(config.include_time) , level_(config.level) {} @@ -135,8 +137,8 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit std::scoped_lock lock(other.tag_mutex_); return std::move(other.tag_); }()) - , rate_limit_(std::move(other.rate_limit_)) - , last_print_(std::move(other.last_print_)) + , rate_limit_(other.rate_limit_) + , last_print_(other.last_print_.load()) , include_time_(other.include_time_.load()) , level_(other.level_.load()) {} @@ -167,8 +169,8 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit if (this != &other) { std::scoped_lock lock(tag_mutex_, other.tag_mutex_); tag_ = std::move(other.tag_); - rate_limit_ = std::move(other.rate_limit_); - last_print_ = std::move(other.last_print_); + rate_limit_ = other.rate_limit_; + last_print_.store(other.last_print_.load()); include_time_ = other.include_time_.load(); level_ = other.level_.load(); } @@ -217,13 +219,17 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit * @param rate_limit The new rate limit. * @note Only calls that have _rate_limited suffixed will be rate limited. */ - void set_rate_limit(const std::chrono::duration rate_limit) { rate_limit_ = rate_limit; } + void set_rate_limit(const std::chrono::duration rate_limit) { + rate_limit_ = std::chrono::duration_cast(rate_limit); + } /** * @brief Get the current rate limit for the logger. * @return The current rate limit. */ - std::chrono::duration get_rate_limit() const { return rate_limit_; } + std::chrono::duration get_rate_limit() const { + return std::chrono::duration_cast>(rate_limit_); + } /** * @brief Format args into string according to format string. From: @@ -329,11 +335,13 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit #if ESPP_LOGGER_DEBUG_ENABLED if (level_ > espp::Logger::Verbosity::DEBUG) return; - if (rate_limit_ > std::chrono::duration::zero()) { - auto now = std::chrono::high_resolution_clock::now(); - if (now - last_print_ < rate_limit_) + if (rate_limit_ > clock_t::duration::zero()) { + const auto now = clock_t::now().time_since_epoch().count(); + // last_print_ is an atomic timestamp (clock_t rep) so the check/update is + // free of data races when logging from multiple threads. + if (clock_t::duration(now - last_print_.load()) < rate_limit_) return; - last_print_ = now; + last_print_.store(now); } // forward the arguments to the debug function debug(rt_fmt_str, std::forward(args)...); @@ -351,11 +359,13 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit #if ESPP_LOGGER_INFO_ENABLED if (level_ > espp::Logger::Verbosity::INFO) return; - if (rate_limit_ > std::chrono::duration::zero()) { - auto now = std::chrono::high_resolution_clock::now(); - if (now - last_print_ < rate_limit_) + if (rate_limit_ > clock_t::duration::zero()) { + const auto now = clock_t::now().time_since_epoch().count(); + // last_print_ is an atomic timestamp (clock_t rep) so the check/update is + // free of data races when logging from multiple threads. + if (clock_t::duration(now - last_print_.load()) < rate_limit_) return; - last_print_ = now; + last_print_.store(now); } // forward the arguments to the info function info(rt_fmt_str, std::forward(args)...); @@ -373,11 +383,13 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit #if ESPP_LOGGER_WARN_ENABLED if (level_ > espp::Logger::Verbosity::WARN) return; - if (rate_limit_ > std::chrono::duration::zero()) { - auto now = std::chrono::high_resolution_clock::now(); - if (now - last_print_ < rate_limit_) + if (rate_limit_ > clock_t::duration::zero()) { + const auto now = clock_t::now().time_since_epoch().count(); + // last_print_ is an atomic timestamp (clock_t rep) so the check/update is + // free of data races when logging from multiple threads. + if (clock_t::duration(now - last_print_.load()) < rate_limit_) return; - last_print_ = now; + last_print_.store(now); } // forward the arguments to the warn function warn(rt_fmt_str, std::forward(args)...); @@ -395,11 +407,13 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit #if ESPP_LOGGER_ERROR_ENABLED if (level_ > espp::Logger::Verbosity::ERROR) return; - if (rate_limit_ > std::chrono::duration::zero()) { - auto now = std::chrono::high_resolution_clock::now(); - if (now - last_print_ < rate_limit_) + if (rate_limit_ > clock_t::duration::zero()) { + const auto now = clock_t::now().time_since_epoch().count(); + // last_print_ is an atomic timestamp (clock_t rep) so the check/update is + // free of data races when logging from multiple threads. + if (clock_t::duration(now - last_print_.load()) < rate_limit_) return; - last_print_ = now; + last_print_.store(now); } // forward the arguments to the error function error(rt_fmt_str, std::forward(args)...); @@ -480,10 +494,10 @@ rate limit. @note Only calls that have _rate_limited suffixed will be rate limit mutable std::mutex tag_mutex_; ///< Mutex for the tag. std::string tag_; ///< Name of the logger to be prepended to all logs. - std::chrono::duration rate_limit_{ - 0.0f}; ///< Rate limit for the logger. If set to 0, no rate limiting will be performed. - std::chrono::high_resolution_clock::time_point - last_print_{}; ///< Last time a log was printed. Used for rate limiting. + clock_t::duration rate_limit_{ + clock_t::duration::zero()}; ///< Rate limit for the logger. If zero, no rate limiting is done. + std::atomic last_print_{ + 0}; ///< Epoch count (clock_t rep) of the last printed log. Used for rate limiting. std::atomic include_time_{true}; ///< Whether to include the time in the log. std::atomic level_ = espp::Logger::Verbosity::WARN; ///< Current verbosity level of the logger.