From 8224f2b067ea91dbcdb79c0df49f06f0c6dbc0ac Mon Sep 17 00:00:00 2001
From: Remy Prechelt <prechelt@hawaii.edu>
Date: Mon, 20 Jul 2020 13:00:02 -1000
Subject: [PATCH] Added SetLevel, -DDEBUG, and AddSourceInfo.

---
 CMakeLists.txt                   |   3 +
 Framework/Logging/Logging.h      | 113 ++++++++++++++++++++++++++++---
 Framework/Logging/testLogging.cc |  41 ++++++++---
 ThirdParty/spdlog                |   2 +-
 4 files changed, 138 insertions(+), 21 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9b2a52509..7e0d2ca7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,6 +79,9 @@ set (CMAKE_SHARED_LINKER_FLAGS_COVERAGE "--coverage")
 # clang produces a lot of unecessary warnings without this:
 add_compile_options ("$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wno-nonportable-include-path>")
 
+# set a flag to inform code that we are in debug mode
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
+
 # COAST - interface, this requires CORSIKA7 to be installed first
 # COAST will allow you to program code in CORSIKA8 and execute it inside CORSIKA7
 if (WITH_COAST)
diff --git a/Framework/Logging/Logging.h b/Framework/Logging/Logging.h
index a06756e05..21664424d 100644
--- a/Framework/Logging/Logging.h
+++ b/Framework/Logging/Logging.h
@@ -7,25 +7,63 @@
  */
 
 /**
-   @File Logging.h
-
-   CORSIKA8 logging utilities.
+ *  @File Logging.h
+ *
+ * CORSIKA8 logging utilities.
+ *
+ * See testLogging.cc for a complete set of examples for
+ * how the logging functions should be used.
  */
 
 #pragma once
 
+// Configure some behaviour of sdlog.
+// This must be done before spdlog is included.
+
+// use the coarse system clock. This is *much* faster
+// but introduces a real time error of O(10 ms).
+#define SPDLOG_CLOCK_COARSE
+
+// do not create a default logger (we provide our own "corsika" logger)
+#define SPDLOG_DISABLE_DEFAULT_LOGGER
+
+// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where
+// printing function names in trace statements. This is much
+// nicer than __FUNCTION__ under GCC/clang.
+#define SPDLOG_FUNCTION __PRETTY_FUNCTION__
+
+// if this is a Debug build, include debug messages in objects
+#ifdef DEBUG
+// trace is the highest level of logging (ALL messages will be printed)
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
+#else // otherwise, remove everything but "critical" messages
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_CRITICAL
+#endif
+
 #include <spdlog/spdlog.h>
 #include "spdlog/sinks/stdout_color_sinks.h"
 
