g2log.hpp is kept as a helper include. It will pull in the most frequent include files

All other are under g3log. Example: #include <g3log/time.hpp> #include <g3log/g3log.hpp> 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)
This commit is contained in:
Kjell Hedstrom 2015-07-16 01:55:23 -06:00
parent 94db9bb4db
commit 6750efe8fe
67 changed files with 2915 additions and 2870 deletions

View File

@ -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})

View File

@ -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
)

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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 <iomanip>
#include <thread>
#include <iostream>
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<std::string> 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<std::string> 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);
}

View File

@ -6,8 +6,10 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <g2logworker.hpp>
#include <g2log.hpp>
//#/g3log.hpp>
#include <iostream>
#include <cctype>
#include <future>
@ -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<int> 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<void()> fatal_function) {
fatal_function();
}
void CallExitFunction(std::function<void()> 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<int> 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<void()> fatal_function) {
fatal_function();
}
void CallExitFunction(std::function<void()> 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());

View File

@ -6,8 +6,8 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include "g2logworker.hpp"
#include "g2log.hpp"
#include <iomanip>
#include <thread>
#include <iostream>
@ -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;

View File

@ -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 <string>
#include <csignal>
#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();
}

View File

@ -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 <csignal>
#include <cstring>
#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 <unistd.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include "g2loglevels.hpp"
// Linux/Clang, OSX/Clang, OSX/gcc
#if (defined(__clang__) || defined(__APPLE__))
#include <sys/ucontext.h>
#else
#include <ucontext.h>
#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<g2::SignalType>(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<int>(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<int>(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

View File

@ -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 <windows.h>
#include <intrin.h>
#include <csignal>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <atomic>
#include <process.h> // getpid
#include "crashhandler.hpp"
#include "stacktrace_windows.hpp"
#include "g2logmessage.hpp"
#include "g2logmessagecapture.hpp"
#define getpid _getpid
namespace {
std::atomic<bool> 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<g2::SignalType>(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<g2::SignalType>(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<int>(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

View File

@ -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 <cassert>
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<std::ofstream> 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

View File

@ -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 <string>
#include <memory>
#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<std::ofstream> _outptr;
void addLogFileHeader();
std::ofstream & filestream() {return *(_outptr.get()); }
FileSink& operator=(const FileSink&) = delete;
FileSink(const FileSink& other) = delete;
};
} // g2

View File

@ -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 <memory>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
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<std::ofstream> createLogFile(const std::string& file_with_full_path) {
std::unique_ptr<std::ofstream> 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;
}
}
}

View File

@ -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 <future>
#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<Active> bgWorker{Active::createActive()};
// ...
// auto msg_call=[=](){return ("Hello from the Background");};
// auto future_msg = g2::spawn_task(msg_lambda, bgWorker.get());
template <typename Func, class BgWorker>
std::future<typename std::result_of<Func()>::type> spawn_task(Func func, BgWorker* worker)
{
typedef typename std::result_of<Func()>::type result_type;
typedef std::packaged_task<result_type()> task_type;
if (nullptr == worker) {
auto p = std::make_shared<std::promise<result_type>>();
std::future<result_type> 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_type> result = task.get_future();
worker->send(MoveOnCopy<task_type>(std::move(task)));
return std::move(result);
}
} // end namespace g2
#endif // G2FUTURE_H

View File

