mirror of
https://github.com/KjellKod/g3log.git
synced 2025-02-21 22:56:20 +01:00
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:
parent
94db9bb4db
commit
6750efe8fe
@ -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})
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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
|
251
src/g2log.cpp
251
src/g2log.cpp
@ -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
|
||||
|
||||
|
||||
|
221
src/g2log.hpp
221
src/g2log.hpp
@ -1,215 +1,14 @@
|
||||
/** ==========================================================================
|
||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
*
|
||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||
* ============================================================================
|
||||
*
|
||||
* Filename:g2log.hpp Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||
* at least in "spirit" from the following sources
|
||||
* 1. kjellkod.cc ;)
|
||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
||||
* 5. Various Q&A at StackOverflow
|
||||
* ********************************************* */
|
||||
|
||||
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
*
|
||||
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||
* ============================================================================*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <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"
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
|
@ -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
|
@ -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
|
63
src/g3log/crashhandler.hpp
Normal file
63
src/g3log/crashhandler.hpp
Normal 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();
|
||||
}
|
235
src/g3log/crashhandler_unix.cpp
Normal file
235
src/g3log/crashhandler_unix.cpp
Normal 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
|
||||
|
259
src/g3log/crashhandler_windows.cpp
Normal file
259
src/g3log/crashhandler_windows.cpp
Normal 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
92
src/g3log/filesink.cpp
Normal 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
42
src/g3log/filesink.hpp
Normal 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
|
||||
|
123
src/g3log/filesinkhelper.ipp
Normal file
123
src/g3log/filesinkhelper.ipp
Normal 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
63
src/g3log/future.hpp
Normal 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
252
src/g3log/g3log.cpp
Normal 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
216
src/g3log/g3log.hpp
Normal 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__)
|
||||
|
||||
|
||||
|
@ -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];
|
@ -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;
|
||||
|
||||
};
|
@ -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
|
||||
|
@ -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
201
src/g3log/logmessage.cpp
Normal 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
|
@ -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
136
src/g3log/logworker.cpp
Normal 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
|
@ -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_
|
@ -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
|
80
src/g3log/shared_queue.hpp
Normal file
80
src/g3log/shared_queue.hpp
Normal 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
79
src/g3log/sink.hpp
Normal 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
55
src/g3log/sinkhandle.hpp
Normal 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());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
222
src/g3log/stacktrace_windows.cpp
Normal file
222
src/g3log/stacktrace_windows.cpp
Normal 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(¤t_context, 0, sizeof(CONTEXT));
|
||||
RtlCaptureContext(¤t_context);
|
||||
return stackdump(¤t_context);
|
||||
}
|
||||
|
||||
/// helper function: retrieve stackdump, starting from an exception pointer
|
||||
std::string stackdump(EXCEPTION_POINTERS *info) {
|
||||
auto context = info->ContextRecord;
|
||||
return stackdump(context);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// main stackdump function. retrieve stackdump, from the given context
|
||||
std::string stackdump(CONTEXT *context) {
|
||||
|
||||
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass
|
||||
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
|
||||
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
|
||||
return recursive_crash;
|
||||
}
|
||||
++g_thread_local_recursive_crash_check;
|
||||
|
||||
static std::mutex m;
|
||||
std::lock_guard<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
|
||||
|
||||
|
||||
|
@ -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
|
48
src/g3log/std2_make_unique.hpp
Normal file
48
src/g3log/std2_make_unique.hpp
Normal 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)...);
|
||||
}
|
||||
}
|
||||
|
@ -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
77
src/g3log/time.cpp
Normal 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
51
src/g3log/time.hpp
Normal 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) ;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
@ -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(¤t_context, 0, sizeof(CONTEXT));
|
||||
RtlCaptureContext(¤t_context);
|
||||
return stackdump(¤t_context);
|
||||
}
|
||||
|
||||
/// helper function: retrieve stackdump, starting from an exception pointer
|
||||
std::string stackdump(EXCEPTION_POINTERS* info) {
|
||||
auto context = info->ContextRecord;
|
||||
return stackdump(context);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// main stackdump function. retrieve stackdump, from the given context
|
||||
std::string stackdump(CONTEXT* context) {
|
||||
|
||||
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass
|
||||
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
|
||||
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
|
||||
return recursive_crash;
|
||||
}
|
||||
++g_thread_local_recursive_crash_check;
|
||||
|
||||
static std::mutex m;
|
||||
std::lock_guard<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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
|
@ -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/
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user