-
 namespace corsika::logging {
 
   // bring spdlog into the corsika::logging namespace
   using namespace spdlog;
 
+  /*
+   * The default pattern for CORSIKA8 loggers.
+   */
+  const std::string default_pattern{"[%n:%^%-8l%$] %v"};
+
   /**
    * Create a new C8-style logger.
    *
+   * Use this if you are explicitly (and can guarantee) that you
+   * are creating a logger for the first time. It is recommended
+   * that for regular usage, the `GetLogger` function is used instead
+   * as that will also create the logger if it has not yet been created.
+   *
+   * Calling `CreateLogger` twice to create the same logger will
+   * result in an spdlog duplicate exception.
+   *
    * @param name           The unique name of the logger.
    * @param defaultlog     If True, set this as the default logger.
    * @returns              The constructed and formatted logger.
@@ -33,10 +71,11 @@ namespace corsika::logging {
   auto CreateLogger(std::string const& name, bool const defaultlog = false) {
 
     // create the logger
+    // this is currently a colorized multi-threading safe logger
     auto logger = spdlog::stdout_color_mt(name);
 
     // set the default C8 format
-    logger->set_pattern("[%n:%^%-8l%$] %v");
+    logger->set_pattern(default_pattern);
 
     // if defaultlog is True, we set this as the default spdlog logger.
     if (defaultlog) { spdlog::set_default_logger(logger); }
@@ -48,7 +87,11 @@ namespace corsika::logging {
   /**
    * Get a smart pointer to an existing logger.
    *
-   * If the logger does not exist, it is created and returned.
+   * This should be the default method for code to obtain a
+   * logger. If the logger *does not* exist, it is *created* and
+   * returned to the caller.
+   *
+   * This should be preferred over `CreateLogger`.
    *
    * @param name    The name of the logger to get.
    * @param defaultlog   If True, make this the default logger.
@@ -68,19 +111,66 @@ namespace corsika::logging {
   }
 
   /**
-   * Set the default log level for all loggers.
+   * The default "corsika" logger.
+   */
+  inline auto corsika = GetLogger("corsika", true);
+
+  /**
+   * Set the default log level for all *newly* created loggers.
    *
-   * @param name    The minimum log level required to print.
+   *  @param name    The minimum log level required to print.
    *
    */
-  auto SetDefaultLevel(level::level_enum minlevel) -> void {
+  auto SetDefaultLevel(level::level_enum const minlevel) -> void {
     spdlog::set_level(minlevel);
   }
 
-  // create the default CORSIKA logger
-  inline auto corsika = GetLogger("corsika", true);
+  /**
+   * Set the log level for the "corsika" logger.
+   *
+   * @param name    The minimum log level required to print.
+   *
+   */
+  auto SetLevel(level::level_enum const minlevel) -> void {
+    corsika->set_level(minlevel);
+  }
+
+  /**
+   * Set the log level for a specific logger.
+   *
+   * @param logger  The logger to set the level of.
+   * @param name    The minimum log level required to print.
+   *
+   */
+  template <typename TLogger>
+  auto SetLevel(TLogger& logger, level::level_enum const minlevel) -> void {
+    logger->set_level(minlevel);
+  }
+
+  /**
+   * Add the source (filename, line no) info to the logger.
+   *
+   * @param logger  The logger to set the level of.
+   *
+   */
+  template <typename TLogger>
+  auto AddSourceInfo(TLogger& logger) -> void {
+    logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v");
+  }
+
+  /**
+   * Reset the logging pattern to the default.
+   *
+   * @param logger  The logger to set the level of.
+   *
+   */
+  template <typename TLogger>
+  auto ResetPattern(TLogger& logger) -> void {
+    logger->set_pattern(default_pattern);
+  }
 
 // define our macro-style loggers
+#define C8LOG_TRACE SPDLOG_TRACE
 #define C8LOG_DEBUG SPDLOG_DEBUG
 #define C8LOG_INFO SPDLOG_INFO
 #define C8LOG_WARN SPDLOG_WARN
@@ -88,6 +178,7 @@ namespace corsika::logging {
 #define C8LOG_CRITICAL SPDLOG_CRITICAL
 
 // and the specific logger versions
+#define C8LOG_LOGGER_TRACE SPDLOG_LOGGER_TRACE
 #define C8LOG_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG
 #define C8LOG_LOGGER_INFO SPDLOG_LOGGER_INFO
 #define C8LOG_LOGGER_WARN SPDLOG_LOGGER_WARN
diff --git a/Framework/Logging/testLogging.cc b/Framework/Logging/testLogging.cc
index 2bead9a54..bf9190580 100644
--- a/Framework/Logging/testLogging.cc
+++ b/Framework/Logging/testLogging.cc
@@ -72,23 +72,46 @@ TEST_CASE("Logging", "[Logging]") {
     logging::error("This should NOT be printed!");
     logging::critical("This SHOULD BE printed!!");
 
+    // get a reference to an unknown logger
+    auto logger = logging::GetLogger("loggerD");
+
+    // now set the default log level for this logger
+    logging::SetLevel(logger, logging::level::critical);
+
+    // now try the various logging functions
+    logger->info("This should NOT be printed!");
+    logger->warn("This should NOT be printed!");
+    logger->debug("This should NOT be printed!");
+    logger->error("This should NOT be printed!");
+    logger->critical("This SHOULD BE printed!!");
+
     // and reset it for the next tests
     logging::SetDefaultLevel(logging::level::debug);
+    logging::SetLevel(logging::level::debug);
   }
 
-  SECTION("test macro style printing") {
+  SECTION("test macro style logging") {
 
     // these print with the "corsika" logger
-    C8LOG_INFO("This is a macro info msg!");
-    C8LOG_DEBUG("This is a macro debug msg!");
-    C8LOG_ERROR("This is a macro error msg!");
-    C8LOG_CRITICAL("This is a macro critical msg!");
+    C8LOG_INFO("test macro style logging");
+    C8LOG_DEBUG("test macro style logging");
+    C8LOG_ERROR("test macro style logging");
+    C8LOG_CRITICAL("test macro style logging");
 
     // get a reference to an unknown logger
-    auto logger = logging::GetLogger("loggerD");
+    auto logger = logging::GetLogger("loggerE");
+
+    // add the filename, source information to this logger
+    logging::AddSourceInfo(logger);
+
+    // these print with the "loggerE" logger
+    C8LOG_LOGGER_INFO(logger, "test macro style logging");
+    C8LOG_LOGGER_WARN(logger, "test macro style logging");
+
+    // reset the logging pattern
+    logging::ResetPattern(logger);
 
-    // these print with the "loggerD" logger
-    C8LOG_LOGGER_INFO(logger, "This is a macro info msg!");
-    C8LOG_LOGGER_WARN(logger, "This is a macro warn msg!");
+    // these trace macros should not print file, function, and line
+    C8LOG_LOGGER_TRACE(logger, "test macro style logging:");
   }
 }
diff --git a/ThirdParty/spdlog b/ThirdParty/spdlog
index 616caa5d3..58875bdcd 160000
--- a/ThirdParty/spdlog
+++ b/ThirdParty/spdlog
@@ -1 +1 @@
-Subproject commit 616caa5d30172b65cc3a06800894c575d70cb8e6
+Subproject commit 58875bdcd790dd2f5cb8d95ef1da7970de0a4530
-- 
GitLab