@ -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 <cstdio> // vsnprintf
#include <mutex>
#include <csignal>
#include <memory>
#include <iostream>
#include <thread>
#include <atomic>
#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<g2::LogMessage> g_first_unintialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_unintialized_flag;
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = []{ /*does nothing */};
std::function<void(void)> g_fatal_pre_logging_hook;
std::atomic<size_t> 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<std::mutex> 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<void(void)> pre_fatal_hook) {
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
g_fatal_pre_logging_hook = pre_fatal_hook;
}
// By default this function pointer goes to \ref pushFatalMessageToLogger;
std::function<void(FatalMessagePtr) > 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<void(FatalMessagePtr) > 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<std::mutex> 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<LogMessage>(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<FatalMessage>(*(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

View File

@ -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 <string>
#include <cstdarg>
#include <functional>
#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 <intrin.h>
* 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<void(void)> 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<void(FatalMessagePtr)> 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"

View File

@ -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 <mutex>
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<std::chrono::microseconds>(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

View File

@ -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 <cassert>
#include <functional>
#include "active.hpp"
#include "g2log.hpp"
#include "g2time.hpp"
#include "g2future.hpp"
#include "crashhandler.hpp"
#include <iostream> // remove
namespace g2 {
LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }
void LogWorkerImpl::bgSave(g2::LogMessagePtr msgPtr) {
std::unique_ptr<LogMessage> 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<LogMessage> 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<g2::internal::SinkWrapper> 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> LogWorker::createWithNoSink() {
return std::unique_ptr<LogWorker>(new LogWorker);
}
DefaultFileLogger::DefaultFileLogger(const std::string& log_prefix, const std::string& log_directory)
: worker(LogWorker::createWithNoSink())
, sink(worker->addSink(std2::make_unique<g2::FileSink>(log_prefix, log_directory), &FileSink::fileWrite)) { }
} // g2

View File

@ -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 <memory>
#include <functional>
#include <type_traits>
#include "g2sinkwrapper.hpp"
#include "active.hpp"
#include "g2future.hpp"
#include "g2logmessage.hpp"
namespace g2 {
namespace internal {
typedef std::function<void(LogMessageMover) > 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<class T>
struct Sink : public SinkWrapper {
std::unique_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call;
template<typename DefaultLogCall >
Sink(std::unique_ptr<T> 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<T> sink, void(T::*Call)(std::string) )
: SinkWrapper{},
_real_sink {std::move(sink)},
_bg(kjellkod::Active::createActive()) {
std::function<void(std::string)> 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<typename Call, typename... Args>
auto async(Call call, Args&&... args)-> std::future< typename std::result_of<decltype(call)(T, Args...)>::type> {
return g2::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
}
};
} // internal
} // g2
#endif /* G2SINK_IPP */

View File

@ -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 <memory>
#include <functional>
#include <type_traits>
#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 T>
class SinkHandle {
std::weak_ptr<internal::Sink<T>> _sink;
public:
SinkHandle(std::shared_ptr<internal::Sink<T>> 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<typename AsyncCall, typename... Args>
auto call(AsyncCall func , Args&&... args) -> std::future<typename std::result_of<decltype(func)(T, Args...)>::type> {
try {
std::shared_ptr<internal::Sink<T>> sink(_sink);
return sink->async(func, std::forward<Args>(args)...);
} catch (const std::bad_weak_ptr& e) {
typedef typename std::result_of<decltype(func)(T, Args...)>::type PromiseType;
std::promise<PromiseType> promise;
promise.set_exception(std::make_exception_ptr(e));
return std::move(promise.get_future());
}
}
};
}
#endif /* G2SINKHANDLE_H */

View File

@ -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 <sstream>
#include <string>
#include <chrono>
#include <thread>
#include <cassert>
#include <iomanip>
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<struct tm*> (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

View File

@ -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 <ctime>
#include <string>
#include <chrono>
// 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<std::chrono::system_clock> 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

View File

@ -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 <thread>
#include <functional>
#include <memory>
#include "shared_queue.hpp"
#include "g3log/shared_queue.hpp"
namespace kjellkod {
typedef std::function<void() > Callback;
typedef std::function<void() > 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<Callback> mq_;
std::thread thd_;
bool done_;
shared_queue<Callback> 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<Active> createActive() {
std::unique_ptr<Active> 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<Active> createActive() {
std::unique_ptr<Active> aPtr(new Active());
aPtr->thd_ = std::thread(&Active::run, aPtr.get());
return aPtr;
}
};
} // kjellkod

View File

@ -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 <string>
#include <csignal>
#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();
}

View File

@ -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 <csignal>
#include <cstring>
#include <unistd.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <cstdlib>
#include <sstream>
#include <iostream>
// Linux/Clang, OSX/Clang, OSX/gcc
#if (defined(__clang__) || defined(__APPLE__))
#include <sys/ucontext.h>
#else
#include <ucontext.h>
#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<g2::SignalType>(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<int>(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<int>(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

View File

@ -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 <windows.h>
#include <intrin.h>
#include <csignal>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <atomic>
#include <process.h> // getpid
#include "g3log/crashhandler.hpp"
#include "g3log/stacktrace_windows.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/logcapture.hpp"
#define getpid _getpid
namespace {
std::atomic<bool> 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<g2::SignalType>(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<g2::SignalType>(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<int>(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

92
src/g3log/filesink.cpp Normal file
View File

@ -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 <cassert>
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<std::ofstream> 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

42
src/g3log/filesink.hpp Normal file
View File

@ -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 <string>
#include <memory>
#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<std::ofstream> _outptr;
void addLogFileHeader();
std::ofstream &filestream() {
return *(_outptr.get());
}
FileSink &operator=(const FileSink &) = delete;
FileSink(const FileSink &other) = delete;
};
} // g2

View File

@ -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 <memory>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
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<std::ofstream> createLogFile(const std::string &file_with_full_path) {
std::unique_ptr<std::ofstream> 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;
}
}
}

63
src/g3log/future.hpp Normal file
View File

@ -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 <future>
#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<Active> bgWorker{Active::createActive()};
// ...
// auto msg_call=[=](){return ("Hello from the Background");};
// auto future_msg = g2::spawn_task(msg_lambda, bgWorker.get());
template <typename Func, class BgWorker>
std::future<typename std::result_of<Func()>::type> spawn_task(Func func, BgWorker *worker)
{
typedef typename std::result_of<Func()>::type result_type;
typedef std::packaged_task<result_type()> task_type;
if (nullptr == worker) {
auto p = std::make_shared<std::promise<result_type>>();
std::future<result_type> 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_type> result = task.get_future();
worker->send(MoveOnCopy<task_type>(std::move(task)));
return std::move(result);
}
} // end namespace g2

252
src/g3log/g3log.cpp Normal file
View File

@ -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 <cstdio> // vsnprintf
#include <mutex>
#include <csignal>
#include <memory>
#include <iostream>
#include <thread>
#include <atomic>
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<g2::LogMessage> g_first_unintialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_unintialized_flag;
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */};
std::function<void(void)> g_fatal_pre_logging_hook;
std::atomic<size_t> 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<std::mutex> 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<void(void)> pre_fatal_hook) {
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
g_fatal_pre_logging_hook = pre_fatal_hook;
}
// By default this function pointer goes to \ref pushFatalMessageToLogger;
std::function<void(FatalMessagePtr) > 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<void(FatalMessagePtr) > 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<std::mutex> 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<LogMessage>(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<FatalMessage>(*(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

216
src/g3log/g3log.hpp Normal file
View File

@ -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 <string>
#include <cstdarg>
#include <functional>
#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 <intrin.h>
* 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<void(void)> 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<void(FatalMessagePtr)> 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__)

View File

@ -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];

View File

@ -7,13 +7,15 @@
* ============================================================================*/
#pragma once
#include "g3log/loglevels.hpp"
#include "g3log/g3log.hpp"
#include "g3log/crashhandler.hpp"
#include <string>
#include <sstream>
#include <cstdarg>
#include <csignal>
#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;
};

View File

@ -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 <atomic>
#include <cassert>
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<bool> g_log_level_status[4]{{true},{true},{true},{true}};
const int g_level_size {
FATAL.value + 1
};
std::atomic<bool> g_log_level_status[4] {{true}, {true}, {true}, {true}};
#endif
} // internal

View File

@ -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

201
src/g3log/logmessage.cpp Normal file
View File

@ -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 <mutex>
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<std::chrono::microseconds>(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

View File

@ -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 <string>
#include <sstream>
#include <chrono>
#include <thread>
#include "g2loglevels.hpp"
#include "g2time.hpp"
#include "g2moveoncopy.hpp"
#include "crashhandler.hpp"
#include <memory>
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;

136
src/g3log/logworker.cpp Normal file
View File

@ -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 <iostream>
#include <cassert>
#include <functional>
namespace g2 {
LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }
void LogWorkerImpl::bgSave(g2::LogMessagePtr msgPtr) {
std::unique_ptr<LogMessage> 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<LogMessage> 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<g2::internal::SinkWrapper> 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> LogWorker::createWithNoSink() {
return std::unique_ptr<LogWorker>(new LogWorker);
}
DefaultFileLogger::DefaultFileLogger(const std::string &log_prefix, const std::string &log_directory)
: worker(LogWorker::createWithNoSink())
, sink(worker->addSink(std2::make_unique<g2::FileSink>(log_prefix, log_directory), &FileSink::fileWrite)) { }
} // g2

View File

@ -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 <memory>
#include <string>
#include <utility>
#include <vector>
#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<LogWorker> worker;
std::unique_ptr<g2::SinkHandle<g2::FileSink>> 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<g2::internal::SinkWrapper> 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<LogWorker> createWithNoSink();
@ -86,5 +83,3 @@ namespace g2 {
}
};
} // g2
#endif // LOG_WORKER_H_

View File

@ -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

View File

@ -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 <queue>
#include <mutex>
#include <exception>
#include <condition_variable>
/** Multiple producer, multiple consumer thread safe queue
* Since 'return by reference' is used this queue won't throw */
template<typename T>
class shared_queue
{
std::queue<T> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(m_);
return queue_.empty();
}
unsigned size() const {
std::lock_guard<std::mutex> lock(m_);
return queue_.size();
}
};

