diff --git a/src/cluster/cluster.cpp b/src/cluster/cluster.cpp index c8aec3d43aa0cf3d68fb9fec54ef5b49430f3341..971d83f6139a615b210a1bdb60077fde51867485 100644 --- a/src/cluster/cluster.cpp +++ b/src/cluster/cluster.cpp @@ -17,6 +17,10 @@ #include "../include/errors.h" #endif +#ifndef INCLUDE_LOGGING_H_ +#include "../include/logging.h" +#endif + #ifndef INCLUDE_CONFIGURATION_H_ #include "../include/Configuration.h" #endif @@ -50,13 +54,15 @@ using namespace std; * \param output_path: `string` Directory to write the output files in. */ void cluster(string config_file, string data_file, string output_path) { - printf("INFO: making legacy configuration..."); + Logger logger(LOG_INFO); + logger.log("INFO: making legacy configuration...", LOG_INFO); ScattererConfiguration *sconf = NULL; try { sconf = ScattererConfiguration::from_dedfb(config_file); } catch(const OpenConfigurationFileException &ex) { - printf("\nERROR: failed to open scatterer configuration file.\n"); - printf("FILE: %s\n", ex.what()); + logger.err("\nERROR: failed to open scatterer configuration file.\n"); + string message = "FILE: " + string(ex.what()) + "\n"; + logger.err(message); exit(1); } sconf->write_formatted(output_path + "/c_OEDFB"); @@ -66,12 +72,13 @@ void cluster(string config_file, string data_file, string output_path) { try { gconf = GeometryConfiguration::from_legacy(data_file); } catch (const OpenConfigurationFileException &ex) { - printf("\nERROR: failed to open geometry configuration file.\n"); - printf("FILE: %s\n", ex.what()); + logger.err("\nERROR: failed to open geometry configuration file.\n"); + string message = "FILE: " + string(ex.what()) + "\n"; + logger.err(message); if (sconf) delete sconf; exit(1); } - printf(" done.\n"); + logger.log(" done.\n", LOG_INFO); if (sconf->number_of_spheres == gconf->number_of_spheres) { // Shortcuts to variables stored in configuration objects int nsph = gconf->number_of_spheres; @@ -279,9 +286,9 @@ void cluster(string config_file, string data_file, string output_path) { tppoan.open(tppoan_name.c_str(), ios::out | ios::binary); if (tppoan.is_open()) { #ifdef USE_LAPACK - printf("INFO: using LAPACK calls.\n"); + logger.log("INFO: using LAPACK calls.\n", LOG_INFO); #else - printf("INFO: using fall-back lucin() calls.\n"); + logger.log("INFO: using fall-back lucin() calls.\n", LOG_INFO); #endif tppoan.write(reinterpret_cast<char *>(&iavm), sizeof(int)); tppoan.write(reinterpret_cast<char *>(&isam), sizeof(int)); @@ -299,7 +306,9 @@ void cluster(string config_file, string data_file, string output_path) { fprintf(output, " \n"); } for (int jxi488 = 1; jxi488 <= nxi; jxi488++) { - printf("INFO: running scale iteration %d of %d...", jxi488, nxi); + string message = ("INFO: running scale iteration " + to_string(jxi488) + + " of " + to_string(nxi) + "..."); + logger.log(message, LOG_INFO); int jaw = 1; fprintf(output, "========== JXI =%3d ====================\n", jxi488); double xi = sconf->scale_vec[jxi488 - 1]; @@ -349,6 +358,7 @@ void cluster(string config_file, string data_file, string output_path) { } if (jer != 0) break; } // i132 loop + logger.push("DEBUG: " + TOSTRING(cms(am, c1, c1ao, c4, c6);)); cms(am, c1, c1ao, c4, c6); invert_matrix(am, ndit, jer, mxndm); if (jer != 0) break; // jxi488 loop: goes to memory clean @@ -904,11 +914,11 @@ void cluster(string config_file, string data_file, string output_path) { } // jph484 loop th += thstp; } // jth486 loop - printf(" done.\n"); + logger.log(" done.\n", LOG_INFO); } // jxi488 loop tppoan.close(); } else { // In case TPPOAN could not be opened. Should never happen. - printf("\nERROR: failed to open TPPOAN file.\n"); + logger.err("\nERROR: failed to open TPPOAN file.\n"); } fclose(output); // Clean memory @@ -992,5 +1002,6 @@ void cluster(string config_file, string data_file, string output_path) { } delete sconf; delete gconf; - printf("Finished: output written to %s.\n", (output_path + "/c_OCLU").c_str()); + logger.flush(LOG_INFO); + logger.log("Finished: output written to " + output_path + "/c_OCLU\n"); } diff --git a/src/include/logging.h b/src/include/logging.h index 69a3b40b2e8da3897f77f65ac8619fba8a5d75c7..9545af27fbe12d47cc0f6e9d4df24579693d68e3 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -7,11 +7,18 @@ #ifndef INCLUDE_LOGGING_H_ #define INCLUDE_LOGGING_H_ +//! \brief Debug level logging (maximum verbosity). #define LOG_DEBG 0 +//! \brief Standard information level logging (default). #define LOG_INFO 1 +//! \brief Warning level logging (almost quiet). #define LOG_WARN 2 +//! \brief Error level logging (silent, unless in pain). #define LOG_ERRO 3 +//! \brief Macro to stringize code lines +#define TOSTRING(ARG) string(#ARG) + /*! \brief Logger class. * * Loggers are objects used to track the execution of a code, reporting activities @@ -23,9 +30,16 @@ */ class Logger { protected: - FILE *log_output; + //! \brief Pointer to error stream. FILE *err_output; + //! \brief Pointer to logging stream. + FILE *log_output; + //! \brief Last logged message. + std::string last_message; + //! \brief Threshold of logging level. int log_threshold; + //! \brief Number of identical message repetitions. + long repetitions; public: /*! \brief Logger instance constructor. @@ -47,12 +61,30 @@ class Logger { */ void err(std::string message); + /*! \brief Print a summary of recurrent messages and clear the stack. + * + * \param level: `int` The priority level (default is `LOG_DEBG = 0`). + */ + void flush(int level=LOG_DEBG); + /*! \brief Print a message, depending on its logging level. * * \param message: `string` The message to be printed. * \param level: `int` The priority level (default is `LOG_INFO = 1`). */ void log(std::string message, int level=LOG_INFO); + + /*! \brief Push a recurrent message to the message stack. + * + * When a long stream of identical messages is expected, it may be more + * convenient to put them in a stack. The stack is the error output stream, + * so that no memory is consumed. After the call stack is over, or is the + * message changes, the stack is flushed, meaning that a summary message + * is written to the logging output and the stack counter is reset to 0. + * + * \param message: `string` The message to be stacked. + */ + void push(std::string message); }; #endif diff --git a/src/libnptm/logging.cpp b/src/libnptm/logging.cpp index e86763153b14ee143163b16a50371d33ea735847..f27bd256c5e6a1b8326014ca756bfd8764e76b20 100644 --- a/src/libnptm/logging.cpp +++ b/src/libnptm/logging.cpp @@ -14,18 +14,42 @@ using namespace std; Logger::Logger(int threshold, FILE *logging_output, FILE *error_output) { + last_message = ""; log_threshold = threshold; log_output = logging_output; err_output = error_output; + repetitions = 0; } void Logger::err(std::string message) { - fprintf(err_output, "%s\n", message.c_str()); + fprintf(err_output, "%s", message.c_str()); +} + +void Logger::flush(int level) { + string summary = "\"" + last_message + "\" issued " + to_string(repetitions); + if (repetitions == 1) summary += " time.\n"; + else summary += " times.\n"; + if (level == LOG_ERRO) err(summary); + else { + if (level >= log_threshold) fprintf(log_output, "%s", summary.c_str()); + } + repetitions = 0; } void Logger::log(std::string message, int level) { if (level == LOG_ERRO) err(message); else { - if (level >= log_threshold) fprintf(log_output, "%s\n", message.c_str()); + if (level >= log_threshold) fprintf(log_output, "%s", message.c_str()); + } +} + +void Logger::push(std::string message) { + if (repetitions > 0) { + if (message.compare(last_message) != 0) { + flush(LOG_DEBG); + } } + log(message, LOG_DEBG); + last_message = message; + repetitions++; }