/*+ ARES/HADES/BORG Package -- ./libLSS/tools/console.hpp Copyright (C) 2014-2020 Guilhem Lavaux Copyright (C) 2009-2020 Jens Jasche Additional contributions from: Guilhem Lavaux (2023) +*/ #ifndef __LIBLSS_CONSOLE_HPP #define __LIBLSS_CONSOLE_HPP // Log traits automatically available when console is loaded #include #include #include #include #include #include #include "libLSS/mpi/generic_mpi.hpp" #include "libLSS/tools/log_traits.hpp" #include "libLSS/tools/static_auto.hpp" #include "libLSS/tools/function_name.hpp" namespace LibLSS { class Console; namespace details { class ProgressBase { private: int numElements; int current; int percent; int iLevel; int step; std::string msg; boost::chrono::system_clock::time_point start; friend class Console; protected: Console *console; ProgressBase(Console *c, const std::string &m, int num, int _step); virtual ~ProgressBase() {} public: void destroy(); virtual void print(const std::string &s) = 0; void update(int i); }; }; // namespace details template class Progress : public details::ProgressBase { private: Progress(Console *c, const std::string &m, int num, int _step) : ProgressBase(c, m, num, _step) {} friend class Console; public: virtual void print(const std::string &s); }; class Console; namespace timings { void record(std::string const &n, double t); void trigger_dump(); void reset(); void set_file_pattern(std::string const &pattern); } // namespace timings namespace details { class ConsoleContextBase; extern thread_local ConsoleContextBase *currentContext; class ConsoleContextBase { protected: ConsoleContextBase *previousContext; #ifdef LIBLSS_TIMED_CONTEXT boost::chrono::system_clock::time_point start_context; std::string ctx_msg, short_msg; void record_timing(boost::chrono::duration const &timing) { timings::record(ctx_msg, timing.count()); } void record_self_timing( boost::chrono::system_clock::time_point const &timing) {} #else void record_timing(boost::chrono::duration const &) {} void record_self_timing(boost::chrono::duration const &) {} #endif public: #ifdef LIBLSS_TIMED_CONTEXT std::string const &getMessage() const { return ctx_msg; } #else std::string const &getMessage() const { static std::string s = ""; return s; } #endif static inline ConsoleContextBase ¤t() { return *currentContext; } ConsoleContextBase(); ~ConsoleContextBase(); }; class ConsoleContextDummy { public: std::string getMessage() const { return std::string(); } static inline ConsoleContextBase ¤t() { return *currentContext; } template void print(const Args &... args) {} template void print2(const Args &... args) {} template void format(Args &&... args) {} template void format2(Args &&... args) {} ConsoleContextDummy() {} ~ConsoleContextDummy() {} }; template class ConsoleContext : public ConsoleContextBase { private: ConsoleContext(ConsoleContext const &) {} public: ConsoleContext(std::string const &code_name); ConsoleContext( std::string const &code_name, std::string const &short_name); ~ConsoleContext(); template void format(Args &&... args); template void format2(Args &&... args); template void print(const Args &... args); template void print2(const Args &... args); }; } // namespace details using details::ConsoleContext; class Console { protected: typedef std::list PList; std::ofstream outputFile; int verboseLevel; int logfileVerboseLevel; int indentLevel; bool noColor; typedef std::function OutputHijacker; OutputHijacker hijackOutput; std::string indentString; PList all_progress; friend class details::ProgressBase; template friend class ConsoleContext; Console(int verbose = DEFAULT_LOG_LEVEL::verboseLevel, int indent = 0) : verboseLevel(verbose), logfileVerboseLevel(LOG_DEBUG::verboseLevel), indentLevel(indent), noColor(false) {} private: void cleanProgress(details::ProgressBase *b) { PList::iterator i = all_progress.begin(); while (i != all_progress.end()) { if ((*i) == b) i = all_progress.erase(i); else ++i; } } public: static Console &instance() { static Console singleton; return singleton; } void setSeparateStream(OutputHijacker f) { hijackOutput = f; } void setNoColor(bool nc) { noColor = nc; } void outputToFile(const std::string &fname) { outputFile.close(); outputFile.open(fname.c_str(), std::ofstream::app); } void indent() { setIndentLevel(indentLevel + 2); } void unindent() { setIndentLevel(indentLevel - 2); } void setIndentLevel(int indent) { indentString = ""; for (int i = 0; i < indent / 2; i++) indentString += "| "; indentLevel = indent; } void setVerboseLevel(int verbose) { verboseLevel = verbose; } void setLogfileVerboseLevel(int verbose) { logfileVerboseLevel = verbose; } int getVerboseLevel() const {return verboseLevel;} template void setVerboseLevel() { setVerboseLevel(T::verboseLevel); } template void setLogfileVerboseLevel() { setLogfileVerboseLevel(T::verboseLevel); } template bool willPrint() const { return verboseLevel >= T::verboseLevel; } // Fast track debug. Reduced functionality but less performance loss. void debug(const std::string &s) { #ifndef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (willPrint()) print(s); #endif } template void print(const boost::format &fmt) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif print(fmt.str()); } template void print(std::vector const &msg_v) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif for (auto const &s : msg_v) print(s); } static void _format_expansion(boost::format &f) {} template static void _format_expansion(boost::format &f, A &&a, U &&... u) { _format_expansion(f % a, u...); } template void format(std::string const &s, U &&... args) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif boost::format f(s); _format_expansion(f, std::forward(args)...); print(f); } template void print(const std::string &msg) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif MPI_Communication *world = MPI_Communication::instance(); bool notMainRank = ((T::mainRankOnly) && (world->rank() != 0)); if (outputFile && T::verboseLevel <= logfileVerboseLevel) { std::string fullMessage = T::prefix + indentString + msg; outputFile << fullMessage << std::endl; } if (hijackOutput) { std::string fullMessage = T::prefix + indentString + msg; hijackOutput(T::verboseLevel, fullMessage); } if (verboseLevel < T::verboseLevel) return; std::string fullMessage_c = (noColor ? T::prefix : T::prefix_c) + indentString + msg; if (!T::mainRankOnly) { fullMessage_c = boost::str( boost::format("[% 3d/% 3d] %s") % world->rank() % world->size() % fullMessage_c); } else if (notMainRank) return; else { fullMessage_c = "[---/---] " + fullMessage_c; } for (int i = 0; i < T::numOutput; i++) if (*T::os[i]) (*T::os[i]) << fullMessage_c << std::endl; } template void print_memory(size_t n) { if (n < 1024L) print(boost::format("Requesting %ld bytes") % n); else if (n < 1024L * 1024L) print(boost::format("Requesting %lg kbytes") % (n / 1024.)); else if (n < 1024L * 1024L * 1024L) print(boost::format("Requesting %lg Mbytes") % (n / 1024. / 1024.)); else print( boost::format("Requesting %lg Gbytes") % (n / 1024. / 1024. / 1024.)); } template Progress & start_progress(const std::string &msg, int numElements, int step = 10) { Progress *p = new Progress(this, msg, numElements, step); all_progress.push_back(p); return *p; } void print_stack_trace(); void c_assert(bool c, const std::string &msg) { if (!c) { print(msg); print_stack_trace(); MPI_Communication::instance()->abort(); } } template void c_assert(bool c, const std::string &msg, U &&u, T &&... t) { if (!c) { boost::format f(msg); _format_expansion(f, std::forward(u), std::forward(t)...); print(f.str()); MPI_Communication::instance()->abort(); } } }; inline details::ConsoleContextBase::ConsoleContextBase() { previousContext = currentContext; currentContext = this; } inline details::ConsoleContextBase::~ConsoleContextBase() { currentContext = previousContext; } template details::ConsoleContext::ConsoleContext(std::string const &msg) : ConsoleContextBase() { #ifdef LIBLSS_TIMED_CONTEXT start_context = boost::chrono::system_clock::now(); short_msg = ctx_msg = msg; #endif Console &c = Console::instance(); c.print("Entering " + msg); c.indent(); } template details::ConsoleContext::ConsoleContext( std::string const &msg, std::string const &short_msg_) : ConsoleContextBase() { #ifdef LIBLSS_TIMED_CONTEXT start_context = boost::chrono::system_clock::now(); ctx_msg = msg; short_msg = short_msg_; record_self_timing(start_context); #endif Console &c = Console::instance(); c.print("Entering " + short_msg_); c.indent(); } template details::ConsoleContext::~ConsoleContext() { Console &c = Console::instance(); c.unindent(); #ifdef LIBLSS_TIMED_CONTEXT boost::chrono::duration ctx_time = boost::chrono::system_clock::now() - start_context; c.print(boost::format("Done (in %s) (ctx='%s')") % ctx_time % short_msg); record_timing(ctx_time); #else c.print("Done"); #endif } template template void details::ConsoleContext::print(const Args &... args) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif Console::instance().print(args...); } template template void details::ConsoleContext::format(U &&... u) { Console::instance().format(std::forward(u)...); } template template void details::ConsoleContext::format2(U &&... u) { #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif Console::instance().format(std::forward(u)...); } template template void details::ConsoleContext::print2(const Args &... args) { /* if (T2::verboseLevel < T::verboseLevel) return;*/ #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT if (T::verboseLevel >= LOG_DEBUG::verboseLevel) return; #endif Console::instance().print(args...); } template void Progress::print(const std::string &m) { Console::instance().print(m); } }; // namespace LibLSS AUTO_REGISTRATOR_DECL(console_timing); namespace LibLSS { namespace fileTools { namespace { constexpr const char *str_end(const char *str) { return *str ? str_end(str + 1) : str; } constexpr bool str_slant(const char *str) { return *str == '/' ? true : (*str ? str_slant(str + 1) : false); } constexpr const char *r_slant(const char *str) { return *str == '/' ? (str + 1) : r_slant(str - 1); } constexpr const char *file_name(const char *str) { return str_slant(str) ? r_slant(str_end(str)) : str; } } // namespace } // namespace fileTools } // namespace LibLSS #define LIBLSS_AUTO_CONTEXT(level, name) \ LibLSS::ConsoleContext name( \ std::string("[" __FILE__ "]") + LIBLSS_FUNCTION) #define LIBLSS_AUTO_CONTEXT2(level, name, short) \ LibLSS::ConsoleContext name( \ std::string("[" __FILE__ "]") + LIBLSS_FUNCTION, short) #ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT # define LIBLSS_AUTO_DEBUG_CONTEXT(name) \ LibLSS::details::ConsoleContextDummy name # define LIBLSS_AUTO_DEBUG_CONTEXT2(name, short) \ LibLSS::details::ConsoleContextDummy name #else # define LIBLSS_AUTO_DEBUG_CONTEXT(name) \ LibLSS::ConsoleContext name( \ std::string("[" __FILE__ "]") + LIBLSS_FUNCTION) # define LIBLSS_AUTO_DEBUG_CONTEXT2(name, short) \ LibLSS::ConsoleContext name( \ std::string("[" __FILE__ "]") + LIBLSS_FUNCTION, short) #endif #endif