From 6750efe8fe80a4ab6c9579b486073196c42c4ad8 Mon Sep 17 00:00:00 2001 From: Kjell Hedstrom Date: Thu, 16 Jul 2015 01:55:23 -0600 Subject: [PATCH] g2log.hpp is kept as a helper include. It will pull in the most frequent include files All other are under g3log. Example: #include #include etc This makes sure that the package manager (to be used soon) does NOT clutter any /usr/local/include space instead all the includes will be in /usr/local/include/g3log (or similar) --- Build.cmake | 8 +- CMakeLists.txt | 36 +- CPackLists.txt | 9 + CleanAll.cmake | 8 + GenerateMacroDefinitionsFile.cmake | 10 +- example/main_contract.cpp | 65 +-- example/main_fatal_choice.cpp | 398 +++++++++--------- example/main_sigsegv.cpp | 44 +- src/crashhandler.hpp | 63 --- src/crashhandler_unix.cpp | 235 ----------- src/crashhandler_windows.cpp | 259 ------------ src/g2filesink.cpp | 92 ---- src/g2filesink.hpp | 40 -- src/g2filesinkhelper.ipp | 123 ------ src/g2future.hpp | 65 --- src/g2log.cpp | 251 ----------- src/g2log.hpp | 221 +--------- src/g2logmessage.cpp | 205 --------- src/g2logworker.cpp | 138 ------ src/g2sink.hpp | 80 ---- src/g2sinkhandle.hpp | 55 --- src/g2time.cpp | 83 ---- src/g2time.hpp | 52 --- src/{ => g3log}/active.hpp | 65 +-- src/g3log/crashhandler.hpp | 63 +++ src/g3log/crashhandler_unix.cpp | 235 +++++++++++ src/g3log/crashhandler_windows.cpp | 259 ++++++++++++ src/g3log/filesink.cpp | 92 ++++ src/g3log/filesink.hpp | 42 ++ src/g3log/filesinkhelper.ipp | 123 ++++++ src/g3log/future.hpp | 63 +++ src/g3log/g3log.cpp | 252 +++++++++++ src/g3log/g3log.hpp | 216 ++++++++++ .../logcapture.cpp} | 13 +- .../logcapture.hpp} | 25 +- src/{g2loglevels.cpp => g3log/loglevels.cpp} | 29 +- src/{g2loglevels.hpp => g3log/loglevels.hpp} | 45 +- src/g3log/logmessage.cpp | 201 +++++++++ .../logmessage.hpp} | 87 ++-- src/g3log/logworker.cpp | 136 ++++++ src/{g2logworker.hpp => g3log/logworker.hpp} | 33 +- .../moveoncopy.hpp} | 26 +- src/g3log/shared_queue.hpp | 80 ++++ src/g3log/sink.hpp | 79 ++++ src/g3log/sinkhandle.hpp | 55 +++ .../sinkwrapper.hpp} | 23 +- src/g3log/stacktrace_windows.cpp | 222 ++++++++++ src/{ => g3log}/stacktrace_windows.hpp | 27 +- src/g3log/std2_make_unique.hpp | 48 +++ src/{ => g3log}/stlpatch_future.hpp | 30 +- src/g3log/time.cpp | 77 ++++ src/g3log/time.hpp | 51 +++ src/shared_queue.hpp | 80 ---- src/stacktrace_windows.cpp | 221 ---------- src/std2_make_unique.hpp | 51 --- test_main/test_main.cpp | 8 +- test_performance/main_threaded_mean.cpp | 126 +++--- test_performance/performance.h | 7 +- test_unit/test_concept_sink.cpp | 10 +- test_unit/test_configuration.cpp | 4 +- test_unit/test_crashhandler_windows.cpp | 2 +- test_unit/test_filechange.cpp | 4 +- test_unit/test_io.cpp | 10 +- .../test_linux_dynamic_loaded_sharedlib.cpp | 8 +- test_unit/test_sink.cpp | 6 +- test_unit/testing_helpers.cpp | 5 +- test_unit/testing_helpers.h | 6 +- 67 files changed, 2915 insertions(+), 2870 deletions(-) delete mode 100644 src/crashhandler.hpp delete mode 100644 src/crashhandler_unix.cpp delete mode 100644 src/crashhandler_windows.cpp delete mode 100644 src/g2filesink.cpp delete mode 100644 src/g2filesink.hpp delete mode 100644 src/g2filesinkhelper.ipp delete mode 100644 src/g2future.hpp delete mode 100644 src/g2log.cpp delete mode 100644 src/g2logmessage.cpp delete mode 100644 src/g2logworker.cpp delete mode 100644 src/g2sink.hpp delete mode 100644 src/g2sinkhandle.hpp delete mode 100644 src/g2time.cpp delete mode 100644 src/g2time.hpp rename src/{ => g3log}/active.hpp (53%) create mode 100644 src/g3log/crashhandler.hpp create mode 100644 src/g3log/crashhandler_unix.cpp create mode 100644 src/g3log/crashhandler_windows.cpp create mode 100644 src/g3log/filesink.cpp create mode 100644 src/g3log/filesink.hpp create mode 100644 src/g3log/filesinkhelper.ipp create mode 100644 src/g3log/future.hpp create mode 100644 src/g3log/g3log.cpp create mode 100644 src/g3log/g3log.hpp rename src/{g2logmessagecapture.cpp => g3log/logcapture.cpp} (88%) rename src/{g2logmessagecapture.hpp => g3log/logcapture.hpp} (77%) rename src/{g2loglevels.cpp => g3log/loglevels.cpp} (55%) rename src/{g2loglevels.hpp => g3log/loglevels.hpp} (59%) create mode 100644 src/g3log/logmessage.cpp rename src/{g2logmessage.hpp => g3log/logmessage.hpp} (52%) create mode 100644 src/g3log/logworker.cpp rename src/{g2logworker.hpp => g3log/logworker.hpp} (78%) rename src/{g2moveoncopy.hpp => g3log/moveoncopy.hpp} (68%) create mode 100644 src/g3log/shared_queue.hpp create mode 100644 src/g3log/sink.hpp create mode 100644 src/g3log/sinkhandle.hpp rename src/{g2sinkwrapper.hpp => g3log/sinkwrapper.hpp} (64%) create mode 100644 src/g3log/stacktrace_windows.cpp rename src/{ => g3log}/stacktrace_windows.hpp (58%) create mode 100644 src/g3log/std2_make_unique.hpp rename src/{ => g3log}/stlpatch_future.hpp (63%) create mode 100644 src/g3log/time.cpp create mode 100644 src/g3log/time.hpp delete mode 100644 src/shared_queue.hpp delete mode 100644 src/stacktrace_windows.cpp delete mode 100644 src/std2_make_unique.hpp diff --git a/Build.cmake b/Build.cmake index c9240cb..3cc5d49 100644 --- a/Build.cmake +++ b/Build.cmake @@ -64,14 +64,14 @@ IF (MSVC OR MINGW) ENDIF() # GENERIC STEPS - file(GLOB SRC_FILES ${LOG_SRC}/*.h ${LOG_SRC}/*.hpp ${LOG_SRC}/*.cpp ${LOG_SRC}/*.ipp) - file(GLOB HEADER_FILES ${LOG_SRC}/*.h ${LOG_SRC}/*.hpp) + file(GLOB SRC_FILES ${LOG_SRC}/g3log/*.h ${LOG_SRC}/g3log/*.hpp ${LOG_SRC}/g3log/*.cpp ${LOG_SRC}/g3log/*.ipp) + file(GLOB HEADER_FILES ${LOG_SRC}/g3log/*.hpp ${LOG_SRC}/*.hpp) #MESSAGE(" HEADER FILES ARE: ${HEADER_FILES}") IF (MSVC OR MINGW) - list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp) + list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/g3log/crashhandler_unix.cpp) ELSE() - list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp) + list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/g3log/crashhandler_windows.cpp ${LOG_SRC}/g3log/stacktrace_windows.hpp ${LOG_SRC}/g3log/stacktrace_windows.cpp) ENDIF (MSVC OR MINGW) set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC}) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99414a1..487b2b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,11 @@ -# g3log is a KjellKod Logger -# 2010 @author Kjell Hedström, hedstrom@kjellkod.cc -# ================================================================== -# 2010 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. -# =================================================================== +# ========================================================================== +# 2010 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. +# +# For more information see g3log/LICENSE or refer refer to http://unlicense.org +# ============================================================================*/ + # Below are details for compiling on Windows and Linux # by default only an example g3log binary is created @@ -53,14 +52,6 @@ set(CMAKE_BUILD_TYPE Release) project (g3log) -MESSAGE("Run 'make clean-cmake-files' to purge your build directory of CMake generated cache files") -add_custom_target(clean-cmake-files - COMMAND ${CMAKE_COMMAND} -P ${g3log_SOURCE_DIR}/CleanAll.cmake -) - - - - # Detect 64 or 32 bit if (CMAKE_SIZEOF_VOID_P EQUAL 8) # 64-bit project @@ -132,6 +123,17 @@ endif() # INCLUDE (${g3log_SOURCE_DIR}/CPackLists.txt) +MESSAGE("\n\n + ******************************************************************* + Please do 'make clean-cmake-files' before next cmake generation. + It is a good idea to purge your build directory of CMake + generated cache files + ******************************************************************* + ") +add_custom_target(clean-cmake-files + COMMAND ${CMAKE_COMMAND} -P ${g3log_SOURCE_DIR}/CleanAll.cmake +) + diff --git a/CPackLists.txt b/CPackLists.txt index 67b4ae6..485b8bc 100644 --- a/CPackLists.txt +++ b/CPackLists.txt @@ -1,3 +1,12 @@ +# ========================================================================== +# 2015 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. +# +# For more information see g3log/LICENSE or refer refer to http://unlicense.org +# ============================================================================*/ + + # INSTALL( TARGETS g3logger_shared # ARCHIVE diff --git a/CleanAll.cmake b/CleanAll.cmake index defc372..6b0c7f6 100644 --- a/CleanAll.cmake +++ b/CleanAll.cmake @@ -1,3 +1,11 @@ +# ========================================================================== +# 2015 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. +# +# For more information see g3log/LICENSE or refer refer to http://unlicense.org +# ============================================================================*/ + set(cmake_generated ${CMAKE_BINARY_DIR}/CMakeCache.txt ${CMAKE_BINARY_DIR}/cmake_install.cmake diff --git a/GenerateMacroDefinitionsFile.cmake b/GenerateMacroDefinitionsFile.cmake index d29b5ba..c17b1f7 100644 --- a/GenerateMacroDefinitionsFile.cmake +++ b/GenerateMacroDefinitionsFile.cmake @@ -1,3 +1,11 @@ +# ========================================================================== +# 2015 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. +# +# For more information see g3log/LICENSE or refer refer to http://unlicense.org +# ============================================================================*/ + # Prerequisite : Options.cmake should run first SET(HEADER "/** ========================================================================== @@ -16,7 +24,7 @@ MESSAGE("End of COMPILE_DEFINITIONS") SET(GENERATED_G3_DEFINITIONS src/g3log/generated_definitions.hpp) file(REMOVE ${GENERATED_G3_DEFINITIONS} ) FILE(WRITE ${GENERATED_G3_DEFINITIONS} "// AUTO GENERATED MACRO DEFINITIONS FOR G3LOG\n\n") -FILE(APPEND ${GENERATED_G3_DEFINITIONS} ${HEADER}"\n") +FILE(APPEND ${GENERATED_G3_DEFINITIONS} "${HEADER}\n") FILE(APPEND ${GENERATED_G3_DEFINITIONS} "#pragma once\n\n") FILE(APPEND ${GENERATED_G3_DEFINITIONS} "// CMake induced definitions below. See g3log/Options.cmake for details.\n\n") diff --git a/example/main_contract.cpp b/example/main_contract.cpp index 6e6c5b7..867780e 100644 --- a/example/main_contract.cpp +++ b/example/main_contract.cpp @@ -2,60 +2,61 @@ * 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#include "g2logworker.hpp" #include "g2log.hpp" #include #include #include + + namespace { #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -const std::string path_to_log_file = "./"; + const std::string path_to_log_file = "./"; #else -const std::string path_to_log_file = "/tmp/"; + const std::string path_to_log_file = "/tmp/"; #endif } namespace example_fatal { - void killWithContractIfNonEqual(int first, int second) - { - CHECK(first == second) << "Test to see if contract works: onetwothree: " << 123 << ". This should be at the end of the log, and will exit this example"; - } + void killWithContractIfNonEqual(int first, int second) + { + CHECK(first == second) << "Test to see if contract works: onetwothree: " << 123 << ". This should be at the end of the log, and will exit this example"; + } } // example fatal -int main(int argc, char** argv) +int main(int argc, char **argv) { - double pi_d = 3.1415926535897932384626433832795; - float pi_f = 3.1415926535897932384626433832795f; + double pi_d = 3.1415926535897932384626433832795; + float pi_f = 3.1415926535897932384626433832795f; - auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(argv[0], path_to_log_file); - g2::initializeLogging(logger_n_handle.worker.get()); - std::future log_file_name = logger_n_handle.sink->call(&g2::FileSink::fileName); - std::cout << "* This is an example of g2log. It WILL exit by a failed CHECK(...)" << std::endl; - std::cout << "* that acts as a FATAL trigger. Please see the generated log and " << std::endl; - std::cout << "* compare to the code at:\n* \t g2log/test_example/main_contract.cpp" << std::endl; - std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl; + auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(argv[0], path_to_log_file); + g2::initializeLogging(logger_n_handle.worker.get()); + std::future log_file_name = logger_n_handle.sink->call(&g2::FileSink::fileName); + std::cout << "* This is an example of g2log. It WILL exit by a failed CHECK(...)" << std::endl; + std::cout << "* that acts as a FATAL trigger. Please see the generated log and " << std::endl; + std::cout << "* compare to the code at:\n* \t g2log/test_example/main_contract.cpp" << std::endl; + std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl; - LOGF(INFO, "Hi log %d", 123); - LOG(INFO) << "Test SLOG INFO"; - LOG(DEBUG) << "Test SLOG DEBUG"; - LOG(INFO) << "one: " << 1; - LOG(INFO) << "two: " << 2; - LOG(INFO) << "one and two: " << 1 << " and " << 2; - LOG(DEBUG) << "float 2.14: " << 1000/2.14f; - LOG(DEBUG) << "pi double: " << pi_d; - LOG(DEBUG) << "pi float: " << pi_f; - LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; - LOGF(INFO, "pi float printf:%f", pi_f); + LOGF(INFO, "Hi log %d", 123); + LOG(INFO) << "Test SLOG INFO"; + LOG(DEBUG) << "Test SLOG DEBUG"; + LOG(INFO) << "one: " << 1; + LOG(INFO) << "two: " << 2; + LOG(INFO) << "one and two: " << 1 << " and " << 2; + LOG(DEBUG) << "float 2.14: " << 1000 / 2.14f; + LOG(DEBUG) << "pi double: " << pi_d; + LOG(DEBUG) << "pi float: " << pi_f; + LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; + LOGF(INFO, "pi float printf:%f", pi_f); // FATAL SECTION - int smaller = 1; - int larger = 2; - example_fatal::killWithContractIfNonEqual(smaller, larger); + int smaller = 1; + int larger = 2; + example_fatal::killWithContractIfNonEqual(smaller, larger); } diff --git a/example/main_fatal_choice.cpp b/example/main_fatal_choice.cpp index b799049..3e23c2f 100644 --- a/example/main_fatal_choice.cpp +++ b/example/main_fatal_choice.cpp @@ -6,8 +6,10 @@ * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#include #include +//#/g3log.hpp> + + #include #include #include @@ -20,226 +22,226 @@ namespace { #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -const std::string path_to_log_file = "./"; + const std::string path_to_log_file = "./"; #else -const std::string path_to_log_file = "/tmp/"; + const std::string path_to_log_file = "/tmp/"; #endif -void ToLower(std::string &str) -{ - for (auto &character : str) { - character = std::tolower(character); - } -} - -void RaiseSIGABRT() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - raise(SIGABRT); - LOG(WARNING) << "Expected to have died by now..."; -} - -void RaiseSIGFPE() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); - raise(SIGFPE); - LOG(WARNING) << "Expected to have died by now..."; -} - -void RaiseSIGSEGV() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - LOG(DEBUG) << "Exit by SIGSEGV"; - raise(SIGSEGV); - LOG(WARNING) << "Expected to have died by now..."; -} - -void RaiseSIGILL() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - LOGF(DEBUG, "Exit by %s", "SIGILL"); - raise(SIGILL); - LOG(WARNING) << "Expected to have died by now..."; -} - -void RAiseSIGTERM() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); - raise(SIGTERM); - LOG(WARNING) << "Expected to have died by now..."; -} - -int gShouldBeZero = 1; -void DivisionByZero() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - std::cout << "Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero << std::endl; - LOG(INFO) << "Division by zero is a big no-no"; - int value = 3; - auto test = value / gShouldBeZero; - LOG(WARNING) << "Expected to have died by now..., test value: " << test; -} - -void IllegalPrintf() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - LOG(DEBUG) << "Impending doom due to illeteracy"; - LOGF(INFO, "2nd attempt at ILLEGAL PRINTF_SYNTAX %d EXAMPLE. %s %s", "hello", 1); - LOG(WARNING) << "Expected to have died by now..."; -} - -void OutOfBoundsArrayIndexing() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - std::vector v; - v[0] = 5; - LOG(WARNING) << "Expected to have died by now..."; -} - - -void AccessViolation() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - char* ptr = 0; - LOG(INFO) << "Death by access violation is imminent"; - *ptr = 0; - LOG(WARNING) << "Expected to have died by now..."; -} - -void NoExitFunction() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - CHECK(false) << "This function should never be called"; -} - -void RaiseSIGABRTAndAccessViolation() { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - - auto f1 = std::async(std::launch::async, &RaiseSIGABRT); - auto f2 = std::async(std::launch::async, &AccessViolation); - f1.wait(); - f2.wait(); -} - -void ThrowInt() { - throw 1233210; -} - -void FailedCHECK() { - CHECK(false) << "This is fatal"; -} - -void CallActualExitFunction(std::function fatal_function) { - fatal_function(); -} - -void CallExitFunction(std::function fatal_function) { - CallActualExitFunction(fatal_function); -} - - - -void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) { - std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; - auto exitFunction = &NoExitFunction; - switch (fatalChoice) { - case 1: exitFunction = &RaiseSIGABRT; break; - case 2: exitFunction = &RaiseSIGFPE; break; - case 3: exitFunction = &RaiseSIGSEGV; break; - case 4: exitFunction = &RaiseSIGILL; break; - case 5: exitFunction = &RAiseSIGTERM; break; - case 6: exitFunction = &DivisionByZero; gShouldBeZero = 0; DivisionByZero(); break; - case 7: exitFunction = &IllegalPrintf; break; - case 8: exitFunction = &OutOfBoundsArrayIndexing; break; - case 9: exitFunction = &AccessViolation; break; - case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break; - case 11: exitFunction = &ThrowInt; break; - case 12: exitFunction = &FailedCHECK; break; - default: break; - } - if (runInNewThread) { - auto dieInNearFuture = std::async(std::launch::async, CallExitFunction, exitFunction); - dieInNearFuture.wait(); - } else { - CallExitFunction(exitFunction); + void ToLower(std::string &str) + { + for (auto &character : str) { + character = std::tolower(character); + } } - std::string unexpected = "Expected to exit by FATAL event. That did not happen (printf choice in Windows?)."; - unexpected.append("Choice was: ").append(std::to_string(fatalChoice)).append(", async?: ") - .append(std::to_string(runInNewThread)).append("\n\n***** TEST WILL RUN AGAIN *****\n\n"); + void RaiseSIGABRT() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + raise(SIGABRT); + LOG(WARNING) << "Expected to have died by now..."; + } - std::cerr << unexpected << std::endl; - LOG(WARNING) << unexpected; + void RaiseSIGFPE() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); + raise(SIGFPE); + LOG(WARNING) << "Expected to have died by now..."; + } -} + void RaiseSIGSEGV() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << "Exit by SIGSEGV"; + raise(SIGSEGV); + LOG(WARNING) << "Expected to have died by now..."; + } -bool AskForAsyncDeath() { - std::string option; - while (true) { - option.clear(); - std::cout << "Do you want to run the test in a separate thread? [yes/no]" << std::endl; - std::getline(std::cin, option); - ToLower(option); - if (("yes" != option) && ("no" != option)) { - std::cout << "\nInvalid value: [" << option << "]\n\n\n"; + void RaiseSIGILL() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOGF(DEBUG, "Exit by %s", "SIGILL"); + raise(SIGILL); + LOG(WARNING) << "Expected to have died by now..."; + } + + void RAiseSIGTERM() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by"); + raise(SIGTERM); + LOG(WARNING) << "Expected to have died by now..."; + } + + int gShouldBeZero = 1; + void DivisionByZero() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + std::cout << "Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero << std::endl; + LOG(INFO) << "Division by zero is a big no-no"; + int value = 3; + auto test = value / gShouldBeZero; + LOG(WARNING) << "Expected to have died by now..., test value: " << test; + } + + void IllegalPrintf() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + LOG(DEBUG) << "Impending doom due to illeteracy"; + LOGF(INFO, "2nd attempt at ILLEGAL PRINTF_SYNTAX %d EXAMPLE. %s %s", "hello", 1); + LOG(WARNING) << "Expected to have died by now..."; + } + + void OutOfBoundsArrayIndexing() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + std::vector v; + v[0] = 5; + LOG(WARNING) << "Expected to have died by now..."; + } + + + void AccessViolation() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + char *ptr = 0; + LOG(INFO) << "Death by access violation is imminent"; + *ptr = 0; + LOG(WARNING) << "Expected to have died by now..."; + } + + void NoExitFunction() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + CHECK(false) << "This function should never be called"; + } + + void RaiseSIGABRTAndAccessViolation() { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + + auto f1 = std::async(std::launch::async, &RaiseSIGABRT); + auto f2 = std::async(std::launch::async, &AccessViolation); + f1.wait(); + f2.wait(); + } + + void ThrowInt() { + throw 1233210; + } + + void FailedCHECK() { + CHECK(false) << "This is fatal"; + } + + void CallActualExitFunction(std::function fatal_function) { + fatal_function(); + } + + void CallExitFunction(std::function fatal_function) { + CallActualExitFunction(fatal_function); + } + + + + void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) { + std::cout << "Calling :" << __FUNCTION__ << " Line: " << __LINE__ << std::endl << std::flush; + auto exitFunction = &NoExitFunction; + switch (fatalChoice) { + case 1: exitFunction = &RaiseSIGABRT; break; + case 2: exitFunction = &RaiseSIGFPE; break; + case 3: exitFunction = &RaiseSIGSEGV; break; + case 4: exitFunction = &RaiseSIGILL; break; + case 5: exitFunction = &RAiseSIGTERM; break; + case 6: exitFunction = &DivisionByZero; gShouldBeZero = 0; DivisionByZero(); break; + case 7: exitFunction = &IllegalPrintf; break; + case 8: exitFunction = &OutOfBoundsArrayIndexing; break; + case 9: exitFunction = &AccessViolation; break; + case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break; + case 11: exitFunction = &ThrowInt; break; + case 12: exitFunction = &FailedCHECK; break; + default: break; + } + if (runInNewThread) { + auto dieInNearFuture = std::async(std::launch::async, CallExitFunction, exitFunction); + dieInNearFuture.wait(); } else { - break; + CallExitFunction(exitFunction); } + + std::string unexpected = "Expected to exit by FATAL event. That did not happen (printf choice in Windows?)."; + unexpected.append("Choice was: ").append(std::to_string(fatalChoice)).append(", async?: ") + .append(std::to_string(runInNewThread)).append("\n\n***** TEST WILL RUN AGAIN *****\n\n"); + + std::cerr << unexpected << std::endl; + LOG(WARNING) << unexpected; + } - return ("yes" == option); -} - - -int ChoiceOfFatalExit() { - std::string option; - int choice = {0}; - - while (true) { - std::cout << "\n\n\n\nChoose your exit" << std::endl; - std::cout << "By throwing an fatal signal" << std::endl; - std::cout << "or By executing a fatal code snippet" << std::endl; - std::cout << "[1] Signal SIGABRT" << std::endl; - std::cout << "[2] Signal SIGFPE" << std::endl; - std::cout << "[3] Signal SIGSEGV" << std::endl; - std::cout << "[4] Signal IGILL" << std::endl; - std::cout << "[5] Signal SIGTERM" << std::endl; - - std::cout << "[6] Division By Zero" << std::endl; - std::cout << "[7] Illegal printf" << std::endl; - std::cout << "[8] Out of bounds array indexing " << std::endl; - std::cout << "[9] Access violation" << std::endl; - std::cout << "[10] Rasing SIGABRT + Access Violation in two separate threads" << std::endl; - std::cout << "[11] Just throw (in this thread)" << std::endl; - std::cout << "[12] Just CHECK(false) (in this thread)" << std::endl; - - - std::cout << std::flush; - - try { + bool AskForAsyncDeath() { + std::string option; + while (true) { + option.clear(); + std::cout << "Do you want to run the test in a separate thread? [yes/no]" << std::endl; std::getline(std::cin, option); - choice = std::stoi(option); - if (choice <= 0 || choice > 12) { - std::cout << "Invalid choice: [" << option << "\n\n"; - } else { - return choice; + ToLower(option); + if (("yes" != option) && ("no" != option)) { + std::cout << "\nInvalid value: [" << option << "]\n\n\n"; + } else { + break; + } + } + return ("yes" == option); + } + + + + int ChoiceOfFatalExit() { + std::string option; + int choice = {0}; + + while (true) { + std::cout << "\n\n\n\nChoose your exit" << std::endl; + std::cout << "By throwing an fatal signal" << std::endl; + std::cout << "or By executing a fatal code snippet" << std::endl; + std::cout << "[1] Signal SIGABRT" << std::endl; + std::cout << "[2] Signal SIGFPE" << std::endl; + std::cout << "[3] Signal SIGSEGV" << std::endl; + std::cout << "[4] Signal IGILL" << std::endl; + std::cout << "[5] Signal SIGTERM" << std::endl; + + std::cout << "[6] Division By Zero" << std::endl; + std::cout << "[7] Illegal printf" << std::endl; + std::cout << "[8] Out of bounds array indexing " << std::endl; + std::cout << "[9] Access violation" << std::endl; + std::cout << "[10] Rasing SIGABRT + Access Violation in two separate threads" << std::endl; + std::cout << "[11] Just throw (in this thread)" << std::endl; + std::cout << "[12] Just CHECK(false) (in this thread)" << std::endl; + + + std::cout << std::flush; + + try { + std::getline(std::cin, option); + choice = std::stoi(option); + if (choice <= 0 || choice > 12) { + std::cout << "Invalid choice: [" << option << "\n\n"; + } else { + return choice; + } + } catch (...) { + std::cout << "Invalid choice: [" << option << "\n\n"; } - } catch (...) { - std::cout << "Invalid choice: [" << option << "\n\n"; } } -} -void ForwardChoiceForFatalExit(bool runInNewThread, int fatalChoice) { - ExecuteDeathFunction(runInNewThread, fatalChoice); -} + void ForwardChoiceForFatalExit(bool runInNewThread, int fatalChoice) { + ExecuteDeathFunction(runInNewThread, fatalChoice); + } -void ChooseFatalExit() { - const bool runInNewThread = AskForAsyncDeath(); - const int exitChoice = ChoiceOfFatalExit(); - ForwardChoiceForFatalExit(runInNewThread, exitChoice); -} + void ChooseFatalExit() { + const bool runInNewThread = AskForAsyncDeath(); + const int exitChoice = ChoiceOfFatalExit(); + ForwardChoiceForFatalExit(runInNewThread, exitChoice); + } } // namespace void breakHere() { - #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) - __debugbreak(); - #endif +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + __debugbreak(); +#endif } -int main(int argc, char** argv) +int main(int argc, char **argv) { auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(argv[0], path_to_log_file); g2::initializeLogging(logger_n_handle.worker.get()); diff --git a/example/main_sigsegv.cpp b/example/main_sigsegv.cpp index 8927699..8d3d738 100644 --- a/example/main_sigsegv.cpp +++ b/example/main_sigsegv.cpp @@ -6,8 +6,8 @@ * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#include "g2logworker.hpp" #include "g2log.hpp" + #include #include #include @@ -15,39 +15,39 @@ namespace { #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -const std::string path_to_log_file = "./"; + const std::string path_to_log_file = "./"; #else -const std::string path_to_log_file = "/tmp/"; + const std::string path_to_log_file = "/tmp/"; #endif } namespace example_fatal { -// on Ubunti this caused get a compiler warning with gcc4.6 -// from gcc 4.7.2 (at least) it causes a crash (as expected) -// On windows it'll probably crash too. -void tryToKillWithIllegalPrintout() -{ - std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush; - std::cout << "************************************************************\n\n" << std::endl << std::flush; - std::this_thread::sleep_for(std::chrono::seconds(1)); - const std::string logging = "logging"; - LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str()); -} + // on Ubunti this caused get a compiler warning with gcc4.6 + // from gcc 4.7.2 (at least) it causes a crash (as expected) + // On windows it'll probably crash too. + void tryToKillWithIllegalPrintout() + { + std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush; + std::cout << "************************************************************\n\n" << std::endl << std::flush; + std::this_thread::sleep_for(std::chrono::seconds(1)); + const std::string logging = "logging"; + LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str()); + } -// The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV -// fault as expected by the illegal printf-format usage. just in case we exit by zero division" -void killByZeroDivision(int value) -{ - int zero = 0; // trying to fool the compiler to automatically warn - LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero; -} + // The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV + // fault as expected by the illegal printf-format usage. just in case we exit by zero division" + void killByZeroDivision(int value) + { + int zero = 0; // trying to fool the compiler to automatically warn + LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero; + } } // example fatal -int main(int argc, char** argv) +int main(int argc, char **argv) { double pi_d = 3.1415926535897932384626433832795; float pi_f = 3.1415926535897932384626433832795f; diff --git a/src/crashhandler.hpp b/src/crashhandler.hpp deleted file mode 100644 index 353ce96..0000000 --- a/src/crashhandler.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ -#include -#include -#include "g2loglevels.hpp" - -// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp -// implementationsfilen kan vara den samma -namespace g2 { - - -#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -typedef unsigned long SignalType; -/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread -/// on Windows. This is automatically done if you do at least one LOG(...) call -/// you can also use this function call, per thread so make sure these three -/// fatal signals are covered in your thread (even if you don't do a LOG(...) call -void installSignalHandlerForThread(); -#else -typedef int SignalType; -#endif - - -namespace internal { -/** return whether or any fatal handling is still ongoing - * this is used by g2log::fatalCallToLogger - * only in the case of Windows exceptions (not fatal signals) - * are we interested in changing this from false to true to - * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/ -bool blockForFatalHandling(); - -/** \return signal_name Ref: signum.hpp and \ref installSignalHandler -* or for Windows exception name */ -std::string exitReasonName(const LEVELS& level, g2::SignalType signal_number); - -/** return calling thread's stackdump*/ -std::string stackdump(const char* dump = nullptr); - -/** 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(const LEVELS& level, g2::SignalType 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 installCrashHandler(); -} diff --git a/src/crashhandler_unix.cpp b/src/crashhandler_unix.cpp deleted file mode 100644 index e4f2d43..0000000 --- a/src/crashhandler_unix.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ - -#include "crashhandler.hpp" -#include "g2logmessage.hpp" -#include "g2logmessagecapture.hpp" - -#include -#include -#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) // windows and not mingw -#error "crashhandler_unix.cpp used but it's a windows system" -#endif - -#include -#include -#include -#include -#include -#include -#include "g2loglevels.hpp" - - -// Linux/Clang, OSX/Clang, OSX/gcc -#if (defined(__clang__) || defined(__APPLE__)) -#include -#else -#include -#endif - - -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 signalHandler(int signal_number, siginfo_t* info, void* unused_context) { - using namespace g2::internal; - { - const auto dump = stackdump(); - std::ostringstream fatal_stream; - const auto fatal_reason = exitReasonName(g2::internal::FATAL_SIGNAL, signal_number); - fatal_stream << "Received fatal signal: " << fatal_reason; - fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; - fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl; - LogCapture trigger(FATAL_SIGNAL, static_cast(signal_number), dump.c_str()); - trigger.stream() << fatal_stream.str(); - } // message sent to g2LogWorker - // wait to die -} -} // end anonymous 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 { - -bool blockForFatalHandling() { - return true; // For windows we will after fatal processing change it to false -} - -/// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one -/// i.e. the latter case is only for Windows and test purposes -std::string stackdump(const char* rawdump) { - if (nullptr != rawdump && !std::string(rawdump).empty()) { - return {rawdump}; - } - - 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 - std::ostringstream oss; - 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 << "\n\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(); -} - - - -/// string representation of signal ID -std::string exitReasonName(const LEVELS& level, g2::SignalType fatal_id) { - - int signal_number = static_cast(fatal_id); - 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 << ") for " << level.text; - return oss.str(); - } -} - - - -// KJELL : TODO. The Fatal Message can contain a callback function that depending on OS and test scenario does -// different things. -// exitWithDefaultSignalHandler is called from g2logworke::bgFatal AFTER all the logging sinks have been cleared -// I.e. saving a function that has the value already encapsulated within. -// FatalMessagePtr msgPtr -// Linux/OSX --> msgPtr.get()->ContinueWithFatalExit(); --> exitWithDefaultSignalHandler(int signal_number); -// Windows ..... (if signal) --> exitWithDefaultSignalHandler(int signal_number); -// (if exception) .... -// the calling thread that is in a never-ending loop should break out of that loop -// i.e. an atomic flag should be set -// the next step should then be to re-throw the same exception -// i.e. just call the next exception handler -// we should make sure that 1) g2log exception handler is called BEFORE widows -// it should continue and then be caught in Visual Studios exception handler -// -// - -// 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(const LEVELS& level, g2::SignalType fatal_signal_id) { - const int signal_number = static_cast(fatal_signal_id); - std::cerr << "Exiting due to " << level.text << ", " << signal_number << " " << std::flush; - -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) - 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); -#endif - - kill(getpid(), signal_number); - abort(); // should never reach this -} -} // end g2::internal - - -// -// Installs FATAL signal handler that is enough to handle most fatal events -// on *NIX systems -void installSignalHandler() { -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) - struct sigaction action; - memset(&action, 0, sizeof (action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = &signalHandler; // 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"); -#endif -} - - - -void installCrashHandler() { - installSignalHandler(); -} // namespace g2::internal - - -} // end namespace g2 - diff --git a/src/crashhandler_windows.cpp b/src/crashhandler_windows.cpp deleted file mode 100644 index cebd874..0000000 --- a/src/crashhandler_windows.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ - -#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -#error "crashhandler_windows.cpp used but not on a windows system" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include // getpid -#include "crashhandler.hpp" -#include "stacktrace_windows.hpp" -#include "g2logmessage.hpp" -#include "g2logmessagecapture.hpp" - -#define getpid _getpid - -namespace { -std::atomic gBlockForFatal {true}; -LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr; - -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) -thread_local bool g_installed_thread_signal_handler = false; -#endif - -#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) - void* g_vector_exception_handler = nullptr; -#endif - - - -// Restore back to default fatal event handling -void ReverseToOriginalFatalHandling() { - SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler); - -#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) - RemoveVectoredExceptionHandler (g_vector_exception_handler); -#endif - -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) - if (SIG_ERR == signal(SIGABRT, SIG_DFL)) - perror("signal - SIGABRT"); - - if (SIG_ERR == signal(SIGFPE, SIG_DFL)) - perror("signal - SIGABRT"); - - if (SIG_ERR == signal(SIGSEGV, SIG_DFL)) - perror("signal - SIGABRT"); - - if (SIG_ERR == signal(SIGILL, SIG_DFL)) - perror("signal - SIGABRT"); - - if (SIG_ERR == signal(SIGTERM, SIG_DFL)) - perror("signal - SIGABRT"); -#endif -} - - - -// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM -void signalHandler(int signal_number) { - using namespace g2::internal; - std::string dump = stacktrace::stackdump(); - - std::ostringstream fatal_stream; - fatal_stream << "\n***** Received fatal signal " << g2::internal::exitReasonName(g2::internal::FATAL_SIGNAL, signal_number); - fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; - - - LogCapture trigger(FATAL_SIGNAL, static_cast(signal_number), dump.c_str()); - trigger.stream() << fatal_stream.str(); - - // Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens. - // Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio. - // For fatal signals only, not exceptions. - // This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint - // Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger - // This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled - // ref: g3log/Options.cmake -#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL)) - __debugbreak(); -#endif -} // scope exit - message sent to LogWorker, wait to die... - - - -// Unhandled exception catching -LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) { - std::string dump = stacktrace::stackdump(info); - - std::ostringstream fatal_stream; - const g2::SignalType exception_code = info->ExceptionRecord->ExceptionCode; - fatal_stream << "\n***** " << handler << ": Received fatal exception " << g2::internal::exitReasonName(g2::internal::FATAL_EXCEPTION, exception_code); - fatal_stream << "\tPID: " << getpid() << std::endl; - - const auto fatal_id = static_cast(exception_code); - LogCapture trigger(g2::internal::FATAL_EXCEPTION, fatal_id, dump.c_str()); - trigger.stream() << fatal_stream.str(); - // FATAL Exception: It doesn't necessarily stop here we pass on continue search - // if no one else will catch that then it's goodbye anyhow. - // The RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER" - // but does not shutdown then the software will be running with g3log shutdown. - // .... However... this must be seen as a bug from standard handling of fatal exceptions - // https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx - return EXCEPTION_CONTINUE_SEARCH; -} - - -// Unhandled exception catching -LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) { - ReverseToOriginalFatalHandling(); - return exceptionHandling(info, "Unexpected Exception Handler"); -} - - -/// Setup through (Windows API) AddVectoredExceptionHandler -/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx -#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) - LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) { - const g2::SignalType exception_code = p->ExceptionRecord->ExceptionCode; - if (false == stacktrace::isKnownException(exception_code)) { - // The unknown exception is ignored. Since it is not a Windows - // fatal exception generated by the OS we leave the - // responsibility to deal with this by the client software. - return EXCEPTION_CONTINUE_SEARCH; - } else { - ReverseToOriginalFatalHandling(); - return exceptionHandling(p, "Vectored Exception Handler"); - } - } -#endif - - - - -} // end anonymous namespace - - -namespace g2 { -namespace internal { - - -// For windows exceptions this might ONCE be set to false, in case of a -// windows exceptions and not a signal -bool blockForFatalHandling() { - return gBlockForFatal; -} - - -/// Generate stackdump. Or in case a stackdump was pre-generated and -/// non-empty just use that one. i.e. the latter case is only for -/// Windows and test purposes -std::string stackdump(const char* dump) { - if (nullptr != dump && !std::string(dump).empty()) { - return {dump}; - } - - return stacktrace::stackdump(); -} - - - -/// string representation of signal ID or Windows exception id -std::string exitReasonName(const LEVELS& level, g2::SignalType fatal_id) { - if (level == g2::internal::FATAL_EXCEPTION) { - return stacktrace::exceptionIdToText(fatal_id); - } - - switch (fatal_id) { - 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(" << fatal_id << ")"; - return oss.str(); - } -} - - -// Triggered by g2log::LogWorker 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(const LEVELS& level, g2::SignalType fatal_signal_id) { - - ReverseToOriginalFatalHandling(); - // For windows exceptions we want to continue the possibility of - // exception handling now when the log and stacktrace are flushed - // to sinks. We therefore avoid to kill the preocess here. Instead - // it will be the exceptionHandling functions above that - // will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH - if (g2::internal::FATAL_EXCEPTION == level) { - gBlockForFatal = false; - return; - } - - // for a sigal however, we exit through that fatal signal - const int signal_number = static_cast(fatal_signal_id); - raise(signal_number); -} - - - -void installSignalHandler() { - g2::installSignalHandlerForThread(); -} - - -} // end g2::internal - - -/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread -/// on Windows. This is automatically done if you do at least one LOG(...) call -/// you can also use this function call, per thread so make sure these three -/// fatal signals are covered in your thread (even if you don't do a LOG(...) call -void installSignalHandlerForThread() { -#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) - if (!g_installed_thread_signal_handler) { - g_installed_thread_signal_handler = true; - if (SIG_ERR == signal(SIGTERM, signalHandler)) - perror("signal - SIGTERM"); - if (SIG_ERR == signal(SIGABRT, signalHandler)) - perror("signal - SIGABRT"); - if (SIG_ERR == signal(SIGFPE, signalHandler)) - perror("signal - SIGFPE"); - if (SIG_ERR == signal(SIGSEGV, signalHandler)) - perror("signal - SIGSEGV"); - if (SIG_ERR == signal(SIGILL, signalHandler)) - perror("signal - SIGILL"); - } -#endif -} - -void installCrashHandler() { - internal::installSignalHandler(); - g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling); - -#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) - // const size_t kFirstExceptionHandler = 1; - // kFirstExeptionsHandler is kept here for documentational purposes. - // The last exception seems more what we want - const size_t kLastExceptionHandler = 0; - g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling); -#endif -} - -} // end namespace g2 diff --git a/src/g2filesink.cpp b/src/g2filesink.cpp deleted file mode 100644 index 8a0327d..0000000 --- a/src/g2filesink.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/** ========================================================================== - * 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ - -#include "g2filesink.hpp" -#include "g2filesinkhelper.ipp" -#include - -namespace g2 { -using namespace internal; - - -FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory) -: _log_file_with_path(log_directory) -, _log_prefix_backup(log_prefix) -, _outptr(new std::ofstream) -{ - _log_prefix_backup = prefixSanityFix(log_prefix); - if (!isValidFilename(_log_prefix_backup)) { - std::cerr << "g2log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; - abort(); - } - - std::string file_name = createLogFileName(_log_prefix_backup); - _log_file_with_path = pathSanityFix(_log_file_with_path, file_name); - _outptr = createLogFile(_log_file_with_path); - - if (!_outptr) { - std::cerr << "Cannot write log file to location, attempting current directory" << std::endl; - _log_file_with_path = "./" + file_name; - _outptr = createLogFile(_log_file_with_path); - } - assert(_outptr && "cannot open log file at startup"); - addLogFileHeader(); -} - - -FileSink::~FileSink() { - std::string exit_msg{"\ng2log g2FileSink shutdown at: "}; - exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted)); - filestream() << exit_msg << std::flush; - - exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"}); - std::cerr << exit_msg << std::flush; -} - -// The actual log receiving function -void FileSink::fileWrite(LogMessageMover message) { - std::ofstream& out(filestream()); - out << message.get().toString(); -} - -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) { - filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; - return {}; // no success - } - - addLogFileHeader(); - std::ostringstream ss_change; - ss_change << "\n\tChanging log file from : " << _log_file_with_path; - ss_change << "\n\tto new location: " << prospect_log << "\n"; - filestream() << now_formatted << ss_change.str(); - ss_change.str(""); - - std::string old_log = _log_file_with_path; - _log_file_with_path = prospect_log; - _outptr = std::move(log_stream); - ss_change << "\n\tNew log file. The previous log file was at: "; - ss_change << old_log; - filestream() << now_formatted << ss_change.str(); - return _log_file_with_path; -} -std::string FileSink::fileName() { - return _log_file_with_path; -} -void FileSink::addLogFileHeader() { - filestream() << header(); -} - -} // g2 diff --git a/src/g2filesink.hpp b/src/g2filesink.hpp deleted file mode 100644 index 239dcac..0000000 --- a/src/g2filesink.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/** ========================================================================== - * 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ -#pragma once - -#include -#include - -#include "g2logmessage.hpp" -namespace g2 { - -class FileSink { -public: - FileSink(const std::string& log_prefix, const std::string& log_directory); - virtual ~FileSink(); - - void fileWrite(LogMessageMover message); - std::string changeLogFile(const std::string& directory); - std::string fileName(); - - -private: - std::string _log_file_with_path; - std::string _log_prefix_backup; // needed in case of future log file changes of directory - std::unique_ptr _outptr; - - void addLogFileHeader(); - std::ofstream & filestream() {return *(_outptr.get()); } - - - FileSink& operator=(const FileSink&) = delete; - FileSink(const FileSink& other) = delete; - -}; -} // g2 - diff --git a/src/g2filesinkhelper.ipp b/src/g2filesinkhelper.ipp deleted file mode 100644 index f2749c8..0000000 --- a/src/g2filesinkhelper.ipp +++ /dev/null @@ -1,123 +0,0 @@ -/** ========================================================================== - * 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ - -#pragma once - - -#include -#include -#include -#include -#include -#include -#include - - -namespace g2 { - namespace internal { - static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S"; - - // check for filename validity - filename should not be part of PATH - bool isValidFilename(const std::string& prefix_filename) { - std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* "); - size_t pos = prefix_filename.find_first_of(illegal_characters, 0); - if (pos != std::string::npos) { - std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl; - return false; - } else if (prefix_filename.empty()) { - std::cerr << "Empty filename prefix is not allowed" << std::endl; - return false; - } - - return true; - } - - std::string prefixSanityFix(std::string prefix) { - prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end()); - prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); - prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); - prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); - prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end()); - if (!isValidFilename(prefix)) { - return - { - }; - } - return prefix; - } - - std::string pathSanityFix(std::string path, std::string file_name) { - // Unify the delimeters,. maybe sketchy solution but it seems to work - // on at least win7 + ubuntu. All bets are off for older windows - std::replace(path.begin(), path.end(), '\\', '/'); - - // clean up in case of multiples - auto contains_end = [&](std::string & in) -> bool { - size_t size = in.size(); - if (!size) return false; - char end = in[size - 1]; - return (end == '/' || end == ' '); - }; - - while (contains_end(path)) { - path.erase(path.size() - 1); - } - - if (!path.empty()) { - path.insert(path.end(), '/'); - } - - path.insert(path.size(), file_name); - return path; - } - - std::string header() { - std::ostringstream ss_entry; - // Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012 - ss_entry << "\t\tg2log created log at: " << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n"; - ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE:LINE] message"; - ss_entry << "\t\t(uuu*: microsecond counter since initialization of log worker)\n\n"; - return ss_entry.str(); - } - - std::string createLogFileName(const std::string& verified_prefix) { - std::stringstream oss_name; - oss_name << verified_prefix << ".g2log."; - oss_name << g2::localtime_formatted(g2::systemtime_now(), file_name_time_formatted); - oss_name << ".log"; - return oss_name.str(); - } - - bool openLogFile(const std::string& complete_file_with_path, std::ofstream& outstream) { - std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream - mode |= std::ios_base::trunc; - outstream.open(complete_file_with_path, mode); - if (!outstream.is_open()) { - std::ostringstream ss_error; - ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]"; - ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate(); - std::cerr << ss_error.str().c_str() << std::endl; - outstream.close(); - return false; - } - return true; - } - - std::unique_ptr createLogFile(const std::string& file_with_full_path) { - std::unique_ptr out(new std::ofstream); - std::ofstream & stream(*(out.get())); - bool success_with_open_file = openLogFile(file_with_full_path, stream); - if (false == success_with_open_file) { - out.release(); - } - return out; - } - - - } -} diff --git a/src/g2future.hpp b/src/g2future.hpp deleted file mode 100644 index 696374c..0000000 --- a/src/g2future.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef G2FUTURE_H -#define G2FUTURE_H -/** ========================================================================== -* 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org -* ============================================================================ -* Filename:g2future.hpp -* Helper functionality to put packaged_tasks in standard container. This -* is especially helpful for background thread processing a la async but through -* an actor pattern (active object), thread pool or similar. -* Created: 2012 by Kjell Hedström -* -* COMMUNITY THANKS: -* The code below is in large thanks to exemplifying code snippets from StackOverflow -* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm -* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html -* -* Both are highly recommended reads if you are interested in c++ concurrency library -* - Kjell, 2012 -* -* PUBLIC DOMAIN and NOT under copywrite protection. -* ********************************************* */ - - - -#include -#include "active.hpp" -#include "g2moveoncopy.hpp" -#include "stlpatch_future.hpp" - -namespace g2 { -// Generic helper function to avoid repeating the steps for managing -// asynchronous task job (by active object) that returns a future results -// could of course be made even more generic if done more in the way of -// std::async, ref: http://en.cppreference.com/w/cpp/thread/async -// -// Example usage: -// std::unique_ptr bgWorker{Active::createActive()}; -// ... -// auto msg_call=[=](){return ("Hello from the Background");}; -// auto future_msg = g2::spawn_task(msg_lambda, bgWorker.get()); -template -std::future::type> spawn_task(Func func, BgWorker* worker) -{ - typedef typename std::result_of::type result_type; - typedef std::packaged_task task_type; - - if (nullptr == worker) { - auto p = std::make_shared>(); - std::future future_result = p->get_future(); - p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker"))); - return future_result; - } - - task_type task(std::move(func)); - - std::future result = task.get_future(); - worker->send(MoveOnCopy(std::move(task))); - return std::move(result); -} -} // end namespace g2 -#endif // G2FUTURE_H diff --git a/src/g2log.cpp b/src/g2log.cpp deleted file mode 100644 index 62e84f2..0000000 --- a/src/g2log.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * - * Filename:g2log.cpp Framework for Logging and Design By Contract - * Created: 2011 by Kjell Hedström - * - * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and at least in "spirit" influenced - * from the following sources - * 1. kjellkod.cc ;) - * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/ - * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ - * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html - * 5. Various Q&A at StackOverflow - * ********************************************* */ - -#include "g2log.hpp" -#include // vsnprintf -#include -#include -#include -#include -#include -#include - -#include "std2_make_unique.hpp" -#include "g2logworker.hpp" -#include "crashhandler.hpp" -#include "g2logmessage.hpp" - -namespace { -std::once_flag g_initialize_flag; -g2::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main) -std::mutex g_logging_init_mutex; - -std::unique_ptr g_first_unintialized_msg = {nullptr}; -std::once_flag g_set_first_uninitialized_flag; -std::once_flag g_save_first_unintialized_flag; -const std::function g_pre_fatal_hook_that_does_nothing = []{ /*does nothing */}; -std::function g_fatal_pre_logging_hook; - - -std::atomic g_fatal_hook_recursive_counter = {0}; -} - - - - - -namespace g2 { -// 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(LogWorker* bgworker) { - std::call_once(g_initialize_flag, [] { - installCrashHandler(); - }); - std::lock_guard lock(g_logging_init_mutex); - CHECK(!internal::isLoggingInitialized()); - CHECK(bgworker != nullptr); - - // Save the first uninitialized message, if any - std::call_once(g_save_first_unintialized_flag, [&bgworker] { - if (g_first_unintialized_msg) { - bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)}); - } - }); - - g_logger_instance = bgworker; - // by default the pre fatal logging hook does nothing - // if it WOULD do something it would happen in - setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); - // recurvise crash counter re-set to zero - g_fatal_hook_recursive_counter.store(0); -} - - -/** -* default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing -* It will be called just before sending the fatal message, @ref pushFatalmessageToLogger -* It will be reset to do nothing in ::initializeLogging(...) -* so please call this function, if you ever need to, after initializeLogging(...) -*/ -void setFatalPreLoggingHook(std::function pre_fatal_hook) { - static std::mutex m; - std::lock_guard lock(m); - g_fatal_pre_logging_hook = pre_fatal_hook; -} - - - - -// By default this function pointer goes to \ref pushFatalMessageToLogger; -std::function g_fatal_to_g2logworker_function_ptr = internal::pushFatalMessageToLogger; - -/** REPLACE fatalCallToLogger for fatalCallForUnitTest - * This function switches the function pointer so that only - * 'unitTest' mock-fatal calls are made. - * */ -void setFatalExitHandler(std::function fatal_call) { - g_fatal_to_g2logworker_function_ptr = fatal_call; -} - - - - - -namespace internal { - -bool isLoggingInitialized() { - return g_logger_instance != nullptr; -} - -/** - * Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted - * that is the responsibility of its owner. * - */ -void shutDownLogging() { - std::lock_guard lock(g_logging_init_mutex); - g_logger_instance = nullptr; -} - -/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further - * LOG(...) calls can happen to a non-existing LogWorker. - * @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored - * and the logging continues to be active. - * @return true if the correct worker was given,. and shutDownLogging was called - */ -bool shutDownLoggingForActiveOnly(LogWorker* active) { - if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) { - LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active." - << "\n\t\tHaving multiple instances of the g2::LogWorker is likely a BUG" - << "\n\t\tEither way, this call to shutDownLogging was ignored" - << "\n\t\tTry g2::internal::shutDownLogging() instead"; - return false; - } - shutDownLogging(); - return true; -} - - - - -/** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries -* i.e. (dlopen + dlsym) */ -void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level, - const char* boolean_expression, int fatal_signal, const char* stack_trace) { - LEVELS msgLevel {level}; - LogMessagePtr message {std2::make_unique(file, line, function, msgLevel)}; - message.get()->write().append(entry); - message.get()->setExpression(boolean_expression); - - - if (internal::wasFatal(level)) { - auto fatalhook = g_fatal_pre_logging_hook; - // In case the fatal_pre logging actually will cause a crash in its turn - // let's not do recursive crashing! - setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); - ++g_fatal_hook_recursive_counter; // thread safe counter - // "benign" race here. If two threads crashes, with recursive crashes - // then it's possible that the "other" fatal stack trace will be shown - // that's OK since it was anyhow the first crash detected - static const std::string first_stack_trace = stack_trace; - fatalhook(); - message.get()->write().append(stack_trace); - - if (g_fatal_hook_recursive_counter.load() > 1) { - message.get()->write() - .append("\n\n\nWARNING\n" - "A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n") - .append("---First crash stacktrace: ").append(first_stack_trace).append("\n---End of first stacktrace\n"); - } - FatalMessagePtr fatal_message{ std2::make_unique(*(message._move_only.get()), fatal_signal) }; - // 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); - } else { - pushMessageToLogger(message); - } -} - -/** - * save the message to the logger. In case of called before the logger is instantiated - * the first message will be saved. Any following subsequent unitnialized log calls - * will be ignored. - * - * The first initialized log entry will also save the first uninitialized log message, if any - * @param log_entry to save to logger - */ -void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker - // Uninitialized messages are ignored but does not CHECK/crash the logger - if (!internal::isLoggingInitialized()) { - std::call_once(g_set_first_uninitialized_flag, [&] { - g_first_unintialized_msg = incoming.release(); - std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"}; - err.append(g_first_unintialized_msg->message()); - std::string& str = g_first_unintialized_msg->write(); - str.clear(); - str.append(err); // replace content - std::cerr << str << std::endl; - }); - return; - } - - // logger is initialized - g_logger_instance->save(incoming); -} - -/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal - * to exit the program. After saving the fatal message the calling thread - * will sleep forever (i.e. until the background thread catches up, saves the fatal - * message and kills the software with the fatal signal. - */ -void pushFatalMessageToLogger(FatalMessagePtr message) { - if (!isLoggingInitialized()) { - std::ostringstream error; - error << "FATAL CALL but logger is NOT initialized\n" - << "CAUSE: " << message.get()->reason() - << "\nMessage: \n" << message.get()->toString() << std::flush; - std::cerr << error.str() << std::flush; - internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id); - } - g_logger_instance->fatal(message); - while (blockForFatalHandling()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} - -/** The default, initial, handling to send a 'fatal' event to g2logworker - * the caller will stay here, eternally, until the software is aborted - * ... in the case of unit testing it is the given "Mock" fatalCall that will - * define the behaviour. - */ -void fatalCall(FatalMessagePtr message) { - g_fatal_to_g2logworker_function_ptr(FatalMessagePtr {std::move(message)}); -} - - -} // internal -} // g2 - - - diff --git a/src/g2log.hpp b/src/g2log.hpp index c5c592c..acc845b 100644 --- a/src/g2log.hpp +++ b/src/g2log.hpp @@ -1,215 +1,14 @@ /** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * - * Filename:g2log.hpp Framework for Logging and Design By Contract - * Created: 2011 by Kjell Hedström - * - * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced - * at least in "spirit" from the following sources - * 1. kjellkod.cc ;) - * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/ - * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ - * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html - * 5. Various Q&A at StackOverflow - * ********************************************* */ - +* 2015 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ #pragma once - -#include -#include -#include - -#include "g2loglevels.hpp" -#include "g2logmessagecapture.hpp" -#include "g2logmessage.hpp" - -#if !(defined(__PRETTY_FUNCTION__)) -#define __PRETTY_FUNCTION__ __FUNCTION__ -#endif - -// thread_local doesn't exist on VS2013 but it might soon? (who knows) -// to work after Microsoft has updated to be C++11 compliant -#if !(defined(thread_local)) && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -#define thread_local __declspec(thread) -#endif - - -/** namespace for LOG() and CHECK() frameworks - * 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 { - class LogWorker; - struct LogMessage; - struct FatalMessage; - - /** 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(LogWorker *logger); - - - /** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called - * - * Set a function-hook before a fatal message will be sent to the logger - * i.e. this is a great place to put a break point, either in your debugger - * or programatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal) - * This will be reset to default (does nothing) at initializeLogging(...); - * - * Example usage: - * Windows: g2::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include - * WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely - * trigger a recursive crash if used here. It should only be used when debugging - * in your Visual Studio IDE. Recursive crashes are handled but are unnecessary. - * - * Linux: g2::setFatalPreLoggingHook([]{ raise(SIGTRAP); }); - */ - void setFatalPreLoggingHook(std::function pre_fatal_hook); - - /** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then - * use "setFatalExithandler". Please see g2log.cpp and crashhandler_windows.cpp or crashhandler_unix for - * example of restoring signal and exception handlers, flushing the log and shutting down. - */ - void setFatalExitHandler(std::function fatal_call); - - - - - // internal namespace is for completely internal or semi-hidden from the g2 namespace due to that it is unlikely - // that you will use these - namespace internal { - /// @returns true if logger is initialized - bool isLoggingInitialized(); - - // Save the created LogMessage to any existing sinks - void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level, - const char* boolean_expression, int fatal_signal, const char* stack_trace); - - // forwards the message to all sinks - void pushMessageToLogger(LogMessagePtr log_entry); - - - // forwards a FATAL message to all sinks,. after which the g2logworker - // will trigger crashhandler / g2::internal::exitWithDefaultSignalHandler - // - // By default the "fatalCall" will forward a Fatalessageptr to this function - // this behaviour can be changed if you set a different fatal handler through - // "setFatalExitHandler" - void pushFatalMessageToLogger(FatalMessagePtr message); - - - // Save the created FatalMessage to any existing sinks and exit with - // the originating fatal signal,. or SIGABRT if it originated from a broken contract - // By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override - // - // If you override it then you probably want to call "pushFatalMessageToLogger" after your - // custom fatal handler is done. This will make sure that the fatal message the pushed - // to sinks as well as shutting down the process - void fatalCall(FatalMessagePtr message); - - // Shuts down logging. No object cleanup but further LOG(...) calls will be ignored. - void shutDownLogging(); - - // Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized - bool shutDownLoggingForActiveOnly(LogWorker* active); - - } // internal -} // g2 - -#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, level) - -#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \ - LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, g2::internal::CONTRACT, boolean_expression) - - -// LOG(level) is the API for the stream log -#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).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).stream() - - -/** For details please see this - * 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 - %[flags][width][.precision][length]specifier - SPECIFIERS - ---------- - %c character - %d signed integers - %i signed integers - %e scientific notation, with a lowercase “e” - %E scientific notation, with a uppercase “E” - %f floating point - %g use %e or %f, whichever is shorter - %G use %E or %f, whichever is shorter - %o octal - %s a string of characters - %u unsigned integer - %x unsigned hexadecimal, with lowercase letters - %X unsigned hexadecimal, with uppercase letters - %p a pointer - %n the argument shall be a pointer to an integer into which is placed the number of characters written so far - -For flags, width, precision etc please see the above references. -EXAMPLES: -{ - LOGF(INFO, "Characters: %c %c \n", 'a', 65); - LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long - LOGF(INFO, "Preceding with blanks: %10d \n", 1977); - LOGF(INFO, "Preceding with zeros: %010d \n", 1977); - LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100); - LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); - LOGF(INFO, "Width trick: %*d \n", 5, 10); - LOGF(INFO, "%s \n", "A string"); - return 0; -} -And here is possible output -: Characters: a A -: Decimals: 1977 650000 -: Preceding with blanks: 1977 -: Preceding with zeros: 0000001977 -: Some different radixes: 100 64 144 0x64 0144 -: floats: 3.14 +3e+000 3.141600E+000 -: Width trick: 10 -: A string \endverbatim */ -#define LOGF(level, printf_like_message, ...) \ - if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) - -// Conditional log printf syntax -#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \ - if(true == boolean_expression) \ - if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) - -// Design By Contract, printf-like API syntax with variadic input parameters. -// Throws std::runtime_eror if contract breaks -#define CHECK_F(boolean_expression, printf_like_message, ...) \ - if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__) - - +#include "g3log/g3log.hpp" +#include "g3log/logworker.hpp" +#include "g3log/loglevels.hpp" +#include "g3log/filesink.hpp" diff --git a/src/g2logmessage.cpp b/src/g2logmessage.cpp deleted file mode 100644 index 2428514..0000000 --- a/src/g2logmessage.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * 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 "crashhandler.hpp" -#include "g2time.hpp" -#include "std2_make_unique.hpp" -#include - -namespace { -std::once_flag g_start_time_flag; -std::chrono::steady_clock::time_point g_start_time; - -int64_t microsecondsCounter() { - std::call_once(g_start_time_flag, []() { - g_start_time = std::chrono::steady_clock::now(); - }); - auto now = std::chrono::steady_clock::now(); - return std::chrono::duration_cast(now - g_start_time).count(); -} - -std::string splitFileName(const std::string &str) { - size_t found; - found = str.find_last_of("(/\\"); - return str.substr(found + 1); -} -} // anonymous - - - -namespace g2 { - - - // helper for setting the normal log details in an entry - std::string LogDetailsToString(const LogMessage& msg) { - std::string out; - out.append("\n" + msg.timestamp() + " " + msg.microseconds() + "\t" - + msg.level() + " [" + msg.file() + " L: " + msg.line() + "]\t"); - return out; - } - - -// helper for normal -std::string normalToString(const LogMessage& msg) { - auto out = LogDetailsToString(msg); - out.append('"' + msg.message() + '"'); - return out; -} - -// helper for fatal signal -std::string fatalSignalToString(const LogMessage &msg) { - std::string out; // clear any previous text and formatting - out.append("\n" + msg.timestamp() + "." + msg.microseconds() - + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" - + '"' + msg.message() + '"'); - return out; -} - - -// helper for fatal exception (windows only) -std::string fatalExceptionToString(const LogMessage &msg) { - std::string out; // clear any previous text and formatting - out.append("\n" + msg.timestamp() + "." + msg.microseconds() - + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" - + '"' + msg.message() + '"'); - return out; -} - - -// helper for fatal LOG -std::string fatalLogToString(const LogMessage& msg) { - auto out = LogDetailsToString(msg); - static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "}; - out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"'); - return out; -} - -// helper for fatal CHECK -std::string fatalCheckToString(const LogMessage& msg) { - auto out = LogDetailsToString(msg); - static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; - out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" - + '"' +msg. message() + '"'); - return out; -} - - -// Format the log message according to it's type -std::string LogMessage::toString() const { - if (false == wasFatal()) { - return normalToString(*this); - } - - const auto level_value = _level.value; - if (internal::FATAL_SIGNAL.value == _level.value) { - return fatalSignalToString(*this); - } - - if (internal::FATAL_EXCEPTION.value == _level.value) { - return fatalExceptionToString(*this); - } - - if (FATAL.value == _level.value) { - return fatalLogToString(*this); - } - - if (internal::CONTRACT.value == level_value) { - return fatalCheckToString(*this); - } - - // What? Did we hit a custom made level? - auto out = LogDetailsToString(*this); - static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"}; - out.append("\n\t*******" + errorUnknown + "\t\n" + '"' + message() + '"'); - return out; -} - - - -std::string LogMessage::timestamp(const std::string &time_look) const { - return localtime_formatted(_timestamp, time_look); -} - - - - -LogMessage::LogMessage(const std::string &file, const int line, - const std::string &function, const LEVELS &level) - : _timestamp(g2::systemtime_now()) - , _call_thread_id(std::this_thread::get_id()) - , _microseconds(microsecondsCounter()) - , _file(splitFileName(file)) - , _line(line) - , _function(function) - , _level(level) -{} - - -LogMessage::LogMessage(const std::string &fatalOsSignalCrashMessage) - : LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) { - _message.append(fatalOsSignalCrashMessage); -} - -LogMessage::LogMessage(const LogMessage &other) - : _timestamp(other._timestamp) - , _call_thread_id(other._call_thread_id) - , _microseconds(other._microseconds) - , _file(other._file) - , _line(other._line) - , _function(other._function) - , _level(other._level) - , _expression(other._expression) - , _message(other._message) -{ -} - - -LogMessage::LogMessage(LogMessage &&other) - : _timestamp(other._timestamp) - , _call_thread_id(other._call_thread_id) - , _microseconds(other._microseconds) - , _file(std::move(other._file)) - , _line(other._line) - , _function(std::move(other._function)) - , _level(other._level) - , _expression(std::move(other._expression)) - , _message(std::move(other._message)) { -} - - -std::string LogMessage::threadID() const { - std::ostringstream oss; - oss << _call_thread_id; - return oss.str(); -} - -FatalMessage::FatalMessage(const LogMessage &details, g2::SignalType signal_id) - : LogMessage(details), _signal_id(signal_id) { } - - - -FatalMessage::FatalMessage(const FatalMessage &other) - : LogMessage(other), _signal_id(other._signal_id) {} - - -LogMessage FatalMessage::copyToLogMessage() const { - return LogMessage(*this); -} - -std::string FatalMessage::reason() const { - return internal::exitReasonName(_level, _signal_id); -} - - -} // g2 diff --git a/src/g2logworker.cpp b/src/g2logworker.cpp deleted file mode 100644 index 212882c..0000000 --- a/src/g2logworker.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * Filename:g2LogWorker.cpp Framework for Logging and Design By Contract - * Created: 2011 by Kjell Hedström - * - * PUBLIC DOMAIN and Not under copywrite protection. First published at KjellKod.cc - * ********************************************* */ - -#include "g2logworker.hpp" -#include "g2logmessage.hpp" - -#include -#include -#include "active.hpp" -#include "g2log.hpp" -#include "g2time.hpp" -#include "g2future.hpp" -#include "crashhandler.hpp" -#include // remove - -namespace g2 { - - LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } - - void LogWorkerImpl::bgSave(g2::LogMessagePtr msgPtr) { - std::unique_ptr uniqueMsg(std::move(msgPtr.get())); - - for (auto& sink : _sinks) { - LogMessage msg(*(uniqueMsg)); - sink->send(LogMessageMover(std::move(msg))); - } - - if (_sinks.empty()) { - std::string err_msg{"g2logworker has no sinks. Message: ["}; - err_msg.append(uniqueMsg.get()->toString()).append({"]\n"}); - std::cerr << err_msg; - } - } - - void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { - // this will be the last message. Only the active logworker can receive a FATAL call so it's - // safe to shutdown logging now - g2::internal::shutDownLogging(); - - std::string reason = msgPtr.get()->reason(); - const auto level = msgPtr.get()->_level; - const auto fatal_id = msgPtr.get()->_signal_id; - - - std::unique_ptr uniqueMsg(std::move(msgPtr.get())); - uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); - - - // Change output in case of a fatal signal (or windows exception) - std::string exiting = {"Fatal type: "}; - - uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) - .append("\nLog content flushed flushed sucessfully to sink\n\n"); - - std::cerr << uniqueMsg->message() << std::flush; - for (auto& sink : _sinks) { - LogMessage msg(*(uniqueMsg)); - sink->send(LogMessageMover(std::move(msg))); - } - - - // This clear is absolutely necessary - // All sinks are forced to receive the fatal message above before we continue - _sinks.clear(); // flush all queues - internal::exitWithDefaultSignalHandler(level, fatal_id); - - // should never reach this point - perror("g2log exited after receiving FATAL trigger. Flush message status: "); - } - - LogWorker::~LogWorker() { - g2::internal::shutDownLoggingForActiveOnly(this); - - // The sinks WILL automatically be cleared at exit of this destructor - // However, the waiting below ensures that all messages until this point are taken care of - // before any internals/LogWorkerImpl of LogWorker starts to be destroyed. - // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling - // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl" - // - // Any messages put into the queue will be OK due to: - // *) If it is before the wait below then they will be executed - // *) If it is AFTER the wait below then they will be ignored and NEVER executed - auto bg_clear_sink_call = [this] { _impl._sinks.clear(); }; - auto token_cleared = g2::spawn_task(bg_clear_sink_call, _impl._bg.get()); - token_cleared.wait(); - - // The background worker WILL be automatically cleared at the exit of the destructor - // However, the explicitly clearing of the background worker (below) makes sure that there can - // be no thread that manages to add another sink after the call to clear the sinks above. - // i.e. this manages the extremely unlikely case of another thread calling - // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp - // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to - // other threads that do not know the state of LogWorker is considered a bug but it is dealt with - // nonetheless below. - // - // If sinks would already have been added after the sink clear above then this reset will deal with it - // without risking lambda execution with a partially deconstructed LogWorkerImpl - // Calling g2::spawn_task on a nullptr Active object will not crash but return - // a future containing an appropriate exception. - _impl._bg.reset(nullptr); - } - - void LogWorker::save(LogMessagePtr msg) { - _impl._bg->send([this, msg] {_impl.bgSave(msg); });} - - void LogWorker::fatal(FatalMessagePtr fatal_message) { - _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); });} - - void LogWorker::addWrappedSink(std::shared_ptr sink) { - auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);}; - auto token_done = g2::spawn_task(bg_addsink_call, _impl._bg.get()); - token_done.wait(); - } - - - g2::DefaultFileLogger LogWorker::createWithDefaultLogger(const std::string& log_prefix, const std::string& log_directory) { - return g2::DefaultFileLogger(log_prefix, log_directory); - } - - std::unique_ptr LogWorker::createWithNoSink() { - return std::unique_ptr(new LogWorker); - } - - DefaultFileLogger::DefaultFileLogger(const std::string& log_prefix, const std::string& log_directory) - : worker(LogWorker::createWithNoSink()) - , sink(worker->addSink(std2::make_unique(log_prefix, log_directory), &FileSink::fileWrite)) { } - -} // g2 diff --git a/src/g2sink.hpp b/src/g2sink.hpp deleted file mode 100644 index 4b25b0d..0000000 --- a/src/g2sink.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/** ========================================================================== -* 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org -* ============================================================================*/ - -#ifndef G2SINK_IPP -#define G2SINK_IPP - -#include -#include -#include - -#include "g2sinkwrapper.hpp" -#include "active.hpp" -#include "g2future.hpp" -#include "g2logmessage.hpp" - -namespace g2 { -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 - -template -struct Sink : public SinkWrapper { - std::unique_ptr _real_sink; - std::unique_ptr _bg; - AsyncMessageCall _default_log_call; - - template - Sink(std::unique_ptr sink, DefaultLogCall call) - : SinkWrapper{}, - _real_sink{std::move(sink)}, - _bg(kjellkod::Active::createActive()), - _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { - } - - - Sink(std::unique_ptr sink, void(T::*Call)(std::string) ) - : SinkWrapper{}, - _real_sink {std::move(sink)}, - _bg(kjellkod::Active::createActive()) { - std::function adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); - _default_log_call = [ = ](LogMessageMover m){adapter(m.get().toString());}; - } - - virtual ~Sink() { - _bg.reset(); // TODO: to remove - } - - void send(LogMessageMover msg) override { - _bg->send([this, msg] { - _default_log_call(msg); - }); - } - - template - auto async(Call call, Args&&... args)-> std::future< typename std::result_of::type> { - return g2::spawn_task(std::bind(call, _real_sink.get(), std::forward(args)...), _bg.get()); - } -}; -} // internal -} // g2 - - -#endif /* G2SINK_IPP */ - diff --git a/src/g2sinkhandle.hpp b/src/g2sinkhandle.hpp deleted file mode 100644 index a796d5c..0000000 --- a/src/g2sinkhandle.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/** ========================================================================== -* 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org -* ============================================================================*/ - -#ifndef G2SINKHANDLE_H -#define G2SINKHANDLE_H - -#include -#include -#include -#include "g2sink.hpp" - -namespace g2 { - - // The Sinkhandle is the client's access point to the specific sink instance. - // Only through the Sinkhandle can, and should, the real sink's specific API - // be called. - // - // The real sink will be owned by the g2logger. If the real sink is deleted - // calls to sink's API through the SinkHandle will return an exception embedded - // in the resulting future. Ref: SinkHandle::call - template - class SinkHandle { - std::weak_ptr> _sink; - - public: - SinkHandle(std::shared_ptr> sink) - : _sink(sink) {} - - ~SinkHandle() {} - - - // Asynchronous call to the real sink. If the real sink is already deleted - // the returned future will contain a bad_weak_ptr exception instead of the - // call result. - template - auto call(AsyncCall func , Args&&... args) -> std::future::type> { - try { - std::shared_ptr> sink(_sink); - return sink->async(func, std::forward(args)...); - } catch (const std::bad_weak_ptr& e) { - typedef typename std::result_of::type PromiseType; - std::promise promise; - promise.set_exception(std::make_exception_ptr(e)); - return std::move(promise.get_future()); - } - } - }; -} -#endif /* G2SINKHANDLE_H */ - diff --git a/src/g2time.cpp b/src/g2time.cpp deleted file mode 100644 index da687d8..0000000 --- a/src/g2time.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * Filename:g2time.cpp cross-platform, thread-safe replacement for C++11 non-thread-safe - * localtime (and similar) - * Created: 2012 by Kjell Hedström - * - * PUBLIC DOMAIN and Not under copywrite protection. First published for g2log at KjellKod.cc - * ********************************************* */ - -#include "g2time.hpp" - -#include -#include -#include -#include -#include -#include - - -namespace g2 { - namespace internal { - // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" - // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. - // return value is SIMPLIFIED to only return a std::string - - std::string put_time(const struct tm* tmb, const char* c_time_format) { -#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) - std::ostringstream oss; - oss.fill('0'); - // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " - oss << std::put_time(const_cast (tmb), c_time_format); - return oss.str(); -#else // LINUX - const size_t size = 1024; - char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. - // ... also ... This is way more buffer space then we need - - auto success = std::strftime(buffer, size, c_time_format, tmb); - if (0 == success) - { - assert((0 != success) && "strftime fails with illegal formatting"); - return c_time_format; - } - - return buffer; -#endif - } - } // internal -} // g2 - - - -namespace g2 { - - std::time_t systemtime_now() { - system_time_point system_now = std::chrono::system_clock::now(); - return std::chrono::system_clock::to_time_t(system_now); - } - - tm localtime(const std::time_t& time) { - struct tm tm_snapshot; -#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) - localtime_s(&tm_snapshot, &time); // windsows -#else - localtime_r(&time, &tm_snapshot); // POSIX -#endif - return tm_snapshot; - } - - /// returns a std::string with content of time_t as localtime formatted by input format string - /// * format string must conform to std::put_time - /// This is similar to std::put_time(std::localtime(std::time_t*), time_format.c_str()); - - std::string localtime_formatted(const std::time_t& time_snapshot, const std::string& time_format) { - std::tm t = localtime(time_snapshot); // could be const, but cannot due to VS2012 is non conformant for C++11's std::put_time (see above) - return g2::internal::put_time(&t, time_format.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); - } -} // g2 diff --git a/src/g2time.hpp b/src/g2time.hpp deleted file mode 100644 index aa5e110..0000000 --- a/src/g2time.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef G2_TIME_HPP_ -#define G2_TIME_HPP_ -/** ========================================================================== -* 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org -* ============================================================================ -* Filename:g2time.h cross-platform, thread-safe replacement for C++11 non-thread-safe -* localtime (and similar) -* Created: 2012 by Kjell Hedström -* -* PUBLIC DOMAIN and Not under copywrite protection. First published for g2log at KjellKod.cc -* ********************************************* */ - -#include -#include -#include - -// FYI: -// namespace g2::internal ONLY in g2time.cpp -// std::string put_time(const struct tm* tmb, const char* c_time_format) - -namespace g2 -{ - namespace internal - { - static const std::string date_formatted = "%Y/%m/%d"; - static const std::string time_formatted = "%H:%M:%S"; - } - - typedef std::chrono::time_point system_time_point; - typedef std::chrono::milliseconds milliseconds; - typedef std::chrono::microseconds microseconds; - - // wrap for std::chrono::system_clock::now() - std::time_t systemtime_now(); - - /** return time representing POD struct (ref ctime + wchar) that is normally - * retrieved with std::localtime. g2::localtime is threadsafe which std::localtime is not. - * g2::localtime is probably used together with @ref g2::systemtime_now */ - tm localtime(const std::time_t& time); - - /** format string must conform to std::put_time's demands. - * WARNING: At time of writing there is only so-so compiler support for - * std::put_time. A possible fix if your c++11 library is not updated is to - * modify this to use std::strftime instead */ - std::string localtime_formatted(const std::time_t& time_snapshot, const std::string& time_format) ; -} - -#endif diff --git a/src/active.hpp b/src/g3log/active.hpp similarity index 53% rename from src/active.hpp rename to src/g3log/active.hpp index bdaa693..b04ca3c 100644 --- a/src/active.hpp +++ b/src/g3log/active.hpp @@ -2,7 +2,7 @@ * 2010 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================ * @@ -22,47 +22,48 @@ #include #include #include - -#include "shared_queue.hpp" +#include "g3log/shared_queue.hpp" namespace kjellkod { -typedef std::function Callback; + typedef std::function Callback; -class Active { -private: - Active() : done_(false) {} // Construction ONLY through factory createActive(); - Active(const Active&) = delete; - Active& operator=(const Active&) = delete; + class Active { + private: + Active() : done_(false) {} // Construction ONLY through factory createActive(); + Active(const Active &) = delete; + Active &operator=(const Active &) = delete; - void run() { - while (!done_) { - Callback func; - mq_.wait_and_pop(func); - func(); + void run() { + while (!done_) { + Callback func; + mq_.wait_and_pop(func); + func(); + } } - } - shared_queue mq_; - std::thread thd_; - bool done_; + shared_queue mq_; + std::thread thd_; + bool done_; -public: - virtual ~Active() { - send([this] { done_ = true;}); - thd_.join(); - } + public: + virtual ~Active() { + send([this] { done_ = true;}); + thd_.join(); + } - void send(Callback msg_) { mq_.push(msg_); } + void send(Callback msg_) { + mq_.push(msg_); + } + + /// Factory: safe construction of object before thread start + static std::unique_ptr createActive() { + std::unique_ptr aPtr(new Active()); + aPtr->thd_ = std::thread(&Active::run, aPtr.get()); + return aPtr; + } + }; - /// Factory: safe construction of object before thread start - static std::unique_ptr createActive() { - std::unique_ptr aPtr(new Active()); - aPtr->thd_ = std::thread(&Active::run, aPtr.get()); - return aPtr; - } -}; - } // kjellkod diff --git a/src/g3log/crashhandler.hpp b/src/g3log/crashhandler.hpp new file mode 100644 index 0000000..895a229 --- /dev/null +++ b/src/g3log/crashhandler.hpp @@ -0,0 +1,63 @@ +#pragma once + +/** ========================================================================== + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ +#include +#include +#include "g3log/loglevels.hpp" + +// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp +// implementationsfilen kan vara den samma +namespace g2 { + + +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) + typedef unsigned long SignalType; + /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread + /// on Windows. This is automatically done if you do at least one LOG(...) call + /// you can also use this function call, per thread so make sure these three + /// fatal signals are covered in your thread (even if you don't do a LOG(...) call + void installSignalHandlerForThread(); +#else + typedef int SignalType; +#endif + + + namespace internal { + /** return whether or any fatal handling is still ongoing + * this is used by g2log::fatalCallToLogger + * only in the case of Windows exceptions (not fatal signals) + * are we interested in changing this from false to true to + * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/ + bool blockForFatalHandling(); + + /** \return signal_name Ref: signum.hpp and \ref installSignalHandler + * or for Windows exception name */ + std::string exitReasonName(const LEVELS &level, g2::SignalType signal_number); + + /** return calling thread's stackdump*/ + std::string stackdump(const char *dump = nullptr); + + /** 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(const LEVELS &level, g2::SignalType 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 installCrashHandler(); +} diff --git a/src/g3log/crashhandler_unix.cpp b/src/g3log/crashhandler_unix.cpp new file mode 100644 index 0000000..57b3b5e --- /dev/null +++ b/src/g3log/crashhandler_unix.cpp @@ -0,0 +1,235 @@ +/** ========================================================================== + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/crashhandler.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/logcapture.hpp" +#include "g3log/loglevels.hpp" + +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) // windows and not mingw +#error "crashhandler_unix.cpp used but it's a windows system" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +// Linux/Clang, OSX/Clang, OSX/gcc +#if (defined(__clang__) || defined(__APPLE__)) +#include +#else +#include +#endif + + +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 signalHandler(int signal_number, siginfo_t *info, void *unused_context) { + using namespace g2::internal; + { + const auto dump = stackdump(); + std::ostringstream fatal_stream; + const auto fatal_reason = exitReasonName(g2::internal::FATAL_SIGNAL, signal_number); + fatal_stream << "Received fatal signal: " << fatal_reason; + fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; + fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl; + LogCapture trigger(FATAL_SIGNAL, static_cast(signal_number), dump.c_str()); + trigger.stream() << fatal_stream.str(); + } // message sent to g2LogWorker + // wait to die + } +} // end anonymous 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 { + + bool blockForFatalHandling() { + return true; // For windows we will after fatal processing change it to false + } + + /// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one + /// i.e. the latter case is only for Windows and test purposes + std::string stackdump(const char *rawdump) { + if (nullptr != rawdump && !std::string(rawdump).empty()) { + return {rawdump}; + } + + 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 + std::ostringstream oss; + 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 << "\n\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(); + } + + + + /// string representation of signal ID + std::string exitReasonName(const LEVELS &level, g2::SignalType fatal_id) { + + int signal_number = static_cast(fatal_id); + 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 << ") for " << level.text; + return oss.str(); + } + } + + + + // KJELL : TODO. The Fatal Message can contain a callback function that depending on OS and test scenario does + // different things. + // exitWithDefaultSignalHandler is called from g2logworke::bgFatal AFTER all the logging sinks have been cleared + // I.e. saving a function that has the value already encapsulated within. + // FatalMessagePtr msgPtr + // Linux/OSX --> msgPtr.get()->ContinueWithFatalExit(); --> exitWithDefaultSignalHandler(int signal_number); + // Windows ..... (if signal) --> exitWithDefaultSignalHandler(int signal_number); + // (if exception) .... + // the calling thread that is in a never-ending loop should break out of that loop + // i.e. an atomic flag should be set + // the next step should then be to re-throw the same exception + // i.e. just call the next exception handler + // we should make sure that 1) g2log exception handler is called BEFORE widows + // it should continue and then be caught in Visual Studios exception handler + // + // + + // 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(const LEVELS &level, g2::SignalType fatal_signal_id) { + const int signal_number = static_cast(fatal_signal_id); + std::cerr << "Exiting due to " << level.text << ", " << signal_number << " " << std::flush; + +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + 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); +#endif + + kill(getpid(), signal_number); + abort(); // should never reach this + } + } // end g2::internal + + + // + // Installs FATAL signal handler that is enough to handle most fatal events + // on *NIX systems + void installSignalHandler() { +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + struct sigaction action; + memset(&action, 0, sizeof (action)); + sigemptyset(&action.sa_mask); + action.sa_sigaction = &signalHandler; // 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"); +#endif + } + + + + void installCrashHandler() { + installSignalHandler(); + } // namespace g2::internal + + +} // end namespace g2 + diff --git a/src/g3log/crashhandler_windows.cpp b/src/g3log/crashhandler_windows.cpp new file mode 100644 index 0000000..5de5c7e --- /dev/null +++ b/src/g3log/crashhandler_windows.cpp @@ -0,0 +1,259 @@ +/** ========================================================================== + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "crashhandler_windows.cpp used but not on a windows system" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include // getpid +#include "g3log/crashhandler.hpp" +#include "g3log/stacktrace_windows.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/logcapture.hpp" + +#define getpid _getpid + +namespace { + std::atomic gBlockForFatal {true}; + LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr; + +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + thread_local bool g_installed_thread_signal_handler = false; +#endif + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + void *g_vector_exception_handler = nullptr; +#endif + + + + // Restore back to default fatal event handling + void ReverseToOriginalFatalHandling() { + SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler); + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + RemoveVectoredExceptionHandler (g_vector_exception_handler); +#endif + +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + if (SIG_ERR == signal(SIGABRT, SIG_DFL)) + perror("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGFPE, SIG_DFL)) + perror("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGSEGV, SIG_DFL)) + perror("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGILL, SIG_DFL)) + perror("signal - SIGABRT"); + + if (SIG_ERR == signal(SIGTERM, SIG_DFL)) + perror("signal - SIGABRT"); +#endif + } + + + + // called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM + void signalHandler(int signal_number) { + using namespace g2::internal; + std::string dump = stacktrace::stackdump(); + + std::ostringstream fatal_stream; + fatal_stream << "\n***** Received fatal signal " << g2::internal::exitReasonName(g2::internal::FATAL_SIGNAL, signal_number); + fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; + + + LogCapture trigger(FATAL_SIGNAL, static_cast(signal_number), dump.c_str()); + trigger.stream() << fatal_stream.str(); + + // Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens. + // Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio. + // For fatal signals only, not exceptions. + // This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint + // Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger + // This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled + // ref: g3log/Options.cmake +#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL)) + __debugbreak(); +#endif + } // scope exit - message sent to LogWorker, wait to die... + + + + // Unhandled exception catching + LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info, const std::string &handler) { + std::string dump = stacktrace::stackdump(info); + + std::ostringstream fatal_stream; + const g2::SignalType exception_code = info->ExceptionRecord->ExceptionCode; + fatal_stream << "\n***** " << handler << ": Received fatal exception " << g2::internal::exitReasonName(g2::internal::FATAL_EXCEPTION, exception_code); + fatal_stream << "\tPID: " << getpid() << std::endl; + + const auto fatal_id = static_cast(exception_code); + LogCapture trigger(g2::internal::FATAL_EXCEPTION, fatal_id, dump.c_str()); + trigger.stream() << fatal_stream.str(); + // FATAL Exception: It doesn't necessarily stop here we pass on continue search + // if no one else will catch that then it's goodbye anyhow. + // The RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER" + // but does not shutdown then the software will be running with g3log shutdown. + // .... However... this must be seen as a bug from standard handling of fatal exceptions + // https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx + return EXCEPTION_CONTINUE_SEARCH; + } + + + // Unhandled exception catching + LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS *info) { + ReverseToOriginalFatalHandling(); + return exceptionHandling(info, "Unexpected Exception Handler"); + } + + + /// Setup through (Windows API) AddVectoredExceptionHandler + /// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) { + const g2::SignalType exception_code = p->ExceptionRecord->ExceptionCode; + if (false == stacktrace::isKnownException(exception_code)) { + // The unknown exception is ignored. Since it is not a Windows + // fatal exception generated by the OS we leave the + // responsibility to deal with this by the client software. + return EXCEPTION_CONTINUE_SEARCH; + } else { + ReverseToOriginalFatalHandling(); + return exceptionHandling(p, "Vectored Exception Handler"); + } + } +#endif + + + + +} // end anonymous namespace + + +namespace g2 { + namespace internal { + + + // For windows exceptions this might ONCE be set to false, in case of a + // windows exceptions and not a signal + bool blockForFatalHandling() { + return gBlockForFatal; + } + + + /// Generate stackdump. Or in case a stackdump was pre-generated and + /// non-empty just use that one. i.e. the latter case is only for + /// Windows and test purposes + std::string stackdump(const char *dump) { + if (nullptr != dump && !std::string(dump).empty()) { + return {dump}; + } + + return stacktrace::stackdump(); + } + + + + /// string representation of signal ID or Windows exception id + std::string exitReasonName(const LEVELS &level, g2::SignalType fatal_id) { + if (level == g2::internal::FATAL_EXCEPTION) { + return stacktrace::exceptionIdToText(fatal_id); + } + + switch (fatal_id) { + 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(" << fatal_id << ")"; + return oss.str(); + } + } + + + // Triggered by g2log::LogWorker 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(const LEVELS &level, g2::SignalType fatal_signal_id) { + + ReverseToOriginalFatalHandling(); + // For windows exceptions we want to continue the possibility of + // exception handling now when the log and stacktrace are flushed + // to sinks. We therefore avoid to kill the preocess here. Instead + // it will be the exceptionHandling functions above that + // will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH + if (g2::internal::FATAL_EXCEPTION == level) { + gBlockForFatal = false; + return; + } + + // for a sigal however, we exit through that fatal signal + const int signal_number = static_cast(fatal_signal_id); + raise(signal_number); + } + + + + void installSignalHandler() { + g2::installSignalHandlerForThread(); + } + + + } // end g2::internal + + + /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread + /// on Windows. This is automatically done if you do at least one LOG(...) call + /// you can also use this function call, per thread so make sure these three + /// fatal signals are covered in your thread (even if you don't do a LOG(...) call + void installSignalHandlerForThread() { +#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) + if (!g_installed_thread_signal_handler) { + g_installed_thread_signal_handler = true; + if (SIG_ERR == signal(SIGTERM, signalHandler)) + perror("signal - SIGTERM"); + if (SIG_ERR == signal(SIGABRT, signalHandler)) + perror("signal - SIGABRT"); + if (SIG_ERR == signal(SIGFPE, signalHandler)) + perror("signal - SIGFPE"); + if (SIG_ERR == signal(SIGSEGV, signalHandler)) + perror("signal - SIGSEGV"); + if (SIG_ERR == signal(SIGILL, signalHandler)) + perror("signal - SIGILL"); + } +#endif + } + + void installCrashHandler() { + internal::installSignalHandler(); + g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling); + +#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) + // const size_t kFirstExceptionHandler = 1; + // kFirstExeptionsHandler is kept here for documentational purposes. + // The last exception seems more what we want + const size_t kLastExceptionHandler = 0; + g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling); +#endif + } + +} // end namespace g2 diff --git a/src/g3log/filesink.cpp b/src/g3log/filesink.cpp new file mode 100644 index 0000000..1a06bf7 --- /dev/null +++ b/src/g3log/filesink.cpp @@ -0,0 +1,92 @@ +/** ========================================================================== + * 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/filesink.hpp" +#include "g3log/filesinkhelper.ipp" +#include + +namespace g2 { + using namespace internal; + + + FileSink::FileSink(const std::string &log_prefix, const std::string &log_directory) + : _log_file_with_path(log_directory) + , _log_prefix_backup(log_prefix) + , _outptr(new std::ofstream) + { + _log_prefix_backup = prefixSanityFix(log_prefix); + if (!isValidFilename(_log_prefix_backup)) { + std::cerr << "g2log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; + abort(); + } + + std::string file_name = createLogFileName(_log_prefix_backup); + _log_file_with_path = pathSanityFix(_log_file_with_path, file_name); + _outptr = createLogFile(_log_file_with_path); + + if (!_outptr) { + std::cerr << "Cannot write log file to location, attempting current directory" << std::endl; + _log_file_with_path = "./" + file_name; + _outptr = createLogFile(_log_file_with_path); + } + assert(_outptr && "cannot open log file at startup"); + addLogFileHeader(); + } + + + FileSink::~FileSink() { + std::string exit_msg {"\ng2log g2FileSink shutdown at: "}; + exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted)); + filestream() << exit_msg << std::flush; + + exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"}); + std::cerr << exit_msg << std::flush; + } + + // The actual log receiving function + void FileSink::fileWrite(LogMessageMover message) { + std::ofstream &out(filestream()); + out << message.get().toString(); + } + + 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) { + filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; + return {}; // no success + } + + addLogFileHeader(); + std::ostringstream ss_change; + ss_change << "\n\tChanging log file from : " << _log_file_with_path; + ss_change << "\n\tto new location: " << prospect_log << "\n"; + filestream() << now_formatted << ss_change.str(); + ss_change.str(""); + + std::string old_log = _log_file_with_path; + _log_file_with_path = prospect_log; + _outptr = std::move(log_stream); + ss_change << "\n\tNew log file. The previous log file was at: "; + ss_change << old_log; + filestream() << now_formatted << ss_change.str(); + return _log_file_with_path; + } + std::string FileSink::fileName() { + return _log_file_with_path; + } + void FileSink::addLogFileHeader() { + filestream() << header(); + } + +} // g2 diff --git a/src/g3log/filesink.hpp b/src/g3log/filesink.hpp new file mode 100644 index 0000000..9b8c3bb --- /dev/null +++ b/src/g3log/filesink.hpp @@ -0,0 +1,42 @@ +/** ========================================================================== + * 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ +#pragma once + +#include +#include + +#include "g3log/logmessage.hpp" +namespace g2 { + + class FileSink { + public: + FileSink(const std::string &log_prefix, const std::string &log_directory); + virtual ~FileSink(); + + void fileWrite(LogMessageMover message); + std::string changeLogFile(const std::string &directory); + std::string fileName(); + + + private: + std::string _log_file_with_path; + std::string _log_prefix_backup; // needed in case of future log file changes of directory + std::unique_ptr _outptr; + + void addLogFileHeader(); + std::ofstream &filestream() { + return *(_outptr.get()); + } + + + FileSink &operator=(const FileSink &) = delete; + FileSink(const FileSink &other) = delete; + + }; +} // g2 + diff --git a/src/g3log/filesinkhelper.ipp b/src/g3log/filesinkhelper.ipp new file mode 100644 index 0000000..fdd018a --- /dev/null +++ b/src/g3log/filesinkhelper.ipp @@ -0,0 +1,123 @@ +/** ========================================================================== + * 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include + + +namespace g2 { + namespace internal { + static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S"; + + // check for filename validity - filename should not be part of PATH + bool isValidFilename(const std::string &prefix_filename) { + std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* "); + size_t pos = prefix_filename.find_first_of(illegal_characters, 0); + if (pos != std::string::npos) { + std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl; + return false; + } else if (prefix_filename.empty()) { + std::cerr << "Empty filename prefix is not allowed" << std::endl; + return false; + } + + return true; + } + + std::string prefixSanityFix(std::string prefix) { + prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); + prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end()); + if (!isValidFilename(prefix)) { + return + { + }; + } + return prefix; + } + + std::string pathSanityFix(std::string path, std::string file_name) { + // Unify the delimeters,. maybe sketchy solution but it seems to work + // on at least win7 + ubuntu. All bets are off for older windows + std::replace(path.begin(), path.end(), '\\', '/'); + + // clean up in case of multiples + auto contains_end = [&](std::string & in) -> bool { + size_t size = in.size(); + if (!size) return false; + char end = in[size - 1]; + return (end == '/' || end == ' '); + }; + + while (contains_end(path)) { + path.erase(path.size() - 1); + } + + if (!path.empty()) { + path.insert(path.end(), '/'); + } + + path.insert(path.size(), file_name); + return path; + } + + std::string header() { + std::ostringstream ss_entry; + // Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012 + ss_entry << "\t\tg2log created log at: " << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n"; + ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE:LINE] message"; + ss_entry << "\t\t(uuu*: microsecond counter since initialization of log worker)\n\n"; + return ss_entry.str(); + } + + std::string createLogFileName(const std::string &verified_prefix) { + std::stringstream oss_name; + oss_name << verified_prefix << ".g2log."; + oss_name << g2::localtime_formatted(g2::systemtime_now(), file_name_time_formatted); + oss_name << ".log"; + return oss_name.str(); + } + + bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) { + std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream + mode |= std::ios_base::trunc; + outstream.open(complete_file_with_path, mode); + if (!outstream.is_open()) { + std::ostringstream ss_error; + ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]"; + ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate(); + std::cerr << ss_error.str().c_str() << std::endl; + outstream.close(); + return false; + } + return true; + } + + std::unique_ptr createLogFile(const std::string &file_with_full_path) { + std::unique_ptr out(new std::ofstream); + std::ofstream &stream(*(out.get())); + bool success_with_open_file = openLogFile(file_with_full_path, stream); + if (false == success_with_open_file) { + out.release(); + } + return out; + } + + + } +} diff --git a/src/g3log/future.hpp b/src/g3log/future.hpp new file mode 100644 index 0000000..d1b6046 --- /dev/null +++ b/src/g3log/future.hpp @@ -0,0 +1,63 @@ +#pragma once +/** ========================================================================== +* 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* Filename:g2future.hpp +* Helper functionality to put packaged_tasks in standard container. This +* is especially helpful for background thread processing a la async but through +* an actor pattern (active object), thread pool or similar. +* Created: 2012 by Kjell Hedström +* +* COMMUNITY THANKS: +* The code below is in large thanks to exemplifying code snippets from StackOverflow +* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm +* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html +* +* Both are highly recommended reads if you are interested in c++ concurrency library +* - Kjell, 2012 +* +* PUBLIC DOMAIN and NOT under copywrite protection. +* ********************************************* */ + + + +#include +#include "g3log/active.hpp" +#include "g3log/moveoncopy.hpp" +#include "g3log/stlpatch_future.hpp" + +namespace g2 { + // Generic helper function to avoid repeating the steps for managing + // asynchronous task job (by active object) that returns a future results + // could of course be made even more generic if done more in the way of + // std::async, ref: http://en.cppreference.com/w/cpp/thread/async + // + // Example usage: + // std::unique_ptr bgWorker{Active::createActive()}; + // ... + // auto msg_call=[=](){return ("Hello from the Background");}; + // auto future_msg = g2::spawn_task(msg_lambda, bgWorker.get()); + template + std::future::type> spawn_task(Func func, BgWorker *worker) + { + typedef typename std::result_of::type result_type; + typedef std::packaged_task task_type; + + if (nullptr == worker) { + auto p = std::make_shared>(); + std::future future_result = p->get_future(); + p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker"))); + return future_result; + } + + task_type task(std::move(func)); + + std::future result = task.get_future(); + worker->send(MoveOnCopy(std::move(task))); + return std::move(result); + } +} // end namespace g2 diff --git a/src/g3log/g3log.cpp b/src/g3log/g3log.cpp new file mode 100644 index 0000000..4ae2589 --- /dev/null +++ b/src/g3log/g3log.cpp @@ -0,0 +1,252 @@ +/** ========================================================================== + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * + * Filename:g2log.cpp Framework for Logging and Design By Contract + * Created: 2011 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and at least in "spirit" influenced + * from the following sources + * 1. kjellkod.cc ;) + * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/ + * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ + * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html + * 5. Various Q&A at StackOverflow + * ********************************************* */ + +#include "g3log/g3log.hpp" +#include "g3log/std2_make_unique.hpp" +#include "g3log/logworker.hpp" +#include "g3log/crashhandler.hpp" +#include "g3log/logmessage.hpp" + +#include // vsnprintf +#include +#include +#include +#include +#include +#include + + +namespace { + std::once_flag g_initialize_flag; + g2::LogWorker *g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main) + std::mutex g_logging_init_mutex; + + std::unique_ptr g_first_unintialized_msg = {nullptr}; + std::once_flag g_set_first_uninitialized_flag; + std::once_flag g_save_first_unintialized_flag; + const std::function g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */}; + std::function g_fatal_pre_logging_hook; + + + std::atomic g_fatal_hook_recursive_counter = {0}; +} + + + + + +namespace g2 { + // 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(LogWorker *bgworker) { + std::call_once(g_initialize_flag, [] { + installCrashHandler(); + }); + std::lock_guard lock(g_logging_init_mutex); + CHECK(!internal::isLoggingInitialized()); + CHECK(bgworker != nullptr); + + // Save the first uninitialized message, if any + std::call_once(g_save_first_unintialized_flag, [&bgworker] { + if (g_first_unintialized_msg) { + bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)}); + } + }); + + g_logger_instance = bgworker; + // by default the pre fatal logging hook does nothing + // if it WOULD do something it would happen in + setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); + // recurvise crash counter re-set to zero + g_fatal_hook_recursive_counter.store(0); + } + + + /** + * default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing + * It will be called just before sending the fatal message, @ref pushFatalmessageToLogger + * It will be reset to do nothing in ::initializeLogging(...) + * so please call this function, if you ever need to, after initializeLogging(...) + */ + void setFatalPreLoggingHook(std::function pre_fatal_hook) { + static std::mutex m; + std::lock_guard lock(m); + g_fatal_pre_logging_hook = pre_fatal_hook; + } + + + + + // By default this function pointer goes to \ref pushFatalMessageToLogger; + std::function g_fatal_to_g2logworker_function_ptr = internal::pushFatalMessageToLogger; + + /** REPLACE fatalCallToLogger for fatalCallForUnitTest + * This function switches the function pointer so that only + * 'unitTest' mock-fatal calls are made. + * */ + void setFatalExitHandler(std::function fatal_call) { + g_fatal_to_g2logworker_function_ptr = fatal_call; + } + + + + + + namespace internal { + + bool isLoggingInitialized() { + return g_logger_instance != nullptr; + } + + /** + * Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted + * that is the responsibility of its owner. * + */ + void shutDownLogging() { + std::lock_guard lock(g_logging_init_mutex); + g_logger_instance = nullptr; + } + + /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further + * LOG(...) calls can happen to a non-existing LogWorker. + * @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored + * and the logging continues to be active. + * @return true if the correct worker was given,. and shutDownLogging was called + */ + bool shutDownLoggingForActiveOnly(LogWorker *active) { + if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) { + LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active." + << "\n\t\tHaving multiple instances of the g2::LogWorker is likely a BUG" + << "\n\t\tEither way, this call to shutDownLogging was ignored" + << "\n\t\tTry g2::internal::shutDownLogging() instead"; + return false; + } + shutDownLogging(); + return true; + } + + + + + /** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries + * i.e. (dlopen + dlsym) */ + void saveMessage(const char *entry, const char *file, int line, const char *function, const LEVELS &level, + const char *boolean_expression, int fatal_signal, const char *stack_trace) { + LEVELS msgLevel {level}; + LogMessagePtr message {std2::make_unique(file, line, function, msgLevel)}; + message.get()->write().append(entry); + message.get()->setExpression(boolean_expression); + + + if (internal::wasFatal(level)) { + auto fatalhook = g_fatal_pre_logging_hook; + // In case the fatal_pre logging actually will cause a crash in its turn + // let's not do recursive crashing! + setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); + ++g_fatal_hook_recursive_counter; // thread safe counter + // "benign" race here. If two threads crashes, with recursive crashes + // then it's possible that the "other" fatal stack trace will be shown + // that's OK since it was anyhow the first crash detected + static const std::string first_stack_trace = stack_trace; + fatalhook(); + message.get()->write().append(stack_trace); + + if (g_fatal_hook_recursive_counter.load() > 1) { + message.get()->write() + .append("\n\n\nWARNING\n" + "A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n") + .append("---First crash stacktrace: ").append(first_stack_trace).append("\n---End of first stacktrace\n"); + } + FatalMessagePtr fatal_message { std2::make_unique(*(message._move_only.get()), fatal_signal) }; + // 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); + } else { + pushMessageToLogger(message); + } + } + + /** + * save the message to the logger. In case of called before the logger is instantiated + * the first message will be saved. Any following subsequent unitnialized log calls + * will be ignored. + * + * The first initialized log entry will also save the first uninitialized log message, if any + * @param log_entry to save to logger + */ + void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker + // Uninitialized messages are ignored but does not CHECK/crash the logger + if (!internal::isLoggingInitialized()) { + std::call_once(g_set_first_uninitialized_flag, [&] { + g_first_unintialized_msg = incoming.release(); + std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"}; + err.append(g_first_unintialized_msg->message()); + std::string &str = g_first_unintialized_msg->write(); + str.clear(); + str.append(err); // replace content + std::cerr << str << std::endl; + }); + return; + } + + // logger is initialized + g_logger_instance->save(incoming); + } + + /** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal + * to exit the program. After saving the fatal message the calling thread + * will sleep forever (i.e. until the background thread catches up, saves the fatal + * message and kills the software with the fatal signal. + */ + void pushFatalMessageToLogger(FatalMessagePtr message) { + if (!isLoggingInitialized()) { + std::ostringstream error; + error << "FATAL CALL but logger is NOT initialized\n" + << "CAUSE: " << message.get()->reason() + << "\nMessage: \n" << message.get()->toString() << std::flush; + std::cerr << error.str() << std::flush; + internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id); + } + g_logger_instance->fatal(message); + while (blockForFatalHandling()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + /** The default, initial, handling to send a 'fatal' event to g2logworker + * the caller will stay here, eternally, until the software is aborted + * ... in the case of unit testing it is the given "Mock" fatalCall that will + * define the behaviour. + */ + void fatalCall(FatalMessagePtr message) { + g_fatal_to_g2logworker_function_ptr(FatalMessagePtr {std::move(message)}); + } + + + } // internal +} // g2 + + + diff --git a/src/g3log/g3log.hpp b/src/g3log/g3log.hpp new file mode 100644 index 0000000..9d80003 --- /dev/null +++ b/src/g3log/g3log.hpp @@ -0,0 +1,216 @@ +/** ========================================================================== + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================ + * + * Filename:g2log.hpp Framework for Logging and Design By Contract + * Created: 2011 by Kjell Hedström + * + * PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced + * at least in "spirit" from the following sources + * 1. kjellkod.cc ;) + * 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/ + * 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/ + * 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html + * 5. Various Q&A at StackOverflow + * ********************************************* */ + + +#pragma once + +#include "g3log/loglevels.hpp" +#include "g3log/logcapture.hpp" +#include "g3log/logmessage.hpp" + +#include +#include +#include + + +#if !(defined(__PRETTY_FUNCTION__)) +#define __PRETTY_FUNCTION__ __FUNCTION__ +#endif + +// thread_local doesn't exist on VS2013 but it might soon? (who knows) +// to work after Microsoft has updated to be C++11 compliant +#if !(defined(thread_local)) && (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#define thread_local __declspec(thread) +#endif + + +/** namespace for LOG() and CHECK() frameworks + * 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 { + class LogWorker; + struct LogMessage; + struct FatalMessage; + + /** 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(LogWorker *logger); + + + /** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called + * + * Set a function-hook before a fatal message will be sent to the logger + * i.e. this is a great place to put a break point, either in your debugger + * or programatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal) + * This will be reset to default (does nothing) at initializeLogging(...); + * + * Example usage: + * Windows: g2::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include + * WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely + * trigger a recursive crash if used here. It should only be used when debugging + * in your Visual Studio IDE. Recursive crashes are handled but are unnecessary. + * + * Linux: g2::setFatalPreLoggingHook([]{ raise(SIGTRAP); }); + */ + void setFatalPreLoggingHook(std::function pre_fatal_hook); + + /** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then + * use "setFatalExithandler". Please see g2log.cpp and crashhandler_windows.cpp or crashhandler_unix for + * example of restoring signal and exception handlers, flushing the log and shutting down. + */ + void setFatalExitHandler(std::function fatal_call); + + + + + // internal namespace is for completely internal or semi-hidden from the g2 namespace due to that it is unlikely + // that you will use these + namespace internal { + /// @returns true if logger is initialized + bool isLoggingInitialized(); + + // Save the created LogMessage to any existing sinks + void saveMessage(const char *message, const char *file, int line, const char *function, const LEVELS &level, + const char *boolean_expression, int fatal_signal, const char *stack_trace); + + // forwards the message to all sinks + void pushMessageToLogger(LogMessagePtr log_entry); + + + // forwards a FATAL message to all sinks,. after which the g2logworker + // will trigger crashhandler / g2::internal::exitWithDefaultSignalHandler + // + // By default the "fatalCall" will forward a Fatalessageptr to this function + // this behaviour can be changed if you set a different fatal handler through + // "setFatalExitHandler" + void pushFatalMessageToLogger(FatalMessagePtr message); + + + // Save the created FatalMessage to any existing sinks and exit with + // the originating fatal signal,. or SIGABRT if it originated from a broken contract + // By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override + // + // If you override it then you probably want to call "pushFatalMessageToLogger" after your + // custom fatal handler is done. This will make sure that the fatal message the pushed + // to sinks as well as shutting down the process + void fatalCall(FatalMessagePtr message); + + // Shuts down logging. No object cleanup but further LOG(...) calls will be ignored. + void shutDownLogging(); + + // Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized + bool shutDownLoggingForActiveOnly(LogWorker *active); + + } // internal +} // g2 + +#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, level) + +#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \ + LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, g2::internal::CONTRACT, boolean_expression) + + +// LOG(level) is the API for the stream log +#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).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).stream() + + +/** For details please see this + * 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 + %[flags][width][.precision][length]specifier + SPECIFIERS + ---------- + %c character + %d signed integers + %i signed integers + %e scientific notation, with a lowercase “e” + %E scientific notation, with a uppercase “E” + %f floating point + %g use %e or %f, whichever is shorter + %G use %E or %f, whichever is shorter + %o octal + %s a string of characters + %u unsigned integer + %x unsigned hexadecimal, with lowercase letters + %X unsigned hexadecimal, with uppercase letters + %p a pointer + %n the argument shall be a pointer to an integer into which is placed the number of characters written so far + +For flags, width, precision etc please see the above references. +EXAMPLES: +{ + LOGF(INFO, "Characters: %c %c \n", 'a', 65); + LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long + LOGF(INFO, "Preceding with blanks: %10d \n", 1977); + LOGF(INFO, "Preceding with zeros: %010d \n", 1977); + LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100); + LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); + LOGF(INFO, "Width trick: %*d \n", 5, 10); + LOGF(INFO, "%s \n", "A string"); + return 0; +} +And here is possible output +: Characters: a A +: Decimals: 1977 650000 +: Preceding with blanks: 1977 +: Preceding with zeros: 0000001977 +: Some different radixes: 100 64 144 0x64 0144 +: floats: 3.14 +3e+000 3.141600E+000 +: Width trick: 10 +: A string \endverbatim */ +#define LOGF(level, printf_like_message, ...) \ + if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) + +// Conditional log printf syntax +#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \ + if(true == boolean_expression) \ + if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__) + +// Design By Contract, printf-like API syntax with variadic input parameters. +// Throws std::runtime_eror if contract breaks +#define CHECK_F(boolean_expression, printf_like_message, ...) \ + if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__) + + + diff --git a/src/g2logmessagecapture.cpp b/src/g3log/logcapture.cpp similarity index 88% rename from src/g2logmessagecapture.cpp rename to src/g3log/logcapture.cpp index 6562aa8..c802e0b 100644 --- a/src/g2logmessagecapture.cpp +++ b/src/g3log/logcapture.cpp @@ -6,8 +6,8 @@ * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#include "g2logmessagecapture.hpp" -#include "crashhandler.hpp" +#include "g3log/logcapture.hpp" +#include "g3log/crashhandler.hpp" // For Windows we need force a thread_local install per thread of three // signals that must have a signal handler instealled per thread-basis @@ -34,8 +34,7 @@ LogCapture::~LogCapture() { /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) -LogCapture::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char* dump) - : LogCapture("", 0, "", level, "", fatal_signal, dump) { +LogCapture::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char *dump) : LogCapture("", 0, "", level, "", fatal_signal, dump) { } /** @@ -44,8 +43,8 @@ LogCapture::LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const c * @expression for CHECK calls * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler */ -LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS &level, - const char* expression, g2::SignalType fatal_signal, const char* dump) +LogCapture::LogCapture(const char *file, const int line, const char *function, const LEVELS &level, + const char *expression, g2::SignalType fatal_signal, const char *dump) : _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) { if (g2::internal::wasFatal(level)) { @@ -60,7 +59,7 @@ LogCapture::LogCapture(const char* file, const int line, const char* function, c * capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF * See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18 */ -void LogCapture::capturef(const char* printf_like_message, ...) { +void LogCapture::capturef(const char *printf_like_message, ...) { static const int kMaxMessageSize = 2048; static const std::string kTruncatedWarningText = "[...truncated...]"; char finished_message[kMaxMessageSize]; diff --git a/src/g2logmessagecapture.hpp b/src/g3log/logcapture.hpp similarity index 77% rename from src/g2logmessagecapture.hpp rename to src/g3log/logcapture.hpp index dd253e7..b052887 100644 --- a/src/g2logmessagecapture.hpp +++ b/src/g3log/logcapture.hpp @@ -7,13 +7,15 @@ * ============================================================================*/ #pragma once + +#include "g3log/loglevels.hpp" +#include "g3log/g3log.hpp" +#include "g3log/crashhandler.hpp" + #include #include #include #include -#include "g2loglevels.hpp" -#include "g2log.hpp" -#include "crashhandler.hpp" /** * Simple struct for capturing log/fatal entries. At destruction the captured message is @@ -23,7 +25,7 @@ */ struct LogCapture { /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) - LogCapture(const LEVELS& level, g2::SignalType fatal_signal, const char* dump = nullptr); + LogCapture(const LEVELS &level, g2::SignalType fatal_signal, const char *dump = nullptr); /** @@ -32,8 +34,7 @@ struct LogCapture { * @expression for CHECK calls * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler */ - LogCapture(const char* file, const int line, const char* function, const LEVELS& level, - const char* expression = "", g2::SignalType fatal_signal = SIGABRT, const char* dump = nullptr); + LogCapture(const char *file, const int line, const char *function, const LEVELS &level, const char *expression = "", g2::SignalType fatal_signal = SIGABRT, const char *dump = nullptr); // At destruction the message will be forwarded to the g2log worker. @@ -49,11 +50,11 @@ struct LogCapture { #ifndef __GNUC__ #define __attribute__(x) // Disable 'attributes' if compiler does not support 'em #endif - void capturef(const char* printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18 + void capturef(const char *printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18 /// prettifying API for this completely open struct - std::ostringstream& stream() { + std::ostringstream &stream() { return _stream; } @@ -61,11 +62,11 @@ struct LogCapture { std::ostringstream _stream; std::string _stack_trace; - const char* _file; + const char *_file; const int _line; - const char* _function; - const LEVELS& _level; - const char* _expression; + const char *_function; + const LEVELS &_level; + const char *_expression; const g2::SignalType _fatal_signal; }; diff --git a/src/g2loglevels.cpp b/src/g3log/loglevels.cpp similarity index 55% rename from src/g2loglevels.cpp rename to src/g3log/loglevels.cpp index 5ac2538..9ed5236 100644 --- a/src/g2loglevels.cpp +++ b/src/g3log/loglevels.cpp @@ -1,31 +1,28 @@ /** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * Filename:g2loglevels.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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ -#include "g2loglevels.hpp" -#include "g2log.hpp" +#include "g3log/loglevels.hpp" +#include "g3log/g3log.hpp" #include #include namespace g2 { namespace internal { - bool wasFatal(const LEVELS& level) { + bool wasFatal(const LEVELS &level) { return level.value >= FATAL.value; } #ifdef G2_DYNAMIC_LOGGING // All levels are by default ON: i.e. for DEBUG, INFO, WARNING, FATAL - const int g_level_size{FATAL.value + 1}; - std::atomic g_log_level_status[4]{{true},{true},{true},{true}}; + const int g_level_size { + FATAL.value + 1 + }; + std::atomic g_log_level_status[4] {{true}, {true}, {true}, {true}}; #endif } // internal diff --git a/src/g2loglevels.hpp b/src/g3log/loglevels.hpp similarity index 59% rename from src/g2loglevels.hpp rename to src/g3log/loglevels.hpp index 050409f..27b6a17 100644 --- a/src/g2loglevels.hpp +++ b/src/g3log/loglevels.hpp @@ -1,15 +1,10 @@ /** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * Filename:g2loglevels.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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ #pragma once @@ -35,12 +30,12 @@ struct LEVELS { // force internal copy of the const char*. This is a simple safeguard for when g3log is used in a // "dynamic, runtime loading of shared libraries" - LEVELS(const LEVELS& other) + LEVELS(const LEVELS &other) : value(other.value), text(other.text.c_str()) {} - LEVELS(int id, const char* idtext) : value(id), text(idtext) {} + LEVELS(int id, const char *idtext) : value(id), text(idtext) {} - friend bool operator==(const LEVELS& lhs, const LEVELS& rhs) { + friend bool operator==(const LEVELS &lhs, const LEVELS &rhs) { return (lhs.value == rhs.value && lhs.text == rhs.text); } @@ -51,7 +46,7 @@ struct LEVELS { namespace g2 { - static const int kDebugVaulue = 0; + static const int kDebugVaulue = 0; } #if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG)) @@ -72,18 +67,18 @@ FATAL {WARNING.value + 1, {"FATAL"}}; namespace g2 { -namespace internal { -const LEVELS CONTRACT { - 100, {"CONTRACT"} -}, FATAL_SIGNAL {101, {"FATAL_SIGNAL"}}, -FATAL_EXCEPTION {102, {"FATAL_EXCEPTION"}}; -bool wasFatal(const LEVELS& level); -} + namespace internal { + const LEVELS CONTRACT { + 100, {"CONTRACT"} + }, FATAL_SIGNAL {101, {"FATAL_SIGNAL"}}, + FATAL_EXCEPTION {102, {"FATAL_EXCEPTION"}}; + bool wasFatal(const LEVELS &level); + } #ifdef G2_DYNAMIC_LOGGING -// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL} -void setLogLevel(LEVELS level, bool enabled_status); + // Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL} + void setLogLevel(LEVELS level, bool enabled_status); #endif -bool logLevel(LEVELS level); + bool logLevel(LEVELS level); } // g2 diff --git a/src/g3log/logmessage.cpp b/src/g3log/logmessage.cpp new file mode 100644 index 0000000..0e4e494 --- /dev/null +++ b/src/g3log/logmessage.cpp @@ -0,0 +1,201 @@ +/** ========================================================================== +* 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/logmessage.hpp" +#include "g3log/crashhandler.hpp" +#include "g3log/time.hpp" +#include "g3log/std2_make_unique.hpp" + +#include + +namespace { + std::once_flag g_start_time_flag; + std::chrono::steady_clock::time_point g_start_time; + + int64_t microsecondsCounter() { + std::call_once(g_start_time_flag, []() { + g_start_time = std::chrono::steady_clock::now(); + }); + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(now - g_start_time).count(); + } + + std::string splitFileName(const std::string &str) { + size_t found; + found = str.find_last_of("(/\\"); + return str.substr(found + 1); + } +} // anonymous + + + +namespace g2 { + + + // helper for setting the normal log details in an entry + std::string LogDetailsToString(const LogMessage &msg) { + std::string out; + out.append("\n" + msg.timestamp() + " " + msg.microseconds() + "\t" + + msg.level() + " [" + msg.file() + " L: " + msg.line() + "]\t"); + return out; + } + + + // helper for normal + std::string normalToString(const LogMessage &msg) { + auto out = LogDetailsToString(msg); + out.append('"' + msg.message() + '"'); + return out; + } + + // helper for fatal signal + std::string fatalSignalToString(const LogMessage &msg) { + std::string out; // clear any previous text and formatting + out.append("\n" + msg.timestamp() + "." + msg.microseconds() + + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + + '"' + msg.message() + '"'); + return out; + } + + + // helper for fatal exception (windows only) + std::string fatalExceptionToString(const LogMessage &msg) { + std::string out; // clear any previous text and formatting + out.append("\n" + msg.timestamp() + "." + msg.microseconds() + + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + + '"' + msg.message() + '"'); + return out; + } + + + // helper for fatal LOG + std::string fatalLogToString(const LogMessage &msg) { + auto out = LogDetailsToString(msg); + static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "}; + out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"'); + return out; + } + + // helper for fatal CHECK + std::string fatalCheckToString(const LogMessage &msg) { + auto out = LogDetailsToString(msg); + static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; + out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + + '"' + msg. message() + '"'); + return out; + } + + + // Format the log message according to it's type + std::string LogMessage::toString() const { + if (false == wasFatal()) { + return normalToString(*this); + } + + const auto level_value = _level.value; + if (internal::FATAL_SIGNAL.value == _level.value) { + return fatalSignalToString(*this); + } + + if (internal::FATAL_EXCEPTION.value == _level.value) { + return fatalExceptionToString(*this); + } + + if (FATAL.value == _level.value) { + return fatalLogToString(*this); + } + + if (internal::CONTRACT.value == level_value) { + return fatalCheckToString(*this); + } + + // What? Did we hit a custom made level? + auto out = LogDetailsToString(*this); + static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"}; + out.append("\n\t*******" + errorUnknown + "\t\n" + '"' + message() + '"'); + return out; + } + + + + std::string LogMessage::timestamp(const std::string &time_look) const { + return localtime_formatted(_timestamp, time_look); + } + + + + + LogMessage::LogMessage(const std::string &file, const int line, + const std::string &function, const LEVELS &level) + : _timestamp(g2::systemtime_now()) + , _call_thread_id(std::this_thread::get_id()) + , _microseconds(microsecondsCounter()) + , _file(splitFileName(file)) + , _line(line) + , _function(function) + , _level(level) + {} + + + LogMessage::LogMessage(const std::string &fatalOsSignalCrashMessage) + : LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) { + _message.append(fatalOsSignalCrashMessage); + } + + LogMessage::LogMessage(const LogMessage &other) + : _timestamp(other._timestamp) + , _call_thread_id(other._call_thread_id) + , _microseconds(other._microseconds) + , _file(other._file) + , _line(other._line) + , _function(other._function) + , _level(other._level) + , _expression(other._expression) + , _message(other._message) + { + } + + + LogMessage::LogMessage(LogMessage &&other) + : _timestamp(other._timestamp) + , _call_thread_id(other._call_thread_id) + , _microseconds(other._microseconds) + , _file(std::move(other._file)) + , _line(other._line) + , _function(std::move(other._function)) + , _level(other._level) + , _expression(std::move(other._expression)) + , _message(std::move(other._message)) { + } + + + std::string LogMessage::threadID() const { + std::ostringstream oss; + oss << _call_thread_id; + return oss.str(); + } + + FatalMessage::FatalMessage(const LogMessage &details, g2::SignalType signal_id) + : LogMessage(details), _signal_id(signal_id) { } + + + + FatalMessage::FatalMessage(const FatalMessage &other) + : LogMessage(other), _signal_id(other._signal_id) {} + + + LogMessage FatalMessage::copyToLogMessage() const { + return LogMessage(*this); + } + + std::string FatalMessage::reason() const { + return internal::exitReasonName(_level, _signal_id); + } + + +} // g2 diff --git a/src/g2logmessage.hpp b/src/g3log/logmessage.hpp similarity index 52% rename from src/g2logmessage.hpp rename to src/g3log/logmessage.hpp index 447e322..bc2dc9a 100644 --- a/src/g2logmessage.hpp +++ b/src/g3log/logmessage.hpp @@ -1,29 +1,26 @@ /** ========================================================================== - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================ - * 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ #pragma once + +#include "g3log/loglevels.hpp" +#include "g3log/time.hpp" +#include "g3log/moveoncopy.hpp" +#include "g3log/crashhandler.hpp" + #include #include #include #include - -#include "g2loglevels.hpp" -#include "g2time.hpp" -#include "g2moveoncopy.hpp" -#include "crashhandler.hpp" #include + namespace g2 { /** LogMessage contains all the data collected from the LOG(...) call. @@ -35,34 +32,54 @@ namespace g2 { * desired way. */ struct LogMessage { - std::string file() const { return _file; } - std::string line() const { return std::to_string(_line); } - std::string function() const { return _function; } - std::string level() const { return _level.text; } + std::string file() const { + return _file; + } + std::string line() const { + return std::to_string(_line); + } + std::string function() const { + return _function; + } + std::string level() const { + return _level.text; + } /// use a different format string to get a different look on the time. // default look is Y/M/D H:M:S - std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const; - std::string microseconds() const { return std::to_string(_microseconds); } + std::string timestamp(const std::string &time_format = {internal::date_formatted + " " + internal::time_formatted}) const; + std::string microseconds() const { + return std::to_string(_microseconds); + } - std::string message() const { return _message; } - std::string& write() const { return _message; } + std::string message() const { + return _message; + } + std::string &write() const { + return _message; + } - std::string expression() const { return _expression; } - bool wasFatal() const { return internal::wasFatal(_level); } + std::string expression() const { + return _expression; + } + bool wasFatal() const { + return internal::wasFatal(_level); + } std::string threadID() const; std::string toString() const; - void setExpression(const std::string expression) { _expression = expression; } + void setExpression(const std::string expression) { + _expression = expression; + } - LogMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level); - explicit LogMessage(const std::string& fatalOsSignalCrashMessage); + LogMessage(const std::string &file, const int line, const std::string &function, const LEVELS &level); + explicit LogMessage(const std::string &fatalOsSignalCrashMessage); - LogMessage(const LogMessage&); - LogMessage(LogMessage&& other); - virtual ~LogMessage(){} + LogMessage(const LogMessage &); + LogMessage(LogMessage &&other); + virtual ~LogMessage() {} // // Complete access to the raw data in case the helper functions above @@ -86,9 +103,9 @@ namespace g2 { * A thread that causes a FatalMessage will sleep forever until the * application has exited (after message flush) */ struct FatalMessage : public LogMessage { - FatalMessage(const LogMessage& details, g2::SignalType signal_id); - FatalMessage(const FatalMessage&); - virtual ~FatalMessage(){} + FatalMessage(const LogMessage &details, g2::SignalType signal_id); + FatalMessage(const FatalMessage &); + virtual ~FatalMessage() {} LogMessage copyToLogMessage() const; std::string reason() const; diff --git a/src/g3log/logworker.cpp b/src/g3log/logworker.cpp new file mode 100644 index 0000000..6261b00 --- /dev/null +++ b/src/g3log/logworker.cpp @@ -0,0 +1,136 @@ +/** ========================================================================== +* 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/logworker.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/active.hpp" +#include "g3log/g3log.hpp" +#include "g3log/time.hpp" +#include "g3log/future.hpp" +#include "g3log/crashhandler.hpp" + +#include +#include +#include + + +namespace g2 { + + LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } + + void LogWorkerImpl::bgSave(g2::LogMessagePtr msgPtr) { + std::unique_ptr uniqueMsg(std::move(msgPtr.get())); + + for (auto &sink : _sinks) { + LogMessage msg(*(uniqueMsg)); + sink->send(LogMessageMover(std::move(msg))); + } + + if (_sinks.empty()) { + std::string err_msg {"g2logworker has no sinks. Message: ["}; + err_msg.append(uniqueMsg.get()->toString()).append({"]\n"}); + std::cerr << err_msg; + } + } + + void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { + // this will be the last message. Only the active logworker can receive a FATAL call so it's + // safe to shutdown logging now + g2::internal::shutDownLogging(); + + std::string reason = msgPtr.get()->reason(); + const auto level = msgPtr.get()->_level; + const auto fatal_id = msgPtr.get()->_signal_id; + + + std::unique_ptr uniqueMsg(std::move(msgPtr.get())); + uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); + + + // Change output in case of a fatal signal (or windows exception) + std::string exiting = {"Fatal type: "}; + + uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) + .append("\nLog content flushed flushed sucessfully to sink\n\n"); + + std::cerr << uniqueMsg->message() << std::flush; + for (auto &sink : _sinks) { + LogMessage msg(*(uniqueMsg)); + sink->send(LogMessageMover(std::move(msg))); + } + + + // This clear is absolutely necessary + // All sinks are forced to receive the fatal message above before we continue + _sinks.clear(); // flush all queues + internal::exitWithDefaultSignalHandler(level, fatal_id); + + // should never reach this point + perror("g2log exited after receiving FATAL trigger. Flush message status: "); + } + + LogWorker::~LogWorker() { + g2::internal::shutDownLoggingForActiveOnly(this); + + // The sinks WILL automatically be cleared at exit of this destructor + // However, the waiting below ensures that all messages until this point are taken care of + // before any internals/LogWorkerImpl of LogWorker starts to be destroyed. + // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling + // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl" + // + // Any messages put into the queue will be OK due to: + // *) If it is before the wait below then they will be executed + // *) If it is AFTER the wait below then they will be ignored and NEVER executed + auto bg_clear_sink_call = [this] { _impl._sinks.clear(); }; + auto token_cleared = g2::spawn_task(bg_clear_sink_call, _impl._bg.get()); + token_cleared.wait(); + + // The background worker WILL be automatically cleared at the exit of the destructor + // However, the explicitly clearing of the background worker (below) makes sure that there can + // be no thread that manages to add another sink after the call to clear the sinks above. + // i.e. this manages the extremely unlikely case of another thread calling + // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp + // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to + // other threads that do not know the state of LogWorker is considered a bug but it is dealt with + // nonetheless below. + // + // If sinks would already have been added after the sink clear above then this reset will deal with it + // without risking lambda execution with a partially deconstructed LogWorkerImpl + // Calling g2::spawn_task on a nullptr Active object will not crash but return + // a future containing an appropriate exception. + _impl._bg.reset(nullptr); + } + + void LogWorker::save(LogMessagePtr msg) { + _impl._bg->send([this, msg] {_impl.bgSave(msg); }); + } + + void LogWorker::fatal(FatalMessagePtr fatal_message) { + _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); }); + } + + void LogWorker::addWrappedSink(std::shared_ptr sink) { + auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);}; + auto token_done = g2::spawn_task(bg_addsink_call, _impl._bg.get()); + token_done.wait(); + } + + + g2::DefaultFileLogger LogWorker::createWithDefaultLogger(const std::string &log_prefix, const std::string &log_directory) { + return g2::DefaultFileLogger(log_prefix, log_directory); + } + + std::unique_ptr LogWorker::createWithNoSink() { + return std::unique_ptr(new LogWorker); + } + + DefaultFileLogger::DefaultFileLogger(const std::string &log_prefix, const std::string &log_directory) + : worker(LogWorker::createWithNoSink()) + , sink(worker->addSink(std2::make_unique(log_prefix, log_directory), &FileSink::fileWrite)) { } + +} // g2 diff --git a/src/g2logworker.hpp b/src/g3log/logworker.hpp similarity index 78% rename from src/g2logworker.hpp rename to src/g3log/logworker.hpp index 2d37a08..36a10e3 100644 --- a/src/g2logworker.hpp +++ b/src/g3log/logworker.hpp @@ -1,10 +1,9 @@ -#ifndef G2_LOG_WORKER_H_ -#define G2_LOG_WORKER_H_ +#pragma once /** ========================================================================== * 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================ * Filename:g2logworker.h Framework for Logging and Design By Contract @@ -12,27 +11,25 @@ * * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc * ********************************************* */ - +#include "g3log/g3log.hpp" +#include "g3log/sinkwrapper.hpp" +#include "g3log/sinkhandle.hpp" +#include "g3log/filesink.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/std2_make_unique.hpp" #include #include #include #include -#include "g2log.hpp" -#include "g2sinkwrapper.hpp" -#include "g2sinkhandle.hpp" -#include "g2filesink.hpp" -#include "g2logmessage.hpp" -#include "std2_make_unique.hpp" - namespace g2 { class LogWorker; struct LogWorkerImpl; struct DefaultFileLogger { - DefaultFileLogger(const std::string& log_prefix, const std::string& log_directory); + DefaultFileLogger(const std::string &log_prefix, const std::string &log_directory); std::unique_ptr worker; std::unique_ptr> sink; @@ -49,8 +46,8 @@ namespace g2 { void bgSave(g2::LogMessagePtr msgPtr); void bgFatal(FatalMessagePtr msgPtr); - LogWorkerImpl(const LogWorkerImpl&) = delete; - LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; + LogWorkerImpl(const LogWorkerImpl &) = delete; + LogWorkerImpl &operator=(const LogWorkerImpl &) = delete; }; class LogWorker final { @@ -58,13 +55,13 @@ namespace g2 { void addWrappedSink(std::shared_ptr wrapper); LogWorkerImpl _impl; - LogWorker(const LogWorker&) = delete; - LogWorker& operator=(const LogWorker&) = delete; + LogWorker(const LogWorker &) = delete; + LogWorker &operator=(const LogWorker &) = delete; public: ~LogWorker(); - static g2::DefaultFileLogger createWithDefaultLogger(const std::string& log_prefix, const std::string& log_directory); + static g2::DefaultFileLogger createWithDefaultLogger(const std::string &log_prefix, const std::string &log_directory); static std::unique_ptr createWithNoSink(); @@ -86,5 +83,3 @@ namespace g2 { } }; } // g2 - -#endif // LOG_WORKER_H_ diff --git a/src/g2moveoncopy.hpp b/src/g3log/moveoncopy.hpp similarity index 68% rename from src/g2moveoncopy.hpp rename to src/g3log/moveoncopy.hpp index 5155656..4690635 100644 --- a/src/g2moveoncopy.hpp +++ b/src/g3log/moveoncopy.hpp @@ -2,7 +2,7 @@ * 2013 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ @@ -18,23 +18,31 @@ namespace g2 { struct MoveOnCopy { mutable Moveable _move_only; - explicit MoveOnCopy(Moveable&& m) : _move_only(std::move(m)) {} - MoveOnCopy(MoveOnCopy const& t) : _move_only(std::move(t._move_only)) {} - MoveOnCopy(MoveOnCopy&& t) : _move_only(std::move(t._move_only)) {} + explicit MoveOnCopy(Moveable &&m) : _move_only(std::move(m)) {} + MoveOnCopy(MoveOnCopy const &t) : _move_only(std::move(t._move_only)) {} + MoveOnCopy(MoveOnCopy &&t) : _move_only(std::move(t._move_only)) {} - MoveOnCopy& operator=(MoveOnCopy const& other) { + MoveOnCopy &operator=(MoveOnCopy const &other) { _move_only = std::move(other._move_only); return *this; } - MoveOnCopy& operator=(MoveOnCopy&& other) { + MoveOnCopy &operator=(MoveOnCopy && other) { _move_only = std::move(other._move_only); return *this; } - void operator()() { _move_only(); } - Moveable& get() { return _move_only; } - Moveable release() { return std::move(_move_only); } + void operator()() { + _move_only(); + } + + Moveable &get() { + return _move_only; + } + + Moveable release() { + return std::move(_move_only); + } }; } // g2 diff --git a/src/g3log/shared_queue.hpp b/src/g3log/shared_queue.hpp new file mode 100644 index 0000000..e8abdde --- /dev/null +++ b/src/g3log/shared_queue.hpp @@ -0,0 +1,80 @@ +/** ========================================================================== +* 2010 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* +* Example of a normal std::queue protected by a mutex for operations, +* making it safe for thread communication, using std::mutex from C++0x with +* the help from the std::thread library from JustSoftwareSolutions +* ref: http://www.stdthread.co.uk/doc/headers/mutex.html +* +* This exampel was totally inspired by Anthony Williams lock-based data structures in +* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */ + +#pragma once + +#include +#include +#include +#include + +/** Multiple producer, multiple consumer thread safe queue +* Since 'return by reference' is used this queue won't throw */ +template +class shared_queue +{ + std::queue queue_; + mutable std::mutex m_; + std::condition_variable data_cond_; + + shared_queue &operator=(const shared_queue &) = delete; + shared_queue(const shared_queue &other) = delete; + +public: + shared_queue() {} + + void push(T item) { + { + std::lock_guard lock(m_); + queue_.push(std::move(item)); + } + data_cond_.notify_one(); + } + + /// \return immediately, with true if successful retrieval + bool try_and_pop(T &popped_item) { + std::lock_guard lock(m_); + if (queue_.empty()) { + return false; + } + popped_item = std::move(queue_.front()); + queue_.pop(); + return true; + } + + /// Try to retrieve, if no items, wait till an item is available and try again + void wait_and_pop(T &popped_item) { + std::unique_lock lock(m_); + while (queue_.empty()) + { + data_cond_.wait(lock); + // This 'while' loop is equal to + // data_cond_.wait(lock, [](bool result){return !queue_.empty();}); + } + popped_item = std::move(queue_.front()); + queue_.pop(); + } + + bool empty() const { + std::lock_guard lock(m_); + return queue_.empty(); + } + + unsigned size() const { + std::lock_guard lock(m_); + return queue_.size(); + } +}; diff --git a/src/g3log/sink.hpp b/src/g3log/sink.hpp new file mode 100644 index 0000000..a1547ae --- /dev/null +++ b/src/g3log/sink.hpp @@ -0,0 +1,79 @@ +/** ========================================================================== +* 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + + +#include "g3log/sinkwrapper.hpp" +#include "g3log/active.hpp" +#include "g3log/future.hpp" +#include "g3log/logmessage.hpp" + +#include +#include +#include + +namespace g2 { + 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 + + template + struct Sink : public SinkWrapper { + std::unique_ptr _real_sink; + std::unique_ptr _bg; + AsyncMessageCall _default_log_call; + + template + Sink(std::unique_ptr sink, DefaultLogCall call) + : SinkWrapper {}, + _real_sink {std::move(sink)}, + _bg(kjellkod::Active::createActive()), + _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { + } + + + Sink(std::unique_ptr sink, void(T::*Call)(std::string) ) + : SinkWrapper {}, + _real_sink {std::move(sink)}, + _bg(kjellkod::Active::createActive()) { + std::function adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); + _default_log_call = [ = ](LogMessageMover m) { + adapter(m.get().toString()); + }; + } + + virtual ~Sink() { + _bg.reset(); // TODO: to remove + } + + void send(LogMessageMover msg) override { + _bg->send([this, msg] { + _default_log_call(msg); + }); + } + + template + auto async(Call call, Args &&... args)-> std::future< typename std::result_of::type> { + return g2::spawn_task(std::bind(call, _real_sink.get(), std::forward(args)...), _bg.get()); + } + }; + } // internal +} // g2 + diff --git a/src/g3log/sinkhandle.hpp b/src/g3log/sinkhandle.hpp new file mode 100644 index 0000000..fec9b2c --- /dev/null +++ b/src/g3log/sinkhandle.hpp @@ -0,0 +1,55 @@ +/** ========================================================================== +* 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#pragma once + +#include "g3log/sink.hpp" + +#include +#include +#include + +namespace g2 { + + // The Sinkhandle is the client's access point to the specific sink instance. + // Only through the Sinkhandle can, and should, the real sink's specific API + // be called. + // + // The real sink will be owned by the g2logger. If the real sink is deleted + // calls to sink's API through the SinkHandle will return an exception embedded + // in the resulting future. Ref: SinkHandle::call + template + class SinkHandle { + std::weak_ptr> _sink; + + public: + SinkHandle(std::shared_ptr> sink) + : _sink(sink) {} + + ~SinkHandle() {} + + + // Asynchronous call to the real sink. If the real sink is already deleted + // the returned future will contain a bad_weak_ptr exception instead of the + // call result. + template + auto call(AsyncCall func , Args &&... args) -> std::future::type> { + try { + std::shared_ptr> sink(_sink); + return sink->async(func, std::forward(args)...); + } catch (const std::bad_weak_ptr &e) { + typedef typename std::result_of::type PromiseType; + std::promise promise; + promise.set_exception(std::make_exception_ptr(e)); + return std::move(promise.get_future()); + } + } + }; +} + + diff --git a/src/g2sinkwrapper.hpp b/src/g3log/sinkwrapper.hpp similarity index 64% rename from src/g2sinkwrapper.hpp rename to src/g3log/sinkwrapper.hpp index 7e7a775..260afc2 100644 --- a/src/g2sinkwrapper.hpp +++ b/src/g3log/sinkwrapper.hpp @@ -2,24 +2,21 @@ * 2013 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#ifndef G2SINKWRAPPER_HPP -#define G2SINKWRAPPER_HPP +#pragma once -#include "g2logmessage.hpp" +#include "g3log/logmessage.hpp" namespace g2 { - namespace internal { - - struct SinkWrapper { - virtual ~SinkWrapper() { } - virtual void send(LogMessageMover msg) = 0; - }; - } + namespace internal { + + struct SinkWrapper { + virtual ~SinkWrapper() { } + virtual void send(LogMessageMover msg) = 0; + }; + } } -#endif /* G2SINKWRAPPER_IPP */ - diff --git a/src/g3log/stacktrace_windows.cpp b/src/g3log/stacktrace_windows.cpp new file mode 100644 index 0000000..0c3c5c2 --- /dev/null +++ b/src/g3log/stacktrace_windows.cpp @@ -0,0 +1,222 @@ +/** ========================================================================== + * Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for + * the benefit of g3log. It was originally published at: + * http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html + + * 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod). + * + * 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * ============================================================================*/ + +#include "g3log/stacktrace_windows.hpp" +#include "g3log/g3log.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +#error "stacktrace_win.cpp used but not on a windows system" +#endif + + + +#define g2_MAP_PAIR_STRINGIFY(x) {x, #x} + +namespace { + thread_local size_t g_thread_local_recursive_crash_check = 0; + + const std::map kExceptionsAsText = { + g2_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT) + , g2_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP) + + }; + + + // Using the given context, fill in all the stack frames. + // Which then later can be interpreted to human readable text + void captureStackTrace(CONTEXT *context, std::vector &frame_pointers) { + DWORD machine_type = 0; + STACKFRAME64 frame = {}; // force zeroeing + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; +#ifdef _M_X64 + frame.AddrPC.Offset = context->Rip; + frame.AddrFrame.Offset = context->Rbp; + frame.AddrStack.Offset = context->Rsp; + machine_type = IMAGE_FILE_MACHINE_AMD64; +#else + frame.AddrPC.Offset = context->Eip; + frame.AddrPC.Offset = context->Ebp; + frame.AddrPC.Offset = context->Esp; + machine_type = IMAGE_FILE_MACHINE_I386; +#endif + for (size_t index = 0; index < frame_pointers.size(); ++index) + { + if (StackWalk64(machine_type, + GetCurrentProcess(), + GetCurrentThread(), + &frame, + context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) { + frame_pointers[index] = frame.AddrPC.Offset; + } else { + break; + } + } + } + + + + // extract readable text from a given stack frame. All thanks to + // using SymFromAddr and SymGetLineFromAddr64 with the stack pointer + std::string getSymbolInformation(const size_t index, const std::vector &frame_pointers) { + auto addr = frame_pointers[index]; + std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t"; + + DWORD64 displacement64; + DWORD displacement; + char symbol_buffer[sizeof(SYMBOL_INFO) + 256]; + SYMBOL_INFO *symbol = reinterpret_cast(symbol_buffer); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + std::string lineInformation; + std::string callInformation; + if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) { + callInformation.append(" ").append({symbol->Name, symbol->NameLen}); + if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) { + lineInformation.append("\t").append(line.FileName).append(" L: "); + lineInformation.append(std::to_string(line.LineNumber)); + } + } + frame_dump.append(lineInformation).append(callInformation); + return frame_dump; + } + + + // Retrieves all the symbols for the stack frames, fills them witin a text representation and returns it + std::string convertFramesToText(std::vector &frame_pointers) { + std::string dump; // slightly more efficient than ostringstream + const size_t kSize = frame_pointers.size(); + for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) { + dump += getSymbolInformation(index, frame_pointers); + dump += "\n"; + } + return dump; + } +} // anonymous + + + + +namespace stacktrace { + const std::string kUnknown = {"UNKNOWN EXCEPTION"}; + /// return the text description of a Windows exception code + /// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx + std::string exceptionIdToText(g2::SignalType id) { + const auto iter = kExceptionsAsText.find(id); + if ( iter == kExceptionsAsText.end()) { + std::string unknown = {kUnknown + ":" + std::to_string(id)}; + return unknown; + } + return iter->second; + } + + /// Yes a double lookup: first for isKnownException and then exceptionIdToText + /// for vectored exceptions we only deal with known exceptions so this tiny + /// overhead we can live with + bool isKnownException(g2::SignalType id) { + return (kExceptionsAsText.end() != kExceptionsAsText.find(id)); + } + + /// helper function: retrieve stackdump from no excisting exception pointer + std::string stackdump() { + CONTEXT current_context; + memset(¤t_context, 0, sizeof(CONTEXT)); + RtlCaptureContext(¤t_context); + return stackdump(¤t_context); + } + + /// helper function: retrieve stackdump, starting from an exception pointer + std::string stackdump(EXCEPTION_POINTERS *info) { + auto context = info->ContextRecord; + return stackdump(context); + + } + + + /// main stackdump function. retrieve stackdump, from the given context + std::string stackdump(CONTEXT *context) { + + if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass + std::string recursive_crash = {"\n\n\n***** Recursive crash detected"}; + recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n"); + return recursive_crash; + } + ++g_thread_local_recursive_crash_check; + + static std::mutex m; + std::lock_guard lock(m); + { + const BOOL kLoadSymModules = TRUE; + const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules); + if (TRUE != initialized) { + return { "Error: Cannot call SymInitialize(...) for retrieving symbols in stack" }; + } + + std::shared_ptr RaiiSymCleaner(nullptr, [&](void *) { + SymCleanup(GetCurrentProcess()); + }); // Raii sym cleanup + + + const size_t kmax_frame_dump_size = 64; + std::vector frame_pointers(kmax_frame_dump_size); + // C++11: size set and values are zeroed + + assert(frame_pointers.size() == kmax_frame_dump_size); + captureStackTrace(context, frame_pointers); + return convertFramesToText(frame_pointers); + } + } + +} // stacktrace + + + diff --git a/src/stacktrace_windows.hpp b/src/g3log/stacktrace_windows.hpp similarity index 58% rename from src/stacktrace_windows.hpp rename to src/g3log/stacktrace_windows.hpp index 58ef1a8..08cc28c 100644 --- a/src/stacktrace_windows.hpp +++ b/src/g3log/stacktrace_windows.hpp @@ -17,25 +17,26 @@ #error "stacktrace_win.cpp used but not on a windows system" #endif +#include "g3log/crashhandler.hpp" + #include #include -#include "crashhandler.hpp" - + namespace stacktrace { -/// return the text description of a Windows exception code -std::string exceptionIdToText(g2::SignalType id); + /// return the text description of a Windows exception code + std::string exceptionIdToText(g2::SignalType id); -/// return whether or not the exception is a known exception, i.e. -/// an exception that we should treat as a fatal event -bool isKnownException(g2::SignalType id); + /// return whether or not the exception is a known exception, i.e. + /// an exception that we should treat as a fatal event + bool isKnownException(g2::SignalType id); -/// helper function: retrieve stackdump from no excisting exception pointer -std::string stackdump(); + /// helper function: retrieve stackdump from no excisting exception pointer + std::string stackdump(); -/// helper function: retrieve stackdump, starting from an exception pointer -std::string stackdump(EXCEPTION_POINTERS* info); + /// helper function: retrieve stackdump, starting from an exception pointer + std::string stackdump(EXCEPTION_POINTERS *info); -/// main stackdump function. retrieve stackdump, from the given context -std::string stackdump(CONTEXT* context); + /// main stackdump function. retrieve stackdump, from the given context + std::string stackdump(CONTEXT *context); } // stacktrace diff --git a/src/g3log/std2_make_unique.hpp b/src/g3log/std2_make_unique.hpp new file mode 100644 index 0000000..1b26181 --- /dev/null +++ b/src/g3log/std2_make_unique.hpp @@ -0,0 +1,48 @@ +/** ========================================================================== + * 2013 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org + * + * make_unique will be in C++14, this implementation is copied as I understood + * Stephan T. Lavavej's description of it. + * + * PUBLIC DOMAIN and NOT under copywrite protection. + * + * + * Example: usage + * auto an_int = make_unique(123); + * auto a_string = make_unique(5, 'x'); + * auto an_int_array = make_unique(11, 22, 33); + * ********************************************* */ + +#pragma once + +#include +#include +#include + +namespace std2 { + namespace impl_fut_stl { + + template + std::unique_ptr make_unique_helper(std::false_type, Args &&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + std::unique_ptr make_unique_helper(std::true_type, Args &&... args) { + static_assert(std::extent::value == 0, "make_unique() is forbidden, please use make_unique(),"); + typedef typename std::remove_extent::type U; + return std::unique_ptr(new U[sizeof...(Args)] {std::forward(args)...}); + } + } + + template + std::unique_ptr make_unique(Args &&... args) { + return impl_fut_stl::make_unique_helper( + std::is_array(), std::forward(args)...); + } +} + diff --git a/src/stlpatch_future.hpp b/src/g3log/stlpatch_future.hpp similarity index 63% rename from src/stlpatch_future.hpp rename to src/g3log/stlpatch_future.hpp index 1916aa3..80cf867 100644 --- a/src/stlpatch_future.hpp +++ b/src/g3log/stlpatch_future.hpp @@ -1,8 +1,18 @@ -/** -* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well +/** ========================================================================== +* 2013 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* +* +* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well * std::packaged_task. Thanks to Michael Rasmussen (lap777) * Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable -*/ +* ============================================================================*/ + + + #pragma once #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) namespace std { @@ -18,28 +28,28 @@ namespace std { } template - explicit packaged_task(_Fty2&& _Fnarg) + explicit packaged_task(_Fty2 &&_Fnarg) : _my_func(_Fnarg) { } - packaged_task(packaged_task&& _Other) + packaged_task(packaged_task &&_Other) : _my_promise(move(_Other._my_promise)), - _my_func(move(_Other._my_func)) { + _my_func(move(_Other._my_func)) { } - packaged_task& operator=(packaged_task&& _Other) { + packaged_task &operator=(packaged_task && _Other) { _my_promise = move(_Other._my_promise); _my_func = move(_Other._my_func); return (*this); } - packaged_task(const packaged_task&) = delete; - packaged_task& operator=(const packaged_task&) = delete; + packaged_task(const packaged_task &) = delete; + packaged_task &operator=(const packaged_task &) = delete; ~packaged_task() { } - void swap(packaged_task& _Other) { + void swap(packaged_task &_Other) { swap(_my_promise, _Other._my_promise); swap(_my_func, _Other._my_func); } diff --git a/src/g3log/time.cpp b/src/g3log/time.cpp new file mode 100644 index 0000000..06f95cc --- /dev/null +++ b/src/g3log/time.cpp @@ -0,0 +1,77 @@ +/** ========================================================================== +* 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. +* +* For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================*/ + +#include "g3log/time.hpp" + +#include +#include +#include +#include +#include +#include + + +namespace g2 { + namespace internal { + // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" + // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. + // return value is SIMPLIFIED to only return a std::string + + std::string put_time(const struct tm *tmb, const char *c_time_format) { +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) + std::ostringstream oss; + oss.fill('0'); + // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " + oss << std::put_time(const_cast (tmb), c_time_format); + return oss.str(); +#else // LINUX + const size_t size = 1024; + char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. + // ... also ... This is way more buffer space then we need + + auto success = std::strftime(buffer, size, c_time_format, tmb); + if (0 == success) + { + assert((0 != success) && "strftime fails with illegal formatting"); + return c_time_format; + } + + return buffer; +#endif + } + } // internal +} // g2 + + + +namespace g2 { + + std::time_t systemtime_now() { + system_time_point system_now = std::chrono::system_clock::now(); + return std::chrono::system_clock::to_time_t(system_now); + } + + tm localtime(const std::time_t &time) { + struct tm tm_snapshot; +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) + localtime_s(&tm_snapshot, &time); // windsows +#else + localtime_r(&time, &tm_snapshot); // POSIX +#endif + return tm_snapshot; + } + + /// returns a std::string with content of time_t as localtime formatted by input format string + /// * format string must conform to std::put_time + /// This is similar to std::put_time(std::localtime(std::time_t*), time_format.c_str()); + + std::string localtime_formatted(const std::time_t &time_snapshot, const std::string &time_format) { + std::tm t = localtime(time_snapshot); // could be const, but cannot due to VS2012 is non conformant for C++11's std::put_time (see above) + return g2::internal::put_time(&t, time_format.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); + } +} // g2 diff --git a/src/g3log/time.hpp b/src/g3log/time.hpp new file mode 100644 index 0000000..038a4ce --- /dev/null +++ b/src/g3log/time.hpp @@ -0,0 +1,51 @@ +#pragma once +/** ========================================================================== +* 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. + * + * For more information see g3log/LICENSE or refer refer to http://unlicense.org +* ============================================================================ +* Filename:g2time.h cross-platform, thread-safe replacement for C++11 non-thread-safe +* localtime (and similar) +* Created: 2012 by Kjell Hedström +* +* PUBLIC DOMAIN and Not under copywrite protection. First published for g2log at KjellKod.cc +* ********************************************* */ + +#include +#include +#include + +// FYI: +// namespace g2::internal ONLY in g2time.cpp +// std::string put_time(const struct tm* tmb, const char* c_time_format) + +namespace g2 +{ + namespace internal + { + static const std::string date_formatted = "%Y/%m/%d"; + static const std::string time_formatted = "%H:%M:%S"; + } + + typedef std::chrono::time_point system_time_point; + typedef std::chrono::milliseconds milliseconds; + typedef std::chrono::microseconds microseconds; + + // wrap for std::chrono::system_clock::now() + std::time_t systemtime_now(); + + /** return time representing POD struct (ref ctime + wchar) that is normally + * retrieved with std::localtime. g2::localtime is threadsafe which std::localtime is not. + * g2::localtime is probably used together with @ref g2::systemtime_now */ + tm localtime(const std::time_t &time); + + /** format string must conform to std::put_time's demands. + * WARNING: At time of writing there is only so-so compiler support for + * std::put_time. A possible fix if your c++11 library is not updated is to + * modify this to use std::strftime instead */ + std::string localtime_formatted(const std::time_t &time_snapshot, const std::string &time_format) ; +} + + diff --git a/src/shared_queue.hpp b/src/shared_queue.hpp deleted file mode 100644 index a3b5652..0000000 --- a/src/shared_queue.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/** ========================================================================== -* 2010 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org -* ============================================================================ -* -* Example of a normal std::queue protected by a mutex for operations, -* making it safe for thread communication, using std::mutex from C++0x with -* the help from the std::thread library from JustSoftwareSolutions -* ref: http://www.stdthread.co.uk/doc/headers/mutex.html -* -* This exampel was totally inspired by Anthony Williams lock-based data structures in -* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */ - -#pragma once - -#include -#include -#include -#include - -/** Multiple producer, multiple consumer thread safe queue -* Since 'return by reference' is used this queue won't throw */ -template -class shared_queue -{ - std::queue queue_; - mutable std::mutex m_; - std::condition_variable data_cond_; - - shared_queue& operator=(const shared_queue&) = delete; - shared_queue(const shared_queue& other) = delete; - -public: - shared_queue(){} - - void push(T item){ - { - std::lock_guard lock(m_); - queue_.push(std::move(item)); - } - data_cond_.notify_one(); - } - - /// \return immediately, with true if successful retrieval - bool try_and_pop(T& popped_item){ - std::lock_guard lock(m_); - if(queue_.empty()){ - return false; - } - popped_item=std::move(queue_.front()); - queue_.pop(); - return true; - } - - /// Try to retrieve, if no items, wait till an item is available and try again - void wait_and_pop(T& popped_item){ - std::unique_lock lock(m_); - while(queue_.empty()) - { - data_cond_.wait(lock); - // This 'while' loop is equal to - // data_cond_.wait(lock, [](bool result){return !queue_.empty();}); - } - popped_item=std::move(queue_.front()); - queue_.pop(); - } - - bool empty() const{ - std::lock_guard lock(m_); - return queue_.empty(); - } - - unsigned size() const{ - std::lock_guard lock(m_); - return queue_.size(); - } -}; diff --git a/src/stacktrace_windows.cpp b/src/stacktrace_windows.cpp deleted file mode 100644 index 73f6bdb..0000000 --- a/src/stacktrace_windows.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/** ========================================================================== - * Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for - * the benefit of g3log. It was originally published at: - * http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html - - * 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod). - * - * 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * ============================================================================*/ - -#include "stacktrace_windows.hpp" -#include "g2log.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -#error "stacktrace_win.cpp used but not on a windows system" -#endif - - - -#define g2_MAP_PAIR_STRINGIFY(x) {x, #x} - -namespace { -thread_local size_t g_thread_local_recursive_crash_check = 0; - -const std::map kExceptionsAsText = { - g2_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT) - , g2_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP) - -}; - - -// Using the given context, fill in all the stack frames. -// Which then later can be interpreted to human readable text -void captureStackTrace(CONTEXT* context, std::vector& frame_pointers) { - DWORD machine_type = 0; - STACKFRAME64 frame = {}; // force zeroeing - frame.AddrPC.Mode = AddrModeFlat; - frame.AddrFrame.Mode = AddrModeFlat; - frame.AddrStack.Mode = AddrModeFlat; -#ifdef _M_X64 - frame.AddrPC.Offset = context->Rip; - frame.AddrFrame.Offset = context->Rbp; - frame.AddrStack.Offset = context->Rsp; - machine_type = IMAGE_FILE_MACHINE_AMD64; -#else - frame.AddrPC.Offset = context->Eip; - frame.AddrPC.Offset = context->Ebp; - frame.AddrPC.Offset = context->Esp; - machine_type = IMAGE_FILE_MACHINE_I386; -#endif - for (size_t index = 0; index < frame_pointers.size(); ++index) - { - if (StackWalk64(machine_type, - GetCurrentProcess(), - GetCurrentThread(), - &frame, - context, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL)) { - frame_pointers[index] = frame.AddrPC.Offset; - } else { - break; - } - } -} - - - -// extract readable text from a given stack frame. All thanks to -// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer -std::string getSymbolInformation(const size_t index, const std::vector& frame_pointers) { - auto addr = frame_pointers[index]; - std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t"; - - DWORD64 displacement64; - DWORD displacement; - char symbol_buffer[sizeof(SYMBOL_INFO) + 256]; - SYMBOL_INFO* symbol = reinterpret_cast(symbol_buffer); - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - - IMAGEHLP_LINE64 line; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - std::string lineInformation; - std::string callInformation; - if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) { - callInformation.append(" ").append({symbol->Name, symbol->NameLen}); - if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) { - lineInformation.append("\t").append(line.FileName).append(" L: "); - lineInformation.append(std::to_string(line.LineNumber)); - } - } - frame_dump.append(lineInformation).append(callInformation); - return frame_dump; -} - - -// Retrieves all the symbols for the stack frames, fills them witin a text representation and returns it -std::string convertFramesToText(std::vector& frame_pointers) { - std::string dump; // slightly more efficient than ostringstream - const size_t kSize = frame_pointers.size(); - for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) { - dump += getSymbolInformation(index, frame_pointers); - dump += "\n"; - } - return dump; -} -} // anonymous - - - - -namespace stacktrace { -const std::string kUnknown = {"UNKNOWN EXCEPTION"}; -/// return the text description of a Windows exception code -/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx -std::string exceptionIdToText(g2::SignalType id) { - const auto iter = kExceptionsAsText.find(id); - if ( iter == kExceptionsAsText.end()) { - std::string unknown = {kUnknown + ":" + std::to_string(id)}; - return unknown; - } - return iter->second; -} - -/// Yes a double lookup: first for isKnownException and then exceptionIdToText -/// for vectored exceptions we only deal with known exceptions so this tiny -/// overhead we can live with -bool isKnownException(g2::SignalType id) { - return (kExceptionsAsText.end() != kExceptionsAsText.find(id)); -} - -/// helper function: retrieve stackdump from no excisting exception pointer -std::string stackdump() { - CONTEXT current_context; - memset(¤t_context, 0, sizeof(CONTEXT)); - RtlCaptureContext(¤t_context); - return stackdump(¤t_context); -} - -/// helper function: retrieve stackdump, starting from an exception pointer -std::string stackdump(EXCEPTION_POINTERS* info) { - auto context = info->ContextRecord; - return stackdump(context); - -} - - -/// main stackdump function. retrieve stackdump, from the given context -std::string stackdump(CONTEXT* context) { - - if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass - std::string recursive_crash = {"\n\n\n***** Recursive crash detected"}; - recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n"); - return recursive_crash; - } - ++g_thread_local_recursive_crash_check; - - static std::mutex m; - std::lock_guard lock(m); - { - const BOOL kLoadSymModules = TRUE; - const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules); - if (TRUE != initialized) { - return{ "Error: Cannot call SymInitialize(...) for retrieving symbols in stack" }; - } - - std::shared_ptr RaiiSymCleaner(nullptr, [&](void*) { - SymCleanup(GetCurrentProcess()); - }); // Raii sym cleanup - - - const size_t kmax_frame_dump_size = 64; - std::vector frame_pointers(kmax_frame_dump_size); - // C++11: size set and values are zeroed - - assert(frame_pointers.size() == kmax_frame_dump_size); - captureStackTrace(context, frame_pointers); - return convertFramesToText(frame_pointers); - } -} - -} // stacktrace - - - diff --git a/src/std2_make_unique.hpp b/src/std2_make_unique.hpp deleted file mode 100644 index f17a81b..0000000 --- a/src/std2_make_unique.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/** ========================================================================== - * 2013 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. - * - * For more information see g3log/LICENSE or refer refer to http://unlicense.org - * - * make_unique will be in C++14, this implementation is copied as I understood - * Stephan T. Lavavej's description of it. - * - * PUBLIC DOMAIN and NOT under copywrite protection. - * - * - * Example: usage - * auto an_int = make_unique(123); - * auto a_string = make_unique(5, 'x'); - * auto an_int_array = make_unique(11, 22, 33); - * ********************************************* */ - -#ifndef STD2_MAKE_UNIQUE_HPP_ -#define STD2_MAKE_UNIQUE_HPP_ - -#include -#include -#include - -namespace std2 { - namespace impl_fut_stl { - - template - std::unique_ptr make_unique_helper(std::false_type, Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); - } - - template - std::unique_ptr make_unique_helper(std::true_type, Args&&... args) { - static_assert(std::extent::value == 0, "make_unique() is forbidden, please use make_unique(),"); - typedef typename std::remove_extent::type U; - return std::unique_ptr(new U[sizeof...(Args)] {std::forward(args)...}); - } - } - - template - std::unique_ptr make_unique(Args&&... args) { - return impl_fut_stl::make_unique_helper( - std::is_array(), std::forward(args)...); - } -} - -#endif - diff --git a/test_main/test_main.cpp b/test_main/test_main.cpp index fabf9fd..cc58ac8 100644 --- a/test_main/test_main.cpp +++ b/test_main/test_main.cpp @@ -9,9 +9,9 @@ int main(int argc, char *argv[]) { - testing::InitGoogleTest(&argc, argv); - int return_value = RUN_ALL_TESTS(); - std::cout << "FINISHED WITH THE TESTING" << std::endl; - return return_value; + testing::InitGoogleTest(&argc, argv); + int return_value = RUN_ALL_TESTS(); + std::cout << "FINISHED WITH THE TESTING" << std::endl; + return return_value; } diff --git a/test_performance/main_threaded_mean.cpp b/test_performance/main_threaded_mean.cpp index e21a690..163c0ea 100644 --- a/test_performance/main_threaded_mean.cpp +++ b/test_performance/main_threaded_mean.cpp @@ -2,7 +2,7 @@ * 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. - * + * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ @@ -20,90 +20,90 @@ const std::string title = "GOOGLE__GLOG"; #endif #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) - const std::string g_path = "./"; +const std::string g_path = "./"; #else - const std::string g_path = "/tmp/"; +const std::string g_path = "/tmp/"; #endif using namespace g2_test; -int main(int argc, char** argv) +int main(int argc, char **argv) { #ifdef G2_DYNAMIC_LOGGING std::cerr << "G2_DYNAMIC_LOGGING is enabled" << std::endl; -#else +#else std::cerr << "G2_DYNAMIC_LOGGING is DISABLED" << std::endl; #endif - - size_t number_of_threads =0; - if(argc == 2) - { - number_of_threads = atoi(argv[1]); - } - if(argc != 2 || number_of_threads == 0) - { - std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; - return 1; - } - std::ostringstream thread_count_oss; - thread_count_oss << number_of_threads; - const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-MEAN_LOG"; - const std::string g_measurement_dump= g_path + g_prefix_log_name + "_RESULT.txt"; + size_t number_of_threads = 0; + if (argc == 2) + { + number_of_threads = atoi(argv[1]); + } + if (argc != 2 || number_of_threads == 0) + { + std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; + return 1; + } - std::ostringstream oss; - const uint64_t us_to_s = 1000000; - oss << "\n\n" << title << " performance " << number_of_threads << " threads MEAN times\n"; - oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry - const uint64_t xtra_margin = 2; - oss << "*** It can take som time. Please wait: Approximate wait time on MY PC was: " <(application_end_time - start_time).count(); - uint64_t total_time_us = std::chrono::duration_cast(worker_end_time - start_time).count(); + auto worker_end_time = std::chrono::high_resolution_clock::now(); + uint64_t application_time_us = std::chrono::duration_cast(application_end_time - start_time).count(); + uint64_t total_time_us = std::chrono::duration_cast(worker_end_time - start_time).count(); - oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk"<< std::endl; - oss << "[Application(" << number_of_threads << "):\t\t:" << application_time_us/1000 << " ms]" << std::endl; - oss << "[Background thread to finish\t:" << total_time_us/uint64_t(1000 )<< " ms]" << std::endl; - oss << "\nAverage time per log entry:" << std::endl; - oss << "[Application: " << application_time_us/(number_of_threads*g_iterations) << " us]" << std::endl; - oss << "[Background+Application: " << total_time_us/(number_of_threads*g_iterations) << " us]" << std::endl; - writeTextToFile(g_measurement_dump,oss.str(), kAppend); - std::cout << "Result can be found at:" << g_measurement_dump << std::endl; + oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk" << std::endl; + oss << "[Application(" << number_of_threads << "):\t\t:" << application_time_us / 1000 << " ms]" << std::endl; + oss << "[Background thread to finish\t:" << total_time_us / uint64_t(1000 ) << " ms]" << std::endl; + oss << "\nAverage time per log entry:" << std::endl; + oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; + oss << "[Background+Application: " << total_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; + writeTextToFile(g_measurement_dump, oss.str(), kAppend); + std::cout << "Result can be found at:" << g_measurement_dump << std::endl; - return 0; + return 0; } diff --git a/test_performance/performance.h b/test_performance/performance.h index 6e11d8f..80bf925 100644 --- a/test_performance/performance.h +++ b/test_performance/performance.h @@ -5,8 +5,7 @@ * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ -#ifndef PERFORMANCE_G2_TEST_H_ -#define PERFORMANCE_G2_TEST_H_ +#pragma once #include #include @@ -22,7 +21,6 @@ #if defined(G2LOG_PERFORMANCE) #include "g2log.hpp" -#include "g2logworker.hpp" using namespace g2::internal; #elif defined(GOOGLE_GLOG_PERFORMANCE) @@ -126,6 +124,3 @@ inline void doLogWrites(const std::string& title) } // end namespace - - -#endif // fPERFORMANCE_G2_TEST_H_ diff --git a/test_unit/test_concept_sink.cpp b/test_unit/test_concept_sink.cpp index 89ac48d..63b11f0 100644 --- a/test_unit/test_concept_sink.cpp +++ b/test_unit/test_concept_sink.cpp @@ -16,11 +16,11 @@ #include #include "testing_helpers.h" -#include "std2_make_unique.hpp" -#include "g2sink.hpp" -#include "g2sinkwrapper.hpp" -#include "g2sinkhandle.hpp" -#include "g2logmessage.hpp" +#include "g3log/std2_make_unique.hpp" +#include "g3log/sink.hpp" +#include "g3log/sinkwrapper.hpp" +#include "g3log/sinkhandle.hpp" +#include "g3log/logmessage.hpp" using namespace std; diff --git a/test_unit/test_configuration.cpp b/test_unit/test_configuration.cpp index d4e510d..158eeba 100644 --- a/test_unit/test_configuration.cpp +++ b/test_unit/test_configuration.cpp @@ -15,8 +15,8 @@ #include #include #include -#include "g2time.hpp" -#include "g2future.hpp" +#include "g3log/time.hpp" +#include "g3log/future.hpp" TEST(Configuration, LOG) { // ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/ diff --git a/test_unit/test_crashhandler_windows.cpp b/test_unit/test_crashhandler_windows.cpp index e68e301..616aef7 100644 --- a/test_unit/test_crashhandler_windows.cpp +++ b/test_unit/test_crashhandler_windows.cpp @@ -10,7 +10,7 @@ #include #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -#include "stacktrace_windows.hpp" +#include "g3log/stacktrace_windows.hpp" #include diff --git a/test_unit/test_filechange.cpp b/test_unit/test_filechange.cpp index 3e2c867..7c64024 100644 --- a/test_unit/test_filechange.cpp +++ b/test_unit/test_filechange.cpp @@ -17,8 +17,8 @@ #include -#include "g2log.hpp" -#include "g2logworker.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logworker.hpp" #include "testing_helpers.h" using namespace testing_helpers; diff --git a/test_unit/test_io.cpp b/test_unit/test_io.cpp index 3b0ad29..0c79b33 100644 --- a/test_unit/test_io.cpp +++ b/test_unit/test_io.cpp @@ -7,10 +7,10 @@ * ============================================================================*/ #include -#include "g2log.hpp" -#include "g2logworker.hpp" +#include "g3log/g3log.hpp" +#include "g3log/logworker.hpp" #include "testing_helpers.h" -#include "g2loglevels.hpp" +#include "g3log/loglevels.hpp" #include #include @@ -282,14 +282,14 @@ TEST(LogTest, LOG_preFatalLogging_hook) { g2::setFatalPreLoggingHook(fatalCounter); LOG(FATAL) << "This message is fatal"; logger.reset(); - EXPECT_EQ(g_fatal_counter.load(), 1); + EXPECT_EQ(g_fatal_counter.load(), size_t{1}); } { // Now with no fatal pre-logging-hook RestoreFileLogger logger(log_directory); ASSERT_FALSE(mockFatalWasCalled()); g_fatal_counter.store(0); LOG(FATAL) << "This message is fatal"; - EXPECT_EQ(g_fatal_counter.load(), 0); + EXPECT_EQ(g_fatal_counter.load(), size_t{0}); } } diff --git a/test_unit/test_linux_dynamic_loaded_sharedlib.cpp b/test_unit/test_linux_dynamic_loaded_sharedlib.cpp index 35ad147..279fcd4 100644 --- a/test_unit/test_linux_dynamic_loaded_sharedlib.cpp +++ b/test_unit/test_linux_dynamic_loaded_sharedlib.cpp @@ -7,10 +7,10 @@ * ============================================================================*/ -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/test_unit/test_sink.cpp b/test_unit/test_sink.cpp index 5e22bfc..dded84a 100644 --- a/test_unit/test_sink.cpp +++ b/test_unit/test_sink.cpp @@ -17,9 +17,9 @@ #include #include "testing_helpers.h" -#include "g2logmessage.hpp" -#include "g2logworker.hpp" -#include "std2_make_unique.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/logworker.hpp" +#include "g3log/std2_make_unique.hpp" using namespace testing_helpers; using namespace std; diff --git a/test_unit/testing_helpers.cpp b/test_unit/testing_helpers.cpp index 8d21d30..9f077a7 100644 --- a/test_unit/testing_helpers.cpp +++ b/test_unit/testing_helpers.cpp @@ -11,9 +11,8 @@ #include #include "testing_helpers.h" #include "g2log.hpp" -#include "g2logworker.hpp" -#include "std2_make_unique.hpp" -#include "g2logmessage.hpp" +#include "g3log/std2_make_unique.hpp" +#include "g3log/logmessage.hpp" #include using namespace std; diff --git a/test_unit/testing_helpers.h b/test_unit/testing_helpers.h index 40367ad..f19874f 100644 --- a/test_unit/testing_helpers.h +++ b/test_unit/testing_helpers.h @@ -14,9 +14,9 @@ #include #include #include -#include "g2logworker.hpp" -#include "g2logmessage.hpp" -#include "g2filesink.hpp" +#include "g3log/logworker.hpp" +#include "g3log/logmessage.hpp" +#include "g3log/filesink.hpp" namespace testing_helpers {