From 4adbd1c065993ffec7fecec08f279e2958f48422 Mon Sep 17 00:00:00 2001 From: KjellKod Date: Thu, 31 Oct 2013 03:28:08 -0600 Subject: [PATCH] compile error fixes after major g2log-dev influence-refactoring --- g2log/src/crashhandler.hpp | 50 ++--- g2log/src/crashhandler_unix.cpp | 296 +++++++++++++++--------------- g2log/src/g2filesink.cpp | 25 +-- g2log/src/g2filesink.hpp | 2 +- g2log/src/g2log.cpp | 150 ++++----------- g2log/src/g2log.hpp | 114 +++++------- g2log/src/g2loglevels.cpp | 4 + g2log/src/g2loglevels.hpp | 9 +- g2log/src/g2logmessage.cpp | 312 +++++++++++++------------------- g2log/src/g2logmessage.hpp | 149 ++++++--------- g2log/src/g2logworker.cpp | 18 +- g2log/src/g2logworker.hpp | 2 +- g2log/src/g2sink.h | 85 +++++---- g2log/src/g2sinkwrapper.h | 4 +- g2log/src/g2time.cpp | 1 - 15 files changed, 522 insertions(+), 699 deletions(-) diff --git a/g2log/src/crashhandler.hpp b/g2log/src/crashhandler.hpp index 5421fec..94342d9 100644 --- a/g2log/src/crashhandler.hpp +++ b/g2log/src/crashhandler.hpp @@ -1,35 +1,41 @@ #ifndef CRASH_HANDLER_H_ #define CRASH_HANDLER_H_ /** ========================================================================== -* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes -* with no warranties. This code is yours to share, use and modify with no -* strings attached and no restrictions or obligations. -* ============================================================================*/ + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * ============================================================================*/ #include #include +// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp +// implementationsfilen kan vara den samma + namespace g2 { - namespace internal { - /** \return signal_name. Ref: signum.hpp and \ref installSignalHandler */ - std::string signalName(int signal_number); +namespace internal { +/** \return signal_name. Ref: signum.hpp and \ref installSignalHandler */ +std::string signalName(int signal_number); - /** Re-"throw" a fatal signal, previously caught. This will exit the application - * This is an internal only function. Do not use it elsewhere. It is triggered - * from g2log, g2LogWorker after flushing messages to file */ - void exitWithDefaultSignalHandler(int signal_number); - } // end g2::internal +/** return calling thread's stackdump*/ +std::string stackdump(); + +/** Re-"throw" a fatal signal, previously caught. This will exit the application + * This is an internal only function. Do not use it elsewhere. It is triggered + * from g2log, g2LogWorker after flushing messages to file */ +void exitWithDefaultSignalHandler(int signal_number); +} // end g2::internal - // PUBLIC API: - /** Install signal handler that catches FATAL C-runtime or OS signals - See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE - See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.hpptm - SIGABRT ABORT (ANSI), abnormal termination - SIGFPE Floating point exception (ANSI) - SIGILL ILlegal instruction (ANSI) - SIGSEGV Segmentation violation i.e. illegal memory reference - SIGTERM TERMINATION (ANSI) */ - void installSignalHandler(); +// PUBLIC API: +/** Install signal handler that catches FATAL C-runtime or OS signals + See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE + See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.hpptm + SIGABRT ABORT (ANSI), abnormal termination + SIGFPE Floating point exception (ANSI) + SIGILL ILlegal instruction (ANSI) + SIGSEGV Segmentation violation i.e. illegal memory reference + SIGTERM TERMINATION (ANSI) */ +void installSignalHandler(); } #endif // CRASH_HANDLER_H_ diff --git a/g2log/src/crashhandler_unix.cpp b/g2log/src/crashhandler_unix.cpp index 49de013..1c27b10 100644 --- a/g2log/src/crashhandler_unix.cpp +++ b/g2log/src/crashhandler_unix.cpp @@ -1,11 +1,12 @@ /** ========================================================================== -* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes -* with no warranties. This code is yours to share, use and modify with no -* strings attached and no restrictions or obligations. -* ============================================================================*/ + * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * ============================================================================*/ #include "crashhandler.hpp" #include "g2log.hpp" +#include "g2logmessage.hpp" #include #include @@ -13,99 +14,42 @@ #error "crashhandler_unix.cpp used but it's a windows system" #endif - #include // getpid, #include #include #include #include +#include +#include -namespace -{ - // Dump of stack,. then exit through g2log background worker - // ALL thanks to this thread at StackOverflow. Pretty much borrowed from: - // Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes - void crashHandler(int signal_number, siginfo_t *info, void *unused_context) - { - const size_t max_dump_size = 50; - void* dump[max_dump_size]; - size_t size = backtrace(dump, max_dump_size); - char** messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address - - std::ostringstream oss; - oss << "Received fatal signal: " << g2::internal::signalName(signal_number); - oss << "(" << signal_number << ")" << std::endl; - oss << "\tPID: " << getpid() << std::endl; - - // dump stack: skip first frame, since that is here - for(size_t idx = 1; idx < size && messages != nullptr; ++idx) - { - char *mangled_name = 0, *offset_begin = 0, *offset_end = 0; - // find parantheses and +address offset surrounding mangled name - for (char *p = messages[idx]; *p; ++p) - { - if (*p == '(') - { - mangled_name = p; - } - else if (*p == '+') - { - offset_begin = p; - } - else if (*p == ')') - { - offset_end = p; - break; - } - } - - // if the line could be processed, attempt to demangle the symbol - if (mangled_name && offset_begin && offset_end && - mangled_name < offset_begin) - { - *mangled_name++ = '\0'; - *offset_begin++ = '\0'; - *offset_end++ = '\0'; - - int status; - char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status); - // if demangling is successful, output the demangled function name - if (status == 0) - { - oss << "\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+"; - oss << offset_begin << offset_end << std::endl; - } - // otherwise, output the mangled function name - else - { - oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+"; - oss << offset_begin << offset_end << std::endl; - } - free(real_name); // mallocated by abi::__cxa_demangle(...) - } - else - { - // no demangling done -- just dump the whole line - oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl; - } - } // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx) - - - - free(messages); - { // Local scope, trigger send +namespace { + // Dump of stack,. then exit through g2log background worker + // ALL thanks to this thread at StackOverflow. Pretty much borrowed from: + // Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes + void crashHandler(int signal_number, siginfo_t *info, void *unused_context) { using namespace g2::internal; - std::ostringstream fatal_stream; - fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl; - fatal_stream << oss.str() << std::endl; - fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl; - FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number); - FatalTrigger trigger(fatal_message); std::ostringstream oss; - std::cerr << fatal_message.message_ << std::endl << std::flush; - } // message sent to g2LogWorker - // wait to die -- will be inside the FatalTrigger - } + + std::ostringstream oss; + oss << "Received fatal signal: " << g2::internal::signalName(signal_number); + oss << "(" << signal_number << ")" << std::endl; + oss << "\tPID: " << getpid() << std::endl; + oss << stackdump(); + + { // Local scope, trigger send + std::ostringstream fatal_stream; + fatal_stream << oss.str() << std::endl; + fatal_stream << "\n***** SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl; + + FatalMessage fatal_message(fatal_stream.str(), signal_number); + const auto& crashMessage = fatal_message._crash_message; + std::cerr << crashMessage.toString() << std::endl << std::flush; + + FatalTrigger trigger(fatal_message); + std::ostringstream oss; + } // message sent to g2LogWorker by FatalTrigger + // wait to die -- will be inside the FatalTrigger + } } // end anonymous namespace @@ -115,73 +59,119 @@ namespace // Redirecting and using signals. In case of fatal signals g2log should log the fatal signal // and flush the log queue and then "rethrow" the signal to exit -namespace g2 -{ - // References: - // sigaction : change the default action if a specific signal is received - // http://linux.die.net/man/2/sigaction - // http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html - // - // signal: http://linux.die.net/man/7/signal and - // http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp - // - // memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here - // ,plenty of examples when both or either are used - // http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number - namespace internal - { - std::string signalName(int signal_number) - { - switch(signal_number) - { - case SIGABRT: return "SIGABRT";break; - case SIGFPE: return "SIGFPE"; break; - case SIGSEGV: return "SIGSEGV"; break; - case SIGILL: return "SIGILL"; break; - case SIGTERM: return "SIGTERM"; break; - default: - std::ostringstream oss; - oss << "UNKNOWN SIGNAL(" << signal_number << ")"; - return oss.str(); +namespace g2 { + // References: + // sigaction : change the default action if a specific signal is received + // http://linux.die.net/man/2/sigaction + // http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html + // + // signal: http://linux.die.net/man/7/signal and + // http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp + // + // memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here + // ,plenty of examples when both or either are used + // http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number + namespace internal { + std::string stackdump() { + std::ostringstream oss; + const size_t max_dump_size = 50; + void* dump[max_dump_size]; + size_t size = backtrace(dump, max_dump_size); + char** messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address + // dump stack: skip first frame, since that is here + for (size_t idx = 1; idx < size && messages != nullptr; ++idx) { + char *mangled_name = 0, *offset_begin = 0, *offset_end = 0; + // find parantheses and +address offset surrounding mangled name + for (char *p = messages[idx]; *p; ++p) { + if (*p == '(') { + mangled_name = p; + } else if (*p == '+') { + offset_begin = p; + } else if (*p == ')') { + offset_end = p; + break; + } + } + + // if the line could be processed, attempt to demangle the symbol + if (mangled_name && offset_begin && offset_end && + mangled_name < offset_begin) { + *mangled_name++ = '\0'; + *offset_begin++ = '\0'; + *offset_end++ = '\0'; + + int status; + char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + oss << "\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+"; + oss << offset_begin << offset_end << std::endl; + }// otherwise, output the mangled function name + else { + oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+"; + oss << offset_begin << offset_end << std::endl; + } + free(real_name); // mallocated by abi::__cxa_demangle(...) + } else { + // no demangling done -- just dump the whole line + oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl; + } + } // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx) + free(messages); + return oss.str(); } - } - // Triggered by g2log->g2LogWorker after receiving a FATAL trigger - // which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught. - // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT - void exitWithDefaultSignalHandler(int signal_number) - { - std::cerr << "Exiting - FATAL SIGNAL: " << signal_number << " " << std::flush; + + std::string signalName(int signal_number) { + switch (signal_number) { + case SIGABRT: return "SIGABRT"; break; + case SIGFPE: return "SIGFPE"; break; + case SIGSEGV: return "SIGSEGV"; break; + case SIGILL: return "SIGILL"; break; + case SIGTERM: return "SIGTERM"; break; + default: + std::ostringstream oss; + oss << "UNKNOWN SIGNAL(" << signal_number << ")"; + return oss.str(); + } + } + + // Triggered by g2log->g2LogWorker after receiving a FATAL trigger + // which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught. + // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT + + + void exitWithDefaultSignalHandler(int signal_number) { + std::cerr << "Exiting - FATAL SIGNAL: " << signal_number << " " << std::flush; + struct sigaction action; + memset(&action, 0, sizeof (action)); // + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_DFL; // take default action for the signal + sigaction(signal_number, &action, NULL); + kill(getpid(), signal_number); + abort(); // should never reach this + } + } // end g2::internal + + + void installSignalHandler() { struct sigaction action; - memset(&action, 0, sizeof(action)); // + memset(&action, 0, sizeof (action)); sigemptyset(&action.sa_mask); - action.sa_handler = SIG_DFL; // take default action for the signal - sigaction(signal_number, &action, NULL); - kill(getpid(), signal_number); - abort(); // should never reach this - } - } // end g2::internal + action.sa_sigaction = &crashHandler; // callback to crashHandler for fatal signals + // sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction + action.sa_flags = SA_SIGINFO; - - void installSignalHandler() - { - struct sigaction action; - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = &crashHandler; // callback to crashHandler for fatal signals - // sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction - action.sa_flags = SA_SIGINFO; - - // do it verbose style - install all signal actions - if(sigaction(SIGABRT, &action, NULL) < 0) - perror("sigaction - SIGABRT"); - if(sigaction(SIGFPE, &action, NULL) < 0) - perror("sigaction - SIGFPE"); - if(sigaction(SIGILL, &action, NULL) < 0) - perror("sigaction - SIGILL"); - if(sigaction(SIGSEGV, &action, NULL) < 0) - perror("sigaction - SIGSEGV"); - if(sigaction(SIGTERM, &action, NULL) < 0) - perror("sigaction - SIGTERM"); - } + // do it verbose style - install all signal actions + if (sigaction(SIGABRT, &action, NULL) < 0) + perror("sigaction - SIGABRT"); + if (sigaction(SIGFPE, &action, NULL) < 0) + perror("sigaction - SIGFPE"); + if (sigaction(SIGILL, &action, NULL) < 0) + perror("sigaction - SIGILL"); + if (sigaction(SIGSEGV, &action, NULL) < 0) + perror("sigaction - SIGSEGV"); + if (sigaction(SIGTERM, &action, NULL) < 0) + perror("sigaction - SIGTERM"); + } } // end namespace g2 diff --git a/g2log/src/g2filesink.cpp b/g2log/src/g2filesink.cpp index c958160..2195822 100644 --- a/g2log/src/g2filesink.cpp +++ b/g2log/src/g2filesink.cpp @@ -42,22 +42,25 @@ FileSink::~FileSink() { exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"}); std::cerr << exit_msg << std::flush; } -void FileSink::fileWrite(internal::LogEntry message) { - std::ofstream & out(filestream()); - auto system_time = systemtime_now(); - auto steady_time = std::chrono::steady_clock::now(); - out << "\n" << localtime_formatted(system_time, date_formatted); - out << " " << localtime_formatted(system_time, time_formatted); // TODO: time kommer från LogEntry - out << "." << std::chrono::duration_cast(steady_time - _steady_start_time).count(); - out << "\t" << message << std::flush; +void FileSink::fileWrite(LogMessage message) { + std::ofstream& out(filestream()); + out << "msg1: \n" << message.toString() << std::flush; + + out << "msg2:\n" << message.timestamp() << "." << message.microseconds() << message.level(); +// << " [" << message.file() << "/" << message.function() << " L" << message.line() << "] " + // << message.expression() << "\t" << '"' << message.message() << '"' << std::flush; } std::string FileSink::changeLogFile(const std::string& directory) { + + auto now = g2::systemtime_now(); + auto now_formatted = g2::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted}); + std::string file_name = createLogFileName(_log_prefix_backup); std::string prospect_log = directory + file_name; std::unique_ptr log_stream = createLogFile(prospect_log); if (nullptr == log_stream) { - fileWrite("Unable to change log file. Illegal filename or busy? Unsuccessful log name was:" + prospect_log); + filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; return {}; // no success } @@ -65,7 +68,7 @@ std::string FileSink::changeLogFile(const std::string& directory) { std::ostringstream ss_change; ss_change << "\n\tChanging log file from : " << _log_file_with_path; ss_change << "\n\tto new location: " << prospect_log << "\n"; - fileWrite(ss_change.str().c_str()); + filestream() << now_formatted << ss_change.str(); ss_change.str(""); std::string old_log = _log_file_with_path; @@ -73,7 +76,7 @@ std::string FileSink::changeLogFile(const std::string& directory) { _outptr = std::move(log_stream); ss_change << "\n\tNew log file. The previous log file was at: "; ss_change << old_log; - fileWrite(ss_change.str()); + filestream() << now_formatted << ss_change.str(); return _log_file_with_path; } std::string FileSink::fileName() { diff --git a/g2log/src/g2filesink.hpp b/g2log/src/g2filesink.hpp index 20f04f0..862750f 100644 --- a/g2log/src/g2filesink.hpp +++ b/g2log/src/g2filesink.hpp @@ -20,7 +20,7 @@ public: FileSink(const std::string& log_prefix, const std::string& log_directory); virtual ~FileSink(); - void fileWrite(internal::LogEntry message); + void fileWrite(LogMessage message); std::string changeLogFile(const std::string& directory); std::string fileName(); diff --git a/g2log/src/g2log.cpp b/g2log/src/g2log.cpp index 7cac45b..cbc7213 100644 --- a/g2log/src/g2log.cpp +++ b/g2log/src/g2log.cpp @@ -17,131 +17,49 @@ * ********************************************* */ #include "g2log.hpp" - +#include "g2log.ipp" #include // vsnprintf -#include #include #include #include "g2logworker.hpp" #include "crashhandler.hpp" #include "g2loglevels.hpp" -#include "g2time.hpp" - -namespace g2 -{ - namespace internal - { - std::once_flag g_start_time_flag; - g2::steady_time_point g_start_time; - std::once_flag g_initialize_flag; - g2LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main) - std::mutex g_logging_init_mutex; - - bool isLoggingInitialized(){ - return g_logger_instance != nullptr; - } - - long microsecondsCounter() - { - std::call_once(g_start_time_flag, [](){g_start_time = std::chrono::steady_clock::now();}); - steady_time_point now = std::chrono::steady_clock::now(); - return std::chrono::duration_cast(g_start_time - now).count(); - } - - void saveMessage(const LogEntry& log_entry) {g_logger_instance->save(log_entry);} - } // internal - // signalhandler and internal clock is only needed to install once - // for unit testing purposes the initializeLogging might be called - // several times... for all other practical use, it shouldn't! - void initializeLogging(g2LogWorker *bgworker) - { - std::call_once(internal::g_initialize_flag, [](){installSignalHandler();}); - std::lock_guard lock(internal::g_logging_init_mutex); +namespace g2 { + namespace internal { + std::once_flag g_initialize_flag; + std::mutex g_logging_init_mutex; + + // ref: g2log.ipp + bool isLoggingInitialized() { + return g_logger_instance != nullptr; + } + + /// Used for test purposes. Should rarely, if ever, be used in production code + g2LogWorker* shutDownLogging() { + std::lock_guard lock(internal::g_logging_init_mutex); + g2LogWorker *backup = internal::g_logger_instance; + internal::g_logger_instance = nullptr; + return backup; + } + } // g2::internal + + + void initializeLogging(g2LogWorker *bgworker) { + std::call_once(internal::g_initialize_flag, []() { + installSignalHandler(); }); + std::lock_guard lock(internal::g_logging_init_mutex); #ifdef G2_DYNAMIC_LOGGING - setLogLevel(DEBUG, true); - setLogLevel(INFO, true); - setLogLevel(WARNING, true); - setLogLevel(FATAL, true); + setLogLevel(DEBUG, true); + setLogLevel(INFO, true); + setLogLevel(WARNING, true); + setLogLevel(FATAL, true); #endif - CHECK(!internal::isLoggingInitialized()); - CHECK(bgworker != nullptr); - internal::g_logger_instance = bgworker; -} - - - g2LogWorker* shutDownLogging() - { - std::lock_guard lock(internal::g_logging_init_mutex); - g2LogWorker *backup = internal::g_logger_instance; - internal::g_logger_instance = nullptr; - return backup; - } - - - namespace internal - { - void fatalCallToLogger(FatalMessage message) - { - // real fatal call to logger - internal::g_logger_instance->fatal(message); - } - - void fatalCallForUnitTest(g2::internal::FatalMessage fatal_message) - { - // mock fatal call, not to logger: used by unit test - assert(internal::g_logger_instance != nullptr); - internal::g_logger_instance->save(fatal_message.message_); // calling 'save' instead of 'fatal' - throw std::runtime_error(fatal_message.message_); - } - - // By default this function pointer goes to \ref fatalCall; - void (*g_fatal_to_g2logworker_function_ptr)(FatalMessage) = fatalCallToLogger; - - - - - // The default, initial, handling to send a 'fatal' event to g2logworker - // the caller will stay here, eternally, until the software is aborted - void fatalCall(FatalMessage message) - { - g_fatal_to_g2logworker_function_ptr(message); - while(true){std::this_thread::sleep_for(std::chrono::seconds(1));} - } - - - - // REPLACE fatalCallToLogger for fatalCallForUnitTest - // This function switches the function pointer so that only - // 'unitTest' mock-fatal calls are made. - void changeFatalInitHandlerForUnitTesting() - { - g_fatal_to_g2logworker_function_ptr = fatalCallForUnitTest; - } - - - // represents the actual fatal message - FatalMessage::FatalMessage(std::string message, FatalType type, int signal_id) - : message_(message) - , type_(type) - , signal_id_(signal_id){} - - - - // used to RAII trigger fatal message sending to g2LogWorker - FatalTrigger::FatalTrigger(const FatalMessage &message) - : message_(message){} - - // at destruction, flushes fatal message to g2LogWorker - FatalTrigger::~FatalTrigger() - { - // either we will stay here eternally, or it's in unit-test mode - // then we throw a std::runtime_error (and never hit sleep) - fatalCall(message_); - } - - - } // g2::internal + CHECK(!internal::isLoggingInitialized()); + CHECK(bgworker != nullptr); + internal::g_logger_instance = bgworker; + } + } // g2 diff --git a/g2log/src/g2log.hpp b/g2log/src/g2log.hpp index 3cb810e..2a34e0d 100644 --- a/g2log/src/g2log.hpp +++ b/g2log/src/g2log.hpp @@ -24,10 +24,9 @@ #include #include "g2loglevels.hpp" -#include "g2logmessage.hpp" +#include "g2LogMessageBuilder.hpp" class g2LogWorker; - #if !(defined(__PRETTY_FUNCTION__)) #define __PRETTY_FUNCTION__ __FUNCTION__ #endif @@ -35,101 +34,80 @@ class g2LogWorker; /** namespace for LOG() and CHECK() frameworks - * Histroy lesson: Why the names 'g2' and 'g2log'?: - * The framework was made in my own free time as PUBLIC DOMAIN but the - * first commercial project to use it used 'g2' as an internal denominator for - * the current project. g2 as in 'generation 2'. I decided to keep the g2 and g2log names - * to give credit to the people in that project (you know who you are :) and I guess also - * for 'sentimental' reasons. That a big influence was google's glog is just a happy - * concidence or subconscious choice. Either way g2log became the name for this logger. - * - * --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod - */ -namespace g2 -{ + * History lesson: Why the names 'g2' and 'g2log'?: + * The framework was made in my own free time as PUBLIC DOMAIN but the + * first commercial project to use it used 'g2' as an internal denominator for + * the current project. g2 as in 'generation 2'. I decided to keep the g2 and g2log names + * to give credit to the people in that project (you know who you are :) and I guess also + * for 'sentimental' reasons. That a big influence was google's glog is just a happy + * concidence or subconscious choice. Either way g2log became the name for this logger. + * + * --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod + */ +namespace g2 { +struct LogMessage; - /** Should be called at very first startup of the software with \ref g2LogWorker - * pointer. Ownership of the \ref g2LogWorker is the responsibilkity of the caller */ - void initializeLogging(g2LogWorker *logger); +/** Should be called at very first startup of the software with \ref g2LogWorker + * pointer. Ownership of the \ref g2LogWorker is the responsibilkity of the caller */ +void initializeLogging(g2LogWorker *logger); - /** Shutdown the logging by making the pointer to the background logger to nullptr +namespace internal { +// Save the created LogMessage to any existing sinks +void saveMessage(const g2::LogMessage& log_entry); + +/** FOR TESTING PURPOSES + * Shutdown the logging by making the pointer to the background logger to nullptr * The \ref pointer to the g2LogWorker is owned by the instantniater \ref initializeLogging * and is not deleted. By restoring the ptr to nullptr we can re-initialize it later again. + * * This is kept for test reasons and should normally not be used */ - g2LogWorker* shutDownLogging(); +g2LogWorker* shutDownLogging(); + +bool isLoggingInitialized(); - // defined here but should't not have to be used outside the g2log - namespace internal - { - typedef const std::string& LogEntry; - - bool isLoggingInitialized(); - long microsecondsCounter(); - void saveMessage(const LogEntry& log_entry); +/** FOR TESTING PURPOSES: @ref g2log.ipp + * By default the g2log will call g2LogWorker::fatal(...) which will + * abort() the system after flushing the logs to file. This makes unit + * test of FATAL level cumbersome. A work around is to change the + * fatal call' which can be done here */ +void changeFatalInitHandlerForUnitTesting(); - /** By default the g2log will call g2LogWorker::fatal(...) which will - * abort() the system after flushing the logs to file. This makes unit - * test of FATAL level cumbersome. A work around is to change the - * 'fatal call' which can be done here */ - void changeFatalInitHandlerForUnitTesting(); - - - - - /** Trigger for flushing the message queue and exiting the application - A thread that causes a FatalMessage will sleep forever until the - application has exited (after message flush) */ - struct FatalMessage - { - enum FatalType {kReasonFatal, kReasonOS_FATAL_SIGNAL}; - FatalMessage(std::string message, FatalType type, int signal_id); - - std::string message_; - FatalType type_; - int signal_id_; - }; - - - // At RAII scope end this struct will trigger a FatalMessage sending - struct FatalTrigger - { - FatalTrigger(const FatalMessage& message); - ~FatalTrigger(); - FatalMessage message_; - }; - - } // end namespace internal +} // end namespace internal } // end namespace g2 -#define INTERNAL_LOG_MESSAGE(level) g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, level) + + +#define INTERNAL_LOG_MESSAGE(level) g2::internal::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, level) + +//LogMessageBuilder(const char* file, const int line, const char* function, const LEVELS& level){} #define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \ - g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, boolean_expression) - + g2::internal::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, ::FATAL) +//.setExpression(boolean_expression) // LOG(level) is the API for the stream log -#define LOG(level) if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream() +#define LOG(level) if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream() // 'Conditional' stream log #define LOG_IF(level, boolean_expression) \ if(true == boolean_expression) \ - if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream() + if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream() // 'Design By Contract' stream API. For Broken Contracts: // unit testing: it will throw std::runtime_error when a contract breaks // I.R.L : it will exit the application by using fatal signal SIGABRT #define CHECK(boolean_expression) \ - if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).messageStream() + if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream() /** For details please see this - * REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format - * \verbatim - * + * REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format + * \verbatim + * There are different %-codes for different variable types, as well as options to limit the length of the variables and whatnot. Code Format diff --git a/g2log/src/g2loglevels.cpp b/g2log/src/g2loglevels.cpp index bda03f7..aa31490 100644 --- a/g2log/src/g2loglevels.cpp +++ b/g2log/src/g2loglevels.cpp @@ -16,6 +16,10 @@ namespace g2 { namespace internal { + bool wasFatal(const LEVELS& level) { + return level.value > WARNING.value; + } + std::atomic g_log_level_status[4]; // DEBUG, INFO, WARNING, FATAL } // internal diff --git a/g2log/src/g2loglevels.hpp b/g2log/src/g2loglevels.hpp index a84a5f6..07f31ba 100644 --- a/g2log/src/g2loglevels.hpp +++ b/g2log/src/g2loglevels.hpp @@ -19,9 +19,16 @@ struct LEVELS { const int value; const std::string text; -} const DEBUG = {0, "DEBUG"}, INFO = {1, "INFO"}, WARNING = {2, "WARNING"}, FATAL = {3, "FATAL"}; +}; +const LEVELS DEBUG = {0, "DEBUG"}, INFO = {1, "INFO"}, WARNING = {2, "WARNING"}, FATAL = {3, "FATAL"}; namespace g2 { +namespace internal { + const LEVELS CONTRACT = {4, "CONTRACT"}, FATAL_SIGNAL{5, "FATAL_SIGNAL"}; + + bool wasFatal(const LEVELS& level); +} + #ifdef G2_DYNAMIC_LOGGING // Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL} // Obviously: 'enabled_status' set to 'false' - means to disable that log level diff --git a/g2log/src/g2logmessage.cpp b/g2log/src/g2logmessage.cpp index e391068..b043762 100644 --- a/g2log/src/g2logmessage.cpp +++ b/g2log/src/g2logmessage.cpp @@ -1,202 +1,146 @@ /** ========================================================================== -* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes -* with no warranties. This code is yours to share, use and modify with no -* strings attached and no restrictions or obligations. -* ============================================================================ -* Filename:g2logmessage.cpp Part of Framework for Logging and Design By Contract -* Created: 2012 by Kjell Hedström -* -* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc -* ********************************************* */ + * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * ============================================================================ + * Filename:g2logmessage.cpp Part of Framework for Logging and Design By Contract + * Created: 2012 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc + * ********************************************* */ #include "g2logmessage.hpp" -#include -#include // exceptions -#include -#include -#include - -#include "g2log.hpp" #include "g2time.hpp" - -struct LogEntryMessageImpl -{ - LogEntryMessageImpl(const std::string &file, const int line, const std::string& function, - const LEVELS& level, const std::string contract_expression); - - const std::string file_; - const int line_; - const std::string function_; - const LEVELS level_; - const std::time_t timestamp_; - const long microseconds_; - const std::string message_; - const std::string expression_; // only with content for CHECK(...) calls - - std::ostringstream stream_; -}; -LogEntryMessageImpl::LogEntryMessageImpl(const std::string &file, const int line, - const std::string& function, const LEVELS& level, - const std::string contract_expression) - : file_(file), line_(line), function_(function), level_(level) - , timestamp_(g2::systemtime_now()) - , microseconds_(g2::internal::microsecondsCounter()) - , expression_(contract_expression) -{} - - - - - - -std::string LogEntryMessage::line(){return std::to_string(pimpl_->line_);} //ltorreturn {""};} // // pimpl_->line_; -std::string LogEntryMessage::file(){return pimpl_->file_;} -std::string LogEntryMessage::function(){return pimpl_->function_;} -std::string LogEntryMessage::level(){return pimpl_->level_.text;} - -std::string LogEntryMessage::timestamp() -{ - using namespace g2; - std::ostringstream oss; - // YYYY/MM/DD HH:MM:SS -- ref g2time.hpp/cpp - oss << localtime_formatted(pimpl_->timestamp_, internal::date_formatted); - oss << " "; - oss << localtime_formatted(pimpl_->timestamp_, internal::time_formatted); - return oss.str(); -} - -std::string LogEntryMessage::microseconds(){return std::to_string(pimpl_->microseconds_);} -std::string LogEntryMessage::message(){return pimpl_->stream_.str();} -std::string LogEntryMessage::expression(){return pimpl_->expression_;} - -std::ostringstream& LogEntryMessage::saveMessageByStream(){return pimpl_->stream_;} - - - - -LogEntryMessage::LogEntryMessage(const std::string &file, const int line, const std::string& function, - const LEVELS& level, const std::string contract_expression) - : pimpl_(new LogEntryMessageImpl(file, line, function, level, contract_expression)) -{ } - - -LogEntryMessage::LogEntryMessage(const std::string &file, const int line, const std::string& function, - const LEVELS& level) - : LogEntryMessage(file, line, function, level, "") -{ } - - - - - +#include "crashhandler.hpp" +#include // exceptions +#include "g2log.hpp" +#include "g2log.ipp" namespace g2 { - namespace constants - { - const int kMaxMessageSize = 2048; - const std::string kTruncatedWarningText = "[...truncated...]"; - } - - /** - Thanks to: http://www.cplusplus.com/reference/string/string/find_last_of/ - * Splits string at the last '/' or '\\' separator - * example: "/mnt/something/else.cpp" --> "else.cpp" - * "c:\\windows\\hello.hpp" --> hello.hpp - * "this.is.not-a-path.hpp" -->"this.is.not-a-path.hpp" */ - std::string splitFileName(const std::string& str) - { - size_t found; - found = str.find_last_of("(/\\"); - return str.substr(found+1); - } - - namespace internal { - LogMessage::LogMessage(const std::string &file, const int line, const std::string& function, const LEVELS &level) - : file_(file) - , line_(line) - , function_(function) - , level_(level) - {} + std::string LogMessage::line() const { + return std::to_string(_pimpl->_line); + } - // TODO: Madness duh! refactor this till sanity! - // All logmessage text should be done in the bacground thread - // Only ONE logmessage? Is needed "expression can just be empty for non-expression types" - LogMessage::~LogMessage() - { - using namespace internal; + + std::string LogMessage::file() const { + return _pimpl->_file; + } + + + std::string LogMessage::function() const { + return _pimpl->_function; + } + + + std::string LogMessage::level() const { + return _pimpl->_level.text; + } + + + std::string LogMessage::timestamp(const std::string & time_look) const { std::ostringstream oss; - const bool fatal = (FATAL.value == level_.value); - oss << level_.text << " [" << splitFileName(file_); - if(fatal) - oss << "\n[ *******\tEXIT trigger caused by LOG(FATAL): \n\t"; - - oss << " at: " << function_ ; // previousl only at FATAL.. make configurable but always on FATAL - oss << " L: " << line_ << "]\t"; - - const std::string str(stream_.str()); - if(!str.empty()) - { - oss << '"' << str << '"'; - } - log_entry_ += oss.str(); - - if(!isLoggingInitialized() ) - { - std::cerr << "Did you forget to call g2::InitializeLogging(g2LogWorker*) in your main.cpp?" << std::endl; - std::cerr << log_entry_ << std::endl << std::flush; - throw std::runtime_error("Logger not initialized with g2::InitializeLogging(g2LogWorker*) for msg:\n" + log_entry_); - } - - if(fatal) // os_fatal is handled by crashhandlers - { - { // local scope - to trigger FatalMessage sending - FatalMessage::FatalType fatal_type(FatalMessage::kReasonFatal); - FatalMessage fatal_message(log_entry_, fatal_type, SIGABRT); - FatalTrigger trigger(fatal_message); - std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush; - } // will send to worker - } - internal::saveMessage(log_entry_); // message saved to g2LogWorker - } + oss << localtime_formatted(_pimpl->_timestamp, time_look); + return oss.str(); + } - void LogMessage::messageSave(const char *printf_like_message, ...) - { - char finished_message[constants::kMaxMessageSize]; - va_list arglist; - va_start(arglist, printf_like_message); - const int nbrcharacters = vsnprintf(finished_message, sizeof(finished_message), printf_like_message, arglist); - va_end(arglist); - if (nbrcharacters <= 0) - { - stream_ << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message"; - stream_ << '"' << printf_like_message << '"' << std::endl; - } - else if (nbrcharacters > constants::kMaxMessageSize) - { - stream_ << finished_message << constants::kTruncatedWarningText; - } - else - { - stream_ << finished_message; - } - } + std::string LogMessage::microseconds() const { + return std::to_string(_pimpl->_microseconds); + } - LogContractMessage::LogContractMessage(const std::string &file, const int line, - const std::string& function, const std::string &boolean_expression) - : LogMessage(file, line, function, FATAL) - , expression_(boolean_expression) - {} + std::string LogMessage::message() const { + return _pimpl->_stream.str(); + } - LogContractMessage::~LogContractMessage() - { + + std::string LogMessage::expression() const { + return _pimpl->_expression; + } + + + bool LogMessage::wasFatal() const { + return internal::wasFatal(_pimpl->_level); + } + + // YYYY/MM/DD HH:MM:SS -- ref g2time.hpp/cpp + //out << "\n" << localtime_formatted(system_time, date_formatted); + //out << " " << localtime_formatted(system_time, time_formatted); // TODO: time kommer från LogEntry + //out << "." << std::chrono::duration_cast(steady_time - _steady_start_time).count(); + //out << "\t" << message << std::flush; + + + std::string LogMessage::toString() const { std::ostringstream oss; - oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t"; - log_entry_ = oss.str(); - } + oss << "\n" << timestamp() << "." << microseconds() << "\t"; - } // internal + + oss << level() << " [" << file(); + oss << " L: " << line() << "]\t"; + + // Non-fatal Log Message + if (false == wasFatal()) { + oss << '"' << message() << '"'; + return oss.str(); + } + + if (internal::FATAL_SIGNAL.value == _pimpl->_level.value) { + oss << "\n\n***** FATAL SIGNAL RECEIVED ******* " << std::endl; + oss << '"' << message() << '"'; + return oss.str(); + } + + + // Not crash scenario but LOG or CONTRACT + auto level_value = _pimpl->_level.value; + if (FATAL.value == level_value) { + oss << "\n\t*******\tEXIT trigger caused LOG(FATAL) entry: \n\t"; + oss << '"' << message() << '"'; + oss << "\n*******\tSTACKDUMP *******\n" << internal::stackdump(); + } else if (internal::CONTRACT.value == level_value) { + oss << "\n\t *******\tEXIT trigger caused by broken Contract: CHECK(" << _pimpl->_expression << ")\n\t"; + oss << '"' << message() << '"'; + oss << "\n*******\tSTACKDUMP *******\n" << internal::stackdump(); + } else { + oss << "\n\t*******\tUNKNOWN Log Message Type\n" << '"' << message() << '"'; + } + + return oss.str(); + } + + + std::ostringstream& LogMessage::stream() { + return _pimpl->_stream; + } + + + LogMessage(std::shared_ptr details) + : _pimpl(details) { } + + + + namespace internal { + + + FatalMessage::FatalMessage(const std::string& crash_message, int signal_id) + : FatalMessage({std::make_shared(crash_message)}, signal_id) { } + + + FatalMessage(const LogMessage& message, int signal_id) + : _crash_message(message), signal_id_(signal_id) { } + + + FatalTrigger::FatalTrigger(const FatalMessage& exit_message) : _fatal_message(exit_message) { } + + + FatalTrigger::~FatalTrigger() { + // At destruction, flushes fatal message to g2LogWorker + // either we will stay here until the background worker has received the fatal + // message, flushed the crash message to the sinks and exits with the same fatal signal + //..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep) + fatalCall(_fatal_message); + } + } // internal } // g2 diff --git a/g2log/src/g2logmessage.hpp b/g2log/src/g2logmessage.hpp index 679a21b..58d64fa 100644 --- a/g2log/src/g2logmessage.hpp +++ b/g2log/src/g2logmessage.hpp @@ -1,16 +1,15 @@ /** ========================================================================== -* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes -* with no warranties. This code is yours to share, use and modify with no -* strings attached and no restrictions or obligations. -* ============================================================================ -* Filename:g2logmessage.hpp Part of Framework for Logging and Design By Contract -* Created: 2012 by Kjell Hedström -* -* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc -* ********************************************* */ + * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes + * with no warranties. This code is yours to share, use and modify with no + * strings attached and no restrictions or obligations. + * ============================================================================ + * Filename:g2logmessage.hpp Part of Framework for Logging and Design By Contract + * Created: 2012 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc + * ********************************************* */ -#ifndef G2_LOG_MESSAGE_HPP -#define G2_LOG_MESSAGE_HPP +#pragma once #include @@ -21,91 +20,53 @@ #include "g2log.hpp" #include "g2loglevels.hpp" +#include "g2time.hpp" -struct LogEntryMessageImpl; -// Notice: Currently the LogEntryMessage does not use polymorphism to -// implement the "expression" part even though it is only used for CHECK(...) -// contracts and not by normal messages -class LogEntryMessage -{ - std::unique_ptr pimpl_; - std::string file(); - std::string line(); - std::string function(); - std::string level(); - std::string timestamp(); - std::string microseconds(); - std::string message(); - std::string expression(); +namespace g2 { +struct LogMessageImpl; - std::ostringstream& saveMessageByStream(); -#ifndef __GNUC__ -#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em -#endif - // Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first - // compiler given argument 'this'. This must be supplied as well, hence '2,3' - // ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod - void saveMessage(const char *printf_like_message, ...) - __attribute__((format(printf,2,3) )); -public: - // replace for factory function instead - // createContractMessage - // createMessage - LogEntryMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level); - LogEntryMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level, const std::string boolean_expression); +struct LogMessage { + mutable std::shared_ptr _pimpl; + std::string file() const; + std::string line() const; + std::string function() const; + std::string level() const; + + std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const; + std::string microseconds() const; + std::string message() const; + std::string expression() const; + + bool wasFatal() const; + + // convert all content to ONE string + std::string toString() const; + + + std::ostringstream& stream(); + explicit LogMessage(std::shared_ptr details); + ~LogMessage() = default; }; + + + namespace internal { + /** Trigger for flushing the message queue and exiting the application + * A thread that causes a FatalMessage will sleep forever until the + * application has exited (after message flush) */ + struct FatalMessage { + FatalMessage(const std::string& message, int signal_id); + FatalMessage(const LogMessage& message, int signal_id); + ~FatalMessage() = default; + mutable LogMessage _crash_message; + int signal_id_; + }; - - - -namespace g2 { namespace internal { - - // temporary message construct to capture log input and push to g2logworker - class LogMessage - { - public: - LogMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level); - virtual ~LogMessage(); // flush the message - std::ostringstream& messageStream(){return stream_;} - - - // The __attribute__ generates compiler warnings if illegal "printf" format - // IMPORTANT: You muse enable the compiler flag '-Wall' for this to work! - // ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html -#ifndef __GNUC__ -#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em -#endif - // Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first - // compiler given argument 'this'. This must be supplied as well, hence '2,3' - // ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod - void messageSave(const char *printf_like_message, ...) - __attribute__((format(printf,2,3) )); - - - protected: - const std::string file_; - const int line_; - const std::string function_; - const LEVELS& level_; - std::ostringstream stream_; - std::string log_entry_; - }; - - - // 'Design-by-Contract' temporary messsage construction - class LogContractMessage : public LogMessage - { - public: - LogContractMessage(const std::string &file, const int line, - const std::string &function, const std::string &boolean_expression); - virtual ~LogContractMessage(); // at destruction will flush the message - - protected: - const std::string expression_; - }; - } // internal - } // g2 - - -#endif //G2_LOG_MESSAGE_HPP + // At RAII scope end this struct will trigger a FatalMessage sending + struct FatalTrigger { + explicit FatalTrigger(const FatalMessage& exit_message); + ~FatalTrigger(); + FatalMessage _fatal_message; + }; + } // internal +} // g2 diff --git a/g2log/src/g2logworker.cpp b/g2log/src/g2logworker.cpp index e79eb10..f14249f 100644 --- a/g2log/src/g2logworker.cpp +++ b/g2log/src/g2logworker.cpp @@ -39,25 +39,25 @@ struct g2LogWorkerImpl { std::cout << "g2logworker active object destroyed. done sending exit messages to all sinks\n"; } - void bgSave(g2::internal::LogEntry msg) { + void bgSave(const g2::LogMessage& msg) { for (auto& sink : _sinks) { sink->send(msg); } if (_sinks.empty()) { std::string err_msg{"g2logworker has no sinks. Message: ["}; - err_msg.append(msg).append({"]\n"}); + err_msg.append(msg.toString()).append({"]\n"}); std::cerr << err_msg; } } void bgFatal(g2::internal::FatalMessage fatal_message) { - auto entry = fatal_message.message_; + auto entry = fatal_message._crash_message; + entry.stream() << "\nExiting after fatal event. Log flushed sucessfully to disk.\n"; bgSave(entry); - std::string end_message{"Exiting after fatal event. Log flushed sucessfully to disk.\n"}; - bgSave(end_message); - std::cerr << "g2log sinks are flushed. Now exiting after receiving fatal event\n" << std::flush; + std::cerr << entry.toString() << std::endl; + std::cerr << "g2log sinks are flushed. Now exiting after receiving fatal event\n" << std::flush; _sinks.clear(); // flush all queues exitWithDefaultSignalHandler(fatal_message.signal_id_); @@ -77,9 +77,9 @@ g2LogWorker::g2LogWorker() g2LogWorker::~g2LogWorker() { _pimpl->_bg->send([this]{_pimpl->_sinks.clear();}); } - -void g2LogWorker::save(g2::internal::LogEntry msg) { - _pimpl->_bg->send([this, msg] { _pimpl->bgSave(msg); }); +// todo move operator +void g2LogWorker::save(g2::LogMessage msg) { + _pimpl->_bg->send([this, msg] { _pimpl->bgSave(msg); }); // TODO std::move } void g2LogWorker::fatal(g2::internal::FatalMessage fatal_message) { diff --git a/g2log/src/g2logworker.hpp b/g2log/src/g2logworker.hpp index 5b38796..8ad44d7 100644 --- a/g2log/src/g2logworker.hpp +++ b/g2log/src/g2logworker.hpp @@ -56,7 +56,7 @@ public: /// pushes in background thread (asynchronously) input messages to log file - void save(g2::internal::LogEntry entry); + void save(g2::LogMessage entry); /// Will push a fatal message on the queue, this is the last message to be processed /// this way it's ensured that all existing entries were flushed before 'fatal' diff --git a/g2log/src/g2sink.h b/g2log/src/g2sink.h index 1afb39d..a66b235 100644 --- a/g2log/src/g2sink.h +++ b/g2log/src/g2sink.h @@ -14,50 +14,63 @@ #include "g2sinkwrapper.h" #include "active.hpp" #include "g2future.h" - +#include "g2logmessage.hpp" namespace g2 { - namespace internal { - typedef std::function AsyncMessageCall; +namespace internal { +typedef std::function AsyncMessageCall; +/// The asynchronous Sink has an active object, incoming requests for actions +// will be processed in the background by the specific object the Sink represents. +// +// The Sink will wrap either +// a Sink with Message object receiving call +// or a Sink with a LogEntry (string) receving call +// +// The Sink can also be used through the SinkHandler to call Sink specific function calls +// Ref: send(Message) deals with incoming log entries (converted if necessary to string) +// Ref: send(Call call, Args... args) deals with calls +// to the real sink's API - /// The asynchronous Sink has an active object, incoming requests for actions - // will be processed in the background by the specific object the Sink represents. - // - // Ref: send(LogEntry) deals with incoming log entries - // Ref: send(Call call, Args... args) deals with calls - // to the real sink's API - template - struct Sink : public SinkWrapper { - std::shared_ptr _real_sink; - std::unique_ptr _bg; - AsyncMessageCall _default_log_call; - - +template +struct Sink : public SinkWrapper { + std::shared_ptr _real_sink; + std::unique_ptr _bg; + AsyncMessageCall _default_log_call; - template - Sink(std::shared_ptr sink, DefaultLogCall call) - : SinkWrapper { }, - _real_sink{sink}, - _bg(kjellkod::Active::createActive()), - _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) - { } + template + Sink(std::shared_ptr sink, DefaultLogCall call) + : SinkWrapper (), + _real_sink{sink}, + _bg(kjellkod::Active::createActive()), + _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { + } - virtual ~Sink() { - _bg.reset(); // TODO: to remove - } + Sink(std::shared_ptr sink, void(T::*Call)(std::string) ) + : SinkWrapper(), + _real_sink {sink}, + _bg(kjellkod::Active::createActive()) { + auto adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); + _default_log_call = [ = ](const LogMessage &m){adapter(m.toString());}; + } - void send(LogEntry msg) override { - _bg->send([this, msg]{_default_log_call(msg);}); - } + virtual ~Sink() { + _bg.reset(); // TODO: to remove + } - template - auto send(Call call, Args... args)-> std::future < decltype(bind(call, _real_sink.get(), args...)()) > { - return g2::spawn_task(std::bind(call, _real_sink.get(), args...), _bg.get()); - } - }; - } -} + void send(LogMessage msg) override { + _bg->send([this, msg] { + _default_log_call(msg); + }); + } + + template + auto send(Call call, Args... args)-> std::future < decltype(bind(call, _real_sink.get(), args...)()) > { + return g2::spawn_task(std::bind(call, _real_sink.get(), args...), _bg.get()); + } +}; +} // internal +} // g2 #endif /* G2SINK_IPP */ diff --git a/g2log/src/g2sinkwrapper.h b/g2log/src/g2sinkwrapper.h index 55b98ee..37f8ed7 100644 --- a/g2log/src/g2sinkwrapper.h +++ b/g2log/src/g2sinkwrapper.h @@ -15,10 +15,10 @@ namespace g2 { struct SinkWrapper { virtual ~SinkWrapper() { } - virtual void send(LogEntry msg) = 0; + virtual void send(LogMessage msg) = 0; }; } } -#endif /* G2SINKWRAPPER_IPP */ +#endif /* G2SINKWRAPPER_IPP */ diff --git a/g2log/src/g2time.cpp b/g2log/src/g2time.cpp index 6acdbb6..6906f50 100644 --- a/g2log/src/g2time.cpp +++ b/g2log/src/g2time.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include