79
src/g3log/sink.hpp Normal file
View File

@ -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 <memory>
#include <functional>
#include <type_traits>
namespace g2 {
namespace internal {
typedef std::function<void(LogMessageMover) > 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<class T>
struct Sink : public SinkWrapper {
std::unique_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call;
template<typename DefaultLogCall >
Sink(std::unique_ptr<T> 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<T> sink, void(T::*Call)(std::string) )
: SinkWrapper {},
_real_sink {std::move(sink)},
_bg(kjellkod::Active::createActive()) {
std::function<void(std::string)> 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<typename Call, typename... Args>
auto async(Call call, Args &&... args)-> std::future< typename std::result_of<decltype(call)(T, Args...)>::type> {
return g2::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
}
};
} // internal
} // g2

55
src/g3log/sinkhandle.hpp Normal file
View File

@ -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 <memory>
#include <functional>
#include <type_traits>
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 T>
class SinkHandle {
std::weak_ptr<internal::Sink<T>> _sink;
public:
SinkHandle(std::shared_ptr<internal::Sink<T>> 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<typename AsyncCall, typename... Args>
auto call(AsyncCall func , Args &&... args) -> std::future<typename std::result_of<decltype(func)(T, Args...)>::type> {
try {
std::shared_ptr<internal::Sink<T>> sink(_sink);
return sink->async(func, std::forward<Args>(args)...);
} catch (const std::bad_weak_ptr &e) {
typedef typename std::result_of<decltype(func)(T, Args...)>::type PromiseType;
std::promise<PromiseType> promise;
promise.set_exception(std::make_exception_ptr(e));
return std::move(promise.get_future());
}
}
};
}

View File

@ -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 */

View File

@ -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 <windows.h>
#include <dbghelp.h>
#include <map>
#include <memory>
#include <atomic>
#include <cassert>
#include <vector>
#include <cstdlib>
#include <mutex>
#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<g2::SignalType, std::string> 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<uint64_t> &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<uint64_t> &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_INFO *>(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<uint64_t> &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(&current_context, 0, sizeof(CONTEXT));
RtlCaptureContext(&current_context);
return stackdump(&current_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<std::mutex> 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<void> RaiiSymCleaner(nullptr, [&](void *) {
SymCleanup(GetCurrentProcess());
}); // Raii sym cleanup
const size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> 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

View File

@ -17,25 +17,26 @@
#error "stacktrace_win.cpp used but not on a windows system"
#endif
#include "g3log/crashhandler.hpp"
#include <string>
#include <windows.h>
#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

View File

@ -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<int>(123);
* auto a_string = make_unique<string>(5, 'x');
* auto an_int_array = make_unique<int[]>(11, 22, 33);
* ********************************************* */
#pragma once
#include <memory>
#include <utility>
#include <type_traits>
namespace std2 {
namespace impl_fut_stl {
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args &&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args &&... args) {
static_assert(std::extent<T>::value == 0, "make_unique<T[N]>() is forbidden, please use make_unique<T[]>(),");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)] {std::forward<Args>(args)...});
}
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique(Args &&... args) {
return impl_fut_stl::make_unique_helper<T>(
std::is_array<T>(), std::forward<Args>(args)...);
}
}

