From 9dddab438807ecc0be0b37dfc030f71be5ce20fe Mon Sep 17 00:00:00 2001 From: heck Date: Fri, 16 Apr 2021 03:35:24 +0200 Subject: [PATCH] pEpLog: Add class and macros to support runtime switching on a class and instance basis. --- src/pEpLog.cc | 87 +++++++++++++++++++++++++++++----- src/pEpLog.hh | 128 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 182 insertions(+), 33 deletions(-) diff --git a/src/pEpLog.cc b/src/pEpLog.cc index f7f61bf..f9ec1a2 100644 --- a/src/pEpLog.cc +++ b/src/pEpLog.cc @@ -8,19 +8,20 @@ #include #ifdef ANDROID - #include +#include #endif +using namespace std; namespace pEp { namespace Adapter { namespace pEpLog { - std::mutex mtx; + // NON CLASS + mutex mtx; + atomic_bool is_enabled{false}; - std::atomic_bool is_enabled{ false }; - - void set_enabled(bool enabled) + void set_enabled(const bool& enabled) { is_enabled.store(enabled); } @@ -30,18 +31,82 @@ namespace pEp { return is_enabled.load(); } - void log(std::string msg) + // Common "print" function implementing the actual "backends" + void _log(const string& msg) { - if (is_enabled.load()) { - std::lock_guard l(mtx); + lock_guard l(mtx); #ifdef ANDROID - __android_log_print(ANDROID_LOG_DEBUG, "pEpDebugLog", "%s", msg.c_str()); + __android_log_print(ANDROID_LOG_DEBUG, "pEpDebugLog", "%s", msg.c_str()); #else - std::cerr << msg << std::endl; //std::endl also flushes + cerr << msg << endl; //endl also flushes, but cerr is unbuffered anyways #endif + } + + void log(const string& msg) + { + if (is_enabled.load()) { + _log(msg); + } + } + } + } +} + +namespace pEp { + namespace Adapter { + namespace pEpLog { + // Class pEpLogger + + int pEpLogger::auto_instance_nr = 0; + pEpLogger::pEpLogger(const string& classname, const bool& enabled) + : classname(classname), + is_enabled(enabled) + { + auto_instance_nr++; + this->set_instancename(to_string(auto_instance_nr)); + } + + void pEpLogger::log(const string& msg) + { + std::stringstream msg_; + msg_ << std::this_thread::get_id(); + msg_ << " - "; + msg_ << this->get_classname() << "[" << this->get_instancename() << "]"; + msg_ << " - " << msg; + this->logRaw(msg_.str()); + } + + void pEpLogger::logRaw(const string& msg) + { + if (this->is_enabled) { + _log(msg); } } + void pEpLogger::set_enabled(const bool& is_enabled) + { + this->is_enabled = is_enabled; + } + + bool pEpLogger::get_enabled() + { + return this->is_enabled; + } + + string pEpLogger::get_classname() + { + return this->classname; + } + + void pEpLogger::set_instancename(const string& instancename) + { + this->instancename = instancename; + } + + string pEpLogger::get_instancename() + { + return this->instancename; + } } // namespace pEpLog - } // namespace Adapter + } // namespace Adapter } // namespace pEp diff --git a/src/pEpLog.hh b/src/pEpLog.hh index 3cb4b7b..0edeb42 100644 --- a/src/pEpLog.hh +++ b/src/pEpLog.hh @@ -10,49 +10,133 @@ // ====== // a "to be kept ultra small and simple" logging unit. // featuring: -// * pEpLog macro that will be eliminated in release-builds (-DNDEBUG=1) -// * thread safe (no interleave when logging from diff threads) +// * Logging macros that completely eliminate any logging calls in release-builds (NDEBUG) +// * thread safe (no interleave when logging from diff threads) TODO: pEpLogger: REALLY? // * OS dependent backend switches: // * android: __android_log_print -// * all other OS: cout -// * runtime enabled/disabled switch (global) +// * all other OS: cerr +// * Logging without any class/object (pEpLog / pEpLogRaw macros) +// * runtime switchable only on a global level +// * Class backed Logging macros (pEpLogClass / pEpLogRawClass) +// * * runtime switchable logging on a class and object level // -// You might want more and more features, but the feature-policy is very restrictive, and there is a -// primary design goal to keep it simple, maintainable and portable. +// There are already too mnay features and you might want even more and more, +// but the feature-policy of this logging unit is very restrictive, and there is a +// primary design goal to keep it very simple, maintainable and portable. // -// How to use: -// include -// use the macro pEpLog(msg) to do logging -// use NDEBUG=1 to turn logging on/off at compile-time -// use set_enabled(bool) to turn logging on/off at runtime -// use set_enabled_(bool) to turn logging on/off per backend +// pEpLog is to be used in a non-class/object context #ifdef NDEBUG - #define pEpLog(msg) \ +#define pEpLog(msg) \ do { \ } while (0) #else - #define pEpLog(msg) \ +#define pEpLog(msg) \ do { \ - std::stringstream msg_ss; \ - msg_ss << std::this_thread::get_id() << " - " << __FILE__ << "::" << __FUNCTION__ \ - << " - " << msg; \ - pEp::Adapter::pEpLog::log(msg_ss.str()); \ + std::stringstream msg_; \ + msg_ << std::this_thread::get_id(); \ + msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \ + msg_ << " - " << msg; \ + pEp::Adapter::pEpLog::log(msg_.str()); \ + } while (0) +#endif // NDEBUG + +// pEpLogRaw the same as pEpLog, but does not print anything except the supplied msg +#ifdef NDEBUG +#define pEpLogRaw(msg) \ + do { \ + } while (0) +#else +#define pEpLogRaw(msg) \ + do { \ + pEp::Adapter::pEpLog::log(msg_.str()); \ } while (0) #endif // NDEBUG namespace pEp { namespace Adapter { namespace pEpLog { + // Logging functions to control pEpLog() macro + void log(const std::string& msg); + void set_enabled(const bool& is_enabled); + bool get_enabled(); + } // pEp + } // Adapter +} // pEpLog - void log(std::string msg); +// -------------------------------------------------------------------------------------------------- - void set_enabled(bool is_enabled); - bool get_enabled(); +// pEpLogClass is to be used in a class +// pEpLogger can only print the "thread - file::class::function - " format using this macro +// WARNING: Some magic is needed +// Usage: +// Just create your logger member in your class (public) +// Adapter::pEpLog::pEpLogger logger{"", enabled: true|false}; +// then, create an alias for your logger called "m4gic_logger_n4ame" +// Adapter::pEpLog::pEpLogger& m4gic_logger_n4ame = logger; +// Thats all. +// Now in your implementation, to log a message you just write: +// pEpLogClass("my great logging message"); +#ifdef NDEBUG +#define pEpLogClass(msg) \ + do { \ + } while (0) +#else +#define pEpLogClass(msg) \ + do { \ + std::stringstream msg_; \ + msg_ << std::this_thread::get_id(); \ + msg_ << " - " << __FILE__; \ + msg_ << "::" << this->m4gic_logger_n4ame.get_classname(); \ + msg_ << "[" << this->m4gic_logger_n4ame.get_instancename() << "]"; \ + msg_ << "::" << __FUNCTION__; \ + msg_ << " - " << msg; \ + this->m4gic_logger_n4ame.logRaw(msg_.str()); \ + } while (0) +#endif // NDEBUG + +// pEpLogRawClass is the same as pEpLogClass, but does not print anything except the supplied msg +// This can also be achieved without this macro, just use the log method of pEpLogger +// You also need to set up the logger in your class as for pEpLogClass +// The only advantage of this macro is that is compiled away to nothing with NDEBUG +#ifdef NDEBUG +#define pEpLogRawClass(msg) \ + do { \ + } while (0) +#else +#define pEpLogRawClass(msg) \ + do { \ + this->m4gic_logger_n4ame.logRaw(msg_.str()); \ + } while (0) +#endif // NDEBUG + +namespace pEp { + namespace Adapter { + namespace pEpLog { + class pEpLogger { + public: + pEpLogger() = delete; + pEpLogger(const std::string& classname, const bool& enabled); + // Print a logging message in the format "thread - classname[instancename] - " + void log(const std::string& msg); + // Prints just "" + void logRaw(const std::string& msg); + void set_enabled(const bool& is_enabled); + bool get_enabled(); + std::string get_classname(); + // If never set, the default instancename is a unique number + void set_instancename(const std::string& instancename); + std::string get_instancename(); + private: + static int auto_instance_nr; + bool is_enabled; + std::string classname; + std::string instancename; + }; } // namespace pEpLog - } // namespace Adapter + } // namespace Adapter } // namespace pEp