diff --git a/libminifi/include/utils/TimeUtil.h b/libminifi/include/utils/TimeUtil.h index 4159f089a6..f167865e4b 100644 --- a/libminifi/include/utils/TimeUtil.h +++ b/libminifi/include/utils/TimeUtil.h @@ -126,6 +126,8 @@ inline std::optional parseDateTimeStr(const std::strin return tp; } +std::string humanReadableDuration(std::chrono::system_clock::duration input_duration); + std::optional parseRfc3339(const std::string& str); inline std::string getDateTimeStr(std::chrono::sys_seconds tp) { diff --git a/libminifi/src/utils/TimeUtil.cpp b/libminifi/src/utils/TimeUtil.cpp index 4e9be88dac..b773293d1b 100644 --- a/libminifi/src/utils/TimeUtil.cpp +++ b/libminifi/src/utils/TimeUtil.cpp @@ -16,6 +16,9 @@ */ #include "utils/TimeUtil.h" + +#include "fmt/format.h" +#include "fmt/chrono.h" #include "range/v3/algorithm/contains.hpp" #ifdef WIN32 @@ -39,6 +42,60 @@ void setClock(std::shared_ptr clock) { global_clock = std::move(clock); } +namespace { +template +std::tuple breakDownDurations(std::chrono::system_clock::duration input_duration) { + std::tuple result; + + ([&](T& duration) { + duration = std::chrono::duration_cast>(input_duration); + input_duration -= duration; + } (std::get(result)), ...); + + return result; +} + +std::string formatAsDaysHoursMinutesSeconds(std::chrono::system_clock::duration input_duration) { + const auto durs = breakDownDurations(input_duration); + const auto& days = std::get(durs); + std::string day_string; + if (days.count() > 0) { + day_string = fmt::format("{} {}", days.count(), days.count() == 1 ? "day, " : "days, "); + } + return fmt::format("{}{:02}:{:02}:{:02}", + day_string, std::get(durs).count(), + std::get(durs).count(), + std::get(durs).count()); +} + +template +std::string formatAsRoundedLargestUnit(std::chrono::system_clock::duration input_duration) { + std::optional rounded_value_str; + using std::chrono::duration_cast; + using std::chrono::duration; + + ((rounded_value_str = input_duration >= Durations(1) + ? std::optional{fmt::format("{:.2}", duration_cast>(input_duration))} + : std::nullopt) || ...); + + + if (!rounded_value_str) { + return fmt::format("{}", input_duration); + } + + return *rounded_value_str; +} + +} // namespace + +std::string humanReadableDuration(std::chrono::system_clock::duration input_duration) { + if (input_duration > 5s) { + return formatAsDaysHoursMinutesSeconds(input_duration); + } + + return formatAsRoundedLargestUnit(input_duration); +} + std::optional parseRfc3339(const std::string& str) { std::istringstream stream(str); date::year_month_day date_part{}; diff --git a/libminifi/test/unit/TimeUtilTests.cpp b/libminifi/test/unit/TimeUtilTests.cpp index e67a0c00bf..a3baf3b42d 100644 --- a/libminifi/test/unit/TimeUtilTests.cpp +++ b/libminifi/test/unit/TimeUtilTests.cpp @@ -342,3 +342,17 @@ TEST_CASE("Parse RFC3339", "[parseRfc3339]") { CHECK_FALSE(parseRfc3339(" 2023-03-01T19:04:55Z")); CHECK_FALSE(parseRfc3339("2023-03-01")); } + +TEST_CASE("Test human readable parser") { + using utils::timeutils::humanReadableDuration; + CHECK(humanReadableDuration(1234567us) == "1.23s"); + const auto nine_hundred_forty_five_microseconds = humanReadableDuration(945us); + CHECK((nine_hundred_forty_five_microseconds == "945.00µs" || nine_hundred_forty_five_microseconds == "945.00us")); + CHECK(humanReadableDuration(52ms) == "52.00ms"); + + CHECK(humanReadableDuration(1000s) == "00:16:40"); + CHECK(humanReadableDuration(10000s) == "02:46:40"); + CHECK(humanReadableDuration(2222222222ms) == "25 days, 17:17:02"); + CHECK(humanReadableDuration(24h) == "1 day, 00:00:00"); + CHECK(humanReadableDuration(23h + 12min + 1233ms) == "23:12:01"); +} diff --git a/minifi_main/MiNiFiMain.cpp b/minifi_main/MiNiFiMain.cpp index cf15f5633a..f2b66df40f 100644 --- a/minifi_main/MiNiFiMain.cpp +++ b/minifi_main/MiNiFiMain.cpp @@ -223,7 +223,9 @@ int main(int argc, char **argv) { auto& logger_configuration = core::logging::LoggerConfiguration::getConfiguration(); const auto logger = logger_configuration.getLogger("main"); auto startup_timepoint = std::chrono::system_clock::now(); - auto log_runtime = gsl::finally([&](){logger->log_info("Runtime was {}", std::chrono::system_clock::now()-startup_timepoint);}); + auto log_runtime = gsl::finally([&]() { + logger->log_info("Runtime was {}", utils::timeutils::humanReadableDuration(std::chrono::system_clock::now()-startup_timepoint)); + }); #ifdef WIN32 if (isStartedByService) { if (!CreateServiceTerminationThread(logger, terminationEventHandler)) {