View File

@ -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<void()>. 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<class _Fty2>
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);
}

77
src/g3log/time.cpp Normal file
View File

@ -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 <sstream>
#include <string>
#include <chrono>
#include <thread>
#include <cassert>
#include <iomanip>
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<struct tm *> (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

51
src/g3log/time.hpp Normal file
View File

@ -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 <ctime>
#include <string>
#include <chrono>
// 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<std::chrono::system_clock> 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) ;
}

View File

@ -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 <queue>
#include <mutex>
#include <exception>
#include <condition_variable>
/** Multiple producer, multiple consumer thread safe queue
* Since 'return by reference' is used this queue won't throw */
template<typename T>
class shared_queue
{
std::queue<T> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(m_);
return queue_.empty();
}
unsigned size() const{
std::lock_guard<std::mutex> lock(m_);
return queue_.size();
}
};

View File

@ -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 <windows.h>
#include <dbghelp.h>
#include <map>
#include <memory>
#include <atomic>
#include <cassert>
#include <vector>
#include <cstdlib>
#include <mutex>
#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<g2::SignalType, std::string> 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<uint64_t>& 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<uint64_t>& 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_INFO*>(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<uint64_t>& 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(&current_context, 0, sizeof(CONTEXT));
RtlCaptureContext(&current_context);
return stackdump(&current_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<std::mutex> 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<void> RaiiSymCleaner(nullptr, [&](void*) {
SymCleanup(GetCurrentProcess());
}); // Raii sym cleanup
const size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> 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

View File

@ -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<int>(123);
* auto a_string = make_unique<string>(5, 'x');
* auto an_int_array = make_unique<int[]>(11, 22, 33);
* ********************************************* */
#ifndef STD2_MAKE_UNIQUE_HPP_
#define STD2_MAKE_UNIQUE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
namespace std2 {
namespace impl_fut_stl {
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
static_assert(std::extent<T>::value == 0, "make_unique<T[N]>() is forbidden, please use make_unique<T[]>(),");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)] {std::forward<Args>(args)...});
}
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return impl_fut_stl::make_unique_helper<T>(
std::is_array<T>(), std::forward<Args>(args)...);
}
}
#endif

