diff --git a/src/pEpLog.cc b/src/pEpLog.cc new file mode 100644 index 0000000..c70b717 --- /dev/null +++ b/src/pEpLog.cc @@ -0,0 +1,158 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#include "pEpLog.hh" +#include +#include +#include +#include +#include +#include "std_utils.hh" + +#ifdef ANDROID + #include +#endif + +using namespace std; + +namespace pEp { + namespace Adapter { + namespace pEpLog { + int line_width = 120; + + // NON CLASS + mutex mtx; + atomic_bool is_enabled{ false }; + + void set_enabled(const bool& enabled) + { + is_enabled.store(enabled); + } + + bool get_enabled() + { + return is_enabled.load(); + } + + // Common "print" function implementing the actual "backends" + void _log(const string& msg, Utils::Color col = Utils::Color::WHITE) + { + lock_guard l(mtx); +#ifdef ANDROID + __android_log_print(ANDROID_LOG_DEBUG, "pEpDebugLog", "%s", msg.c_str()); +#else + cerr << Utils::to_termcol(col) << msg << Utils::to_termcol(Utils::Color::RESET) + << endl; //endl also flushes, but cerr is unbuffered anyways +#endif + } + + void log(const string& msg, Utils::Color col) + { + _log(msg, col); + } + + void logH1(const string& msg, Utils::Color col) + { + log(decorate_three_lines(msg, '='), col); + } + + void logH2(const string& msg, Utils::Color col) + { + log("\n" + decorate_centered(msg, '='), col); + } + + void logH3(const string& msg, Utils::Color col) + { + log(decorate_centered(msg, '-'), col); + } + + string decorate_three_lines(const string& msg, char decoration) + { + stringstream tmp; + tmp << std::string(line_width, decoration) << endl + << msg << endl + << std::string(line_width, decoration); + return tmp.str(); + } + + string decorate_centered(const string& msg, char decoration) + { + stringstream tmp; + size_t max_len = line_width - 10; + // truncate msg + string msg_truncated = msg; + if (msg.length() >= max_len) { + msg_truncated = msg.substr(0, max_len); + msg_truncated += "..."; + } + + // define decolen + int decolen = static_cast( + floor((double(line_width - msg_truncated.length()))) / 2.0); + + tmp << std::string(decolen, decoration) << ' ' << msg_truncated << ' ' + << std::string(decolen, decoration); + return tmp.str(); + } + + } // namespace pEpLog + } // namespace Adapter +} // namespace pEp + +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, Utils::Color col) const + { + std::stringstream msg_; + msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; + msg_ << " - "; + msg_ << this->get_classname() << "[" << this->get_instancename() << "]"; + msg_ << " - " << msg; + this->logRaw(msg_.str(), col); + } + + void pEpLogger::logRaw(const string& msg, Utils::Color col) const + { + if (this->is_enabled) { + _log(msg, col); + } + } + + void pEpLogger::set_enabled(const bool& enabled) + { + this->is_enabled = enabled; + } + + bool pEpLogger::get_enabled() const + { + return this->is_enabled; + } + + string pEpLogger::get_classname() const + { + return this->classname; + } + + void pEpLogger::set_instancename(const string& name) + { + this->instancename = name; + } + + string pEpLogger::get_instancename() const + { + return this->instancename; + } + } // namespace pEpLog + } // namespace Adapter +} // namespace pEp diff --git a/src/pEpLog.hh b/src/pEpLog.hh new file mode 100644 index 0000000..8a2083b --- /dev/null +++ b/src/pEpLog.hh @@ -0,0 +1,228 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_PEPLOG_HH +#define LIBPEPADAPTER_PEPLOG_HH + +// getpid +// Linux - unistd.h +// macOS - unistd.h +// Android - unistd.h +// Win - process.h +#ifdef WIN32 + #include + // TODO: once this works, move this to pEpEngine platform_windows.h and file a PR + #ifndef getpid + #define getpid() _getpid() + #endif + +#else + #include +#endif + +#include +#include +#include "std_utils.hh" + +// pEpLog +// ====== +// a "to be kept ultra small and simple" logging unit. +// featuring: +// * 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: cerr +// * Logging without any class/object (pEpLog / pEpLogRaw macros) +// * runtime switchable (on/off) only on a global level +// * Class backed Logging macros (pEpLogClass / pEpLogClassRaw) +// * * runtime switchable (on/off) on a class and object level +// +// There are already too many 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. +// + +// pEpLog - logformat "thread - __FILE__::__FUNTION__ - " +// To be used in a non-class/object context +#ifdef NDEBUG + #define pEpLog(msg) \ + do { \ + } while (0) +#else + #define pEpLog(msg) \ + do { \ + if (pEp::Adapter::pEpLog::get_enabled()) { \ + std::stringstream msg_; \ + msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \ + msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \ + msg_ << " - " << msg; \ + pEp::Adapter::pEpLog::log(msg_.str()); \ + } \ + } while (0) +#endif // NDEBUG + + +// RAW == without prefix of thread, file, function +// pEpLogRaw - logformat "" +#ifdef NDEBUG + #define pEpLogRaw(msg) \ + do { \ + } while (0) +#else + #define pEpLogRaw(msg) \ + do { \ + if (pEp::Adapter::pEpLog::get_enabled()) { \ + std::stringstream ss_msg{}; \ + ss_msg << msg; \ + pEp::Adapter::pEpLog::log(ss_msg.str()); \ + } \ + } while (0) +#endif // NDEBUG + + +// pEpLogH1 - logformat "<==============================>" +// pEpLogH1 - logformat message" +// pEpLogH1 - logformat "<==============================>" +#ifdef NDEBUG + #define pEpLogH1(msg) \ + do { \ + } while (0) +#else + #define pEpLogH1(msg) \ + do { \ + if (pEp::Adapter::pEpLog::get_enabled()) { \ + std::stringstream ss_msg{}; \ + ss_msg << msg; \ + pEp::Adapter::pEpLog::logH1(ss_msg.str()); \ + } \ + } while (0) +#endif // NDEBUG + + +// pEpLogH2 - logformat <=============== message ==============>" +#ifdef NDEBUG + #define pEpLogH2(msg) \ + do { \ + } while (0) +#else + #define pEpLogH2(msg) \ + do { \ + if (pEp::Adapter::pEpLog::get_enabled()) { \ + std::stringstream ss_msg{}; \ + ss_msg << msg; \ + pEp::Adapter::pEpLog::logH2(ss_msg.str()); \ + } \ + } while (0) +#endif // NDEBUG + + +// pEpLogH2 - logformat <---------------- message ----------------" +#ifdef NDEBUG + #define pEpLogH3(msg) \ + do { \ + } while (0) +#else + #define pEpLogH3(msg) \ + do { \ + if (pEp::Adapter::pEpLog::get_enabled()) { \ + std::stringstream ss_msg{}; \ + ss_msg << msg; \ + pEp::Adapter::pEpLog::logH3(ss_msg.str()); \ + } \ + } while (0) +#endif // NDEBUG + + +namespace pEp { + namespace Adapter { + namespace pEpLog { + // Logging functions to control pEpLog() macro + void set_enabled(const bool& is_enabled); + bool get_enabled(); + void log(const std::string& msg, Utils::Color col = Utils::Color::WHITE); + void logH1(const std::string& msg, Utils::Color col = Utils::Color::WHITE); + void logH2(const std::string& msg, Utils::Color col = Utils::Color::WHITE); + void logH3(const std::string& msg, Utils::Color col = Utils::Color::WHITE); + std::string decorate_three_lines(const std::string& msg, char decoration = '-'); + std::string decorate_centered(const std::string& msg, char decoration = '-'); + } // namespace pEpLog + } // namespace Adapter +} // namespace pEp + +// -------------------------------------------------------------------------------------------------- + + +// 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: +// create your logger obj in your class as a public member (usually) +// Adapter::pEpLog::pEpLogger logger{"", enabled: true|false}; +// then, create an alias for your logger called "m4gic_logger_n4me" as a private member +// Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = 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_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \ + msg_ << " - " << this->m4gic_logger_n4me.get_classname(); \ + msg_ << "[" << this->m4gic_logger_n4me.get_instancename() << "]"; \ + msg_ << "::" << __FUNCTION__; \ + msg_ << " - " << (msg); \ + this->m4gic_logger_n4me.logRaw(msg_.str()); \ + } while (0) +#endif // NDEBUG + +// pEpLogClassRaw 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 pEpLogClassRaw(msg) \ + do { \ + } while (0) +#else + #define pEpLogClassRaw(msg) \ + do { \ + this->m4gic_logger_n4me.logRaw(msg); \ + } 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, Utils::Color col = Utils::Color::WHITE) const; + // Prints just "" + void logRaw(const std::string& msg, Utils::Color col = Utils::Color::WHITE) const; + void set_enabled(const bool& enabled); + bool get_enabled() const; + std::string get_classname() const; + // If never set, the default instancename is a unique number + void set_instancename(const std::string& name); + std::string get_instancename() const; + + private: + static int auto_instance_nr; + bool is_enabled; + std::string classname; + std::string instancename; + }; + } // namespace pEpLog + } // namespace Adapter +} // namespace pEp + + +#endif // LIBPEPADAPTER_PEPLOG_HH