This is an automated email from the ASF dual-hosted git repository.

szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit ef27b60bdcfb1bb28064f89f8a0e4a6b0d3476f9
Author: Martin Zink <[email protected]>
AuthorDate: Sun Dec 29 21:00:10 2024 +0100

    MINIFICPP-2500 Log runtime as human readable duration
    
    Closes #1907
    
    Signed-off-by: Marton Szasz <[email protected]>
---
 libminifi/include/utils/TimeUtil.h    |  2 ++
 libminifi/src/utils/TimeUtil.cpp      | 57 +++++++++++++++++++++++++++++++++++
 libminifi/test/unit/TimeUtilTests.cpp | 14 +++++++++
 minifi_main/MiNiFiMain.cpp            |  4 ++-
 4 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/libminifi/include/utils/TimeUtil.h 
b/libminifi/include/utils/TimeUtil.h
index 4159f089a..f167865e4 100644
--- a/libminifi/include/utils/TimeUtil.h
+++ b/libminifi/include/utils/TimeUtil.h
@@ -126,6 +126,8 @@ inline std::optional<std::chrono::sys_seconds> 
parseDateTimeStr(const std::strin
   return tp;
 }
 
+std::string humanReadableDuration(std::chrono::system_clock::duration 
input_duration);
+
 std::optional<std::chrono::system_clock::time_point> 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 4e9be88da..b773293d1 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<SteadyClock> clock) {
   global_clock = std::move(clock);
 }
 
+namespace {
+template<class... Durations>
+std::tuple<Durations...> 
breakDownDurations(std::chrono::system_clock::duration input_duration) {
+  std::tuple<Durations...> result;
+
+  ([&]<typename T>(T& duration) {
+      duration = std::chrono::duration_cast<std::decay_t<T>>(input_duration);
+      input_duration -= duration;
+    } (std::get<Durations>(result)), ...);
+
+  return result;
+}
+
+std::string 
formatAsDaysHoursMinutesSeconds(std::chrono::system_clock::duration 
input_duration) {
+  const auto durs = breakDownDurations<std::chrono::days, std::chrono::hours, 
std::chrono::minutes, std::chrono::seconds>(input_duration);
+  const auto& days = std::get<std::chrono::days>(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<std::chrono::hours>(durs).count(),
+      std::get<std::chrono::minutes>(durs).count(),
+      std::get<std::chrono::seconds>(durs).count());
+}
+
+template<class... Durations>
+std::string formatAsRoundedLargestUnit(std::chrono::system_clock::duration 
input_duration) {
+  std::optional<std::string> rounded_value_str;
+  using std::chrono::duration_cast;
+  using std::chrono::duration;
+
+  ((rounded_value_str = input_duration >= Durations(1)
+    ? std::optional<std::string>{fmt::format("{:.2}", 
duration_cast<duration<double, typename Durations::period>>(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<std::chrono::seconds, 
std::chrono::milliseconds, std::chrono::microseconds>(input_duration);
+}
+
 std::optional<std::chrono::system_clock::time_point> 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 e67a0c00b..a3baf3b42 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 cf15f5633..f2b66df40 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)) {

Reply via email to