View File

@ -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;
}

View File

@ -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: " <<number_of_threads* (uint64_t) (g_iterations * 10 * xtra_margin / us_to_s ) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
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";
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: " << number_of_threads* (uint64_t) (g_iterations * 10 * xtra_margin / us_to_s ) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
#if defined(G2LOG_PERFORMANCE)
auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(g_prefix_log_name, g_path);
g2::initializeLogging(logger_n_handle.worker.get());
auto logger_n_handle = g2::LogWorker::createWithDefaultLogger(g_prefix_log_name, g_path);
g2::initializeLogging(logger_n_handle.worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
google::InitGoogleLogging(argv[0]);
#endif
auto start_time = std::chrono::high_resolution_clock::now();
auto start_time = std::chrono::high_resolution_clock::now();
std::thread* threads = new std::thread[number_of_threads];
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for(size_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx+1;
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(doLogWrites,thread_name);
}
for(size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads;
std::thread *threads = new std::thread[number_of_threads];
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx + 1;
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(doLogWrites, thread_name);
}
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads;
#if defined(G2LOG_PERFORMANCE)
logger_n_handle.worker.reset(); // will flush anything in the queue to file
logger_n_handle.worker.reset(); // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
google::ShutdownGoogleLogging();
#endif
auto worker_end_time = std::chrono::high_resolution_clock::now();
uint64_t application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(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<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(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;
}

View File

@ -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 <iostream>
#include <sstream>
@ -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_

View File

@ -16,11 +16,11 @@
#include <atomic>
#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;

View File

@ -15,8 +15,8 @@
#include <exception>
#include <functional>
#include <memory>
#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/

View File

@ -10,7 +10,7 @@
#include <gtest/gtest.h>
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#include "stacktrace_windows.hpp"
#include "g3log/stacktrace_windows.hpp"
#include <windows.h>

View File

@ -17,8 +17,8 @@
#include <thread>
#include "g2log.hpp"
#include "g2logworker.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logworker.hpp"
#include "testing_helpers.h"
using namespace testing_helpers;

View File

@ -7,10 +7,10 @@
* ============================================================================*/
#include <gtest/gtest.h>
#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 <memory>
#include <string>
@ -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});
}
}

View File

@ -7,10 +7,10 @@
* ============================================================================*/
#include <g2log.hpp>
#include <g2logworker.hpp>
#include <g2filesink.hpp>
#include <std2_make_unique.hpp>
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/filesink.hpp>
#include <g3log/std2_make_unique.hpp>
#include <gtest/gtest.h>
#include <string>

View File

@ -17,9 +17,9 @@
#include <future>
#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;

View File

@ -11,9 +11,8 @@
#include <iostream>
#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 <fstream>
using namespace std;

View File

@ -14,9 +14,9 @@
#include <chrono>
#include <thread>
#include <algorithm>
#include "g2logworker.hpp"
#include "g2logmessage.hpp"
#include "g2filesink.hpp"
#include "g3log/logworker.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/filesink.hpp"
namespace testing_helpers {