mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-12 10:23:50 +01:00
Added signalhandler and a little bit of stackdump for linux.
BUGS: how to test fatal unit tests? BUGS: the stack dump is fugly
This commit is contained in:
parent
9dbc9023fc
commit
b7240a610b
BIN
3rdParty/glog/glog-0.3.1_hacked_to_be_truly_synchronous.tgz
vendored
Normal file
BIN
3rdParty/glog/glog-0.3.1_hacked_to_be_truly_synchronous.tgz
vendored
Normal file
Binary file not shown.
@ -1,16 +1,25 @@
|
||||
# CMakeLists.txt cmake configuration for g2log test
|
||||
# g2log is a KjellKod Logger
|
||||
# 2011 @author Kjell Hedström, hedstrom@kjellkod.cc */
|
||||
#
|
||||
# g2log is a KjellKod Logger
|
||||
|
||||
|
||||
# WINDOWS == README: Example how to setup environment + running an example
|
||||
# 1. please use the "Visual Studio Command Prompt 2010)"
|
||||
# 2. from the g2log folder
|
||||
# mkdir build
|
||||
# cd build;
|
||||
# 3. cmake -G "Visual Studio 10" ..
|
||||
# 4. msbuild g2log-example.sln
|
||||
# 5. Debug\g2log-example.exe
|
||||
#
|
||||
# == README: Example how to setup environment + running tests ===============
|
||||
|
||||
# LINUX == README: Example how to setup environment + running tests for ===============
|
||||
# 1. Install gtest
|
||||
# cmake
|
||||
# make
|
||||
# make install (possibly as root)
|
||||
#
|
||||
#
|
||||
#
|
||||
# 2. update path to libraries
|
||||
# sudo /sbin/ldconfig -v | grep gtest
|
||||
#
|
||||
@ -19,9 +28,7 @@
|
||||
# libgtest.so.0 -> libgtest.so.0.0.0
|
||||
# libgtest_main.so.0 -> libgtest_main.so.0.0.0
|
||||
#
|
||||
#
|
||||
#
|
||||
# 3. To try this out from folder g2log:
|
||||
# 3. LINUX:To try this out from folder g2log:
|
||||
# mkdir build
|
||||
# cd build
|
||||
# cmake .. # create makefiles in g2log/build directory
|
||||
@ -31,12 +38,24 @@
|
||||
# ============================================================================
|
||||
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
project (kjellkod_logger)
|
||||
set(LOG_SRC ${kjellkod_logger_SOURCE_DIR}/src)
|
||||
project (g2log_by_kjellkod)
|
||||
set(LOG_SRC ${g2log_by_kjellkod_SOURCE_DIR}/src)
|
||||
MESSAGE(" LOG_SRC = : ${LOG_SRC}")
|
||||
include_directories(${LOG_SRC})
|
||||
SET(ACTIVE_CPP0xx_DIR "Release")
|
||||
|
||||
|
||||
|
||||
IF(UNIX)
|
||||
MESSAGE("")
|
||||
MESSAGE("cmake for *NIX ")
|
||||
MESSAGE("if cmake finishes OK, do make")
|
||||
MESSAGE("then run './g2log-example' or whatever performance test you feel like trying")
|
||||
MESSAGE("")
|
||||
set(PLATFORM_LINK_LIBRIES justthread rt)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++0x ${CMAKE_CXX_FLAGS_DEBUG} -pthread -I/usr/include/justthread")
|
||||
set(G2_LOG_FILES ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler_unix.cpp)
|
||||
include_directories("/usr/include/justthread")
|
||||
|
||||
# SETUP for GTEST
|
||||
set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped)
|
||||
@ -44,80 +63,95 @@ IF(UNIX)
|
||||
include_directories(${GTEST_INCLUDE_DIRECTORIES})
|
||||
add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc)
|
||||
enable_testing(true)
|
||||
ENDIF(UNIX)
|
||||
|
||||
#Visual Studio 2010
|
||||
IF(WIN32)
|
||||
MESSAGE("")
|
||||
MESSAGE("cmake for Visual Studio 2010")
|
||||
MESSAGE("if cmake finishes OK, do 'msbuild g2log_by_kjellkod.sln'")
|
||||
MESSAGE("then run 'Debug\\g2log-example.exe' or whatever performance test you feel like trying")
|
||||
MESSAGE("")
|
||||
set(PLATFORM_LINK_LIBRIES $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc10_mdd.lib)
|
||||
set(G2_LOG_FILES ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler_win.cpp)
|
||||
include_directories("$ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/include")
|
||||
ENDIF(WIN32)
|
||||
|
||||
|
||||
# make the src directory available
|
||||
include_directories(/usr/include/justthread) #not necessarily needed if it's in the path
|
||||
|
||||
# GENERIC STEPS
|
||||
# add a ActiveObject library
|
||||
set(ACTIVE_DIR ${LOG_SRC})
|
||||
include_directories(${ACTIVE_DIR})
|
||||
MESSAGE(" ACTIVE_DIR = : ${ACTIVE_DIR}")
|
||||
SET(ACTIVE_CPP0xx_DIR "Release")
|
||||
add_library(lib_activeobject ${ACTIVE_DIR}/active.cpp ${ACTIVE_DIR}/active.h ${ACTIVE_DIR}/shared_queue.h)
|
||||
add_library(lib_activeobject ${LOG_SRC}/active.cpp ${LOG_SRC}/active.h ${LOG_SRC}/shared_queue.h)
|
||||
set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
|
||||
# add a g2log library
|
||||
include_directories(src)
|
||||
include_directories(${LOG_SRC})
|
||||
#MESSAGE(" LOG_SRC = : ${LOG_SRC}")
|
||||
add_library(lib_logger ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp )
|
||||
set_target_properties(lib_logger PROPERTIES LINKER_LANGUAGE CXX)
|
||||
target_link_libraries(lib_logger lib_activeobject)
|
||||
MESSAGE(" LOG_SRC = : ${LOG_SRC}")
|
||||
MESSAGE(" g2logger files: [${G2_LOG_FILES}]")
|
||||
add_library(lib_g2logger ${G2_LOG_FILES})
|
||||
set_target_properties(lib_g2logger PROPERTIES LINKER_LANGUAGE CXX)
|
||||
target_link_libraries(lib_g2logger lib_activeobject)
|
||||
|
||||
|
||||
# create the the example EXECUTABLE
|
||||
add_executable(g2log-example src/main.cpp)
|
||||
# link executable with the src library
|
||||
target_link_libraries(g2log-example lib_activeobject lib_logger justthread rt)
|
||||
target_link_libraries(g2log-example lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Below are g2log unit testTEST
|
||||
# and PERFORMANCE comparisons between g2log and google's glog
|
||||
#
|
||||
#
|
||||
include_directories(build)
|
||||
|
||||
# create the the TEST executable
|
||||
add_executable(g2log-unit_test ../test_main/test_main.cpp test/test_io.cpp)
|
||||
target_link_libraries(g2log-unit_test lib_activeobject lib_logger gtest_160_lib justthread rt)
|
||||
|
||||
# ---- Below g2log Performance -----
|
||||
# create the the g2log MEAN_PERFORMANCE executable
|
||||
add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h)
|
||||
set_target_properties(g2log-performance-mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
|
||||
target_link_libraries(g2log-performance-mean lib_activeobject lib_logger justthread rt)
|
||||
target_link_libraries(g2log-performance-mean lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# create the the g2log TWO_THREADS_MEAN_PERFORMANCE executable
|
||||
add_executable(g2log-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h)
|
||||
set_target_properties(g2log-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
|
||||
target_link_libraries(g2log-performance-2threads_mean lib_activeobject lib_logger justthread rt)
|
||||
target_link_libraries(g2log-performance-2threads_mean lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# create the the g2log TWO_THREADS_WORST_CASE_PERFORMANCE executable
|
||||
add_executable(g2log-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h)
|
||||
set_target_properties(g2log-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
|
||||
target_link_libraries(g2log-performance-2threads_worst lib_activeobject lib_logger justthread rt)
|
||||
target_link_libraries(g2log-performance-2threads_worst lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
|
||||
|
||||
|
||||
IF(UNIX)
|
||||
#
|
||||
# ---- Below GOOGLE glog Performance -----
|
||||
# create the the GOOGLE MEAN_PERFORMANCE executable
|
||||
# Generate the DEFINE (for glog) needed to differentiate between the glog and the g2log test
|
||||
add_executable(google_glog-performance-mean test/main_mean.cpp test/performance.h)
|
||||
set_target_properties(google_glog-performance-mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
|
||||
target_link_libraries(google_glog-performance-mean lib_activeobject glog justthread rt)
|
||||
target_link_libraries(google_glog-performance-mean lib_activeobject glog ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# create the the GOOGLE MEAN_PERFORMANCE executable
|
||||
add_executable(google_glog-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h)
|
||||
set_target_properties(google_glog-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
|
||||
target_link_libraries(google_glog-performance-2threads_mean lib_activeobject glog justthread rt)
|
||||
target_link_libraries(google_glog-performance-2threads_mean lib_activeobject glog ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# create the the GOOGLE MEAN_PERFORMANCE executable
|
||||
add_executable(google_glog-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h)
|
||||
set_target_properties(google_glog-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
|
||||
target_link_libraries(google_glog-performance-2threads_worst lib_activeobject glog justthread rt)
|
||||
|
||||
|
||||
|
||||
|
||||
target_link_libraries(google_glog-performance-2threads_worst lib_activeobject glog ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# create the the TEST executable
|
||||
add_executable(g2log-unit_test ../test_main/test_main.cpp test/test_io.cpp)
|
||||
add_library(lib_test_logger ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp ${LOG_SRC}/crashhandler.h ${LOG_SRC}/crashhandler.cpp)
|
||||
set_target_properties(lib_test_logger PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_target_properties(lib_test_logger PROPERTIES COMPILE_DEFINITIONS "YALLA=1")
|
||||
target_link_libraries(lib_test_logger lib_activeobject)
|
||||
# For unit-test only, dont 'crash' by signals, instead just throw a std::runtime_error
|
||||
set_target_properties(g2log-unit_test PROPERTIES COMPILE_DEFINITIONS "YALLA=1")
|
||||
target_link_libraries(g2log-unit_test lib_activeobject lib_test_logger gtest_160_lib justthread rt)
|
||||
ENDIF(UNIX)
|
||||
|
||||
|
@ -34,8 +34,8 @@ typedef std::function<void()> Callback;
|
||||
|
||||
class Active {
|
||||
private:
|
||||
Active(const Active&) = delete;
|
||||
Active& operator=(const Active&) = delete;
|
||||
Active(const Active&); // c++11 feature not yet in vs2010 = delete;
|
||||
Active& operator=(const Active&); // c++11 feature not yet in vs2010 = delete;
|
||||
|
||||
Active(); // Construction ONLY through factory createActive();
|
||||
|
||||
|
33
g2log/src/crashhandler.h
Normal file
33
g2log/src/crashhandler.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef CRASH_HANDLER_H_
|
||||
#define CRASH_HANDLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <csignal>
|
||||
|
||||
namespace g2
|
||||
{
|
||||
|
||||
// PRIVATE-INTERNAL API
|
||||
namespace internal
|
||||
{
|
||||
/** \return signal_name. Ref: signum.h and \ref installSignalHandler */
|
||||
std::string signalName(int signal_number);
|
||||
|
||||
/** Re-"throw" a fatal signal, previously caught. This will exit the application
|
||||
* This is an internal only function. Do not use it elsewhere. It is triggered
|
||||
* from g2log, LogWorker after flushing messages to file */
|
||||
void exitWithDefaultSignalHandler(int signal_number);
|
||||
} // end g2::interal
|
||||
|
||||
|
||||
// PUBLIC API:
|
||||
/** Install signal handler that catches FATAL C-runtime or OS signals
|
||||
SIGABRT ABORT (ANSI), abnormal termination
|
||||
SIGFPE Floating point exception (ANSI): http://en.wikipedia.org/wiki/SIGFPE
|
||||
SIGILL ILlegal instruction (ANSI)
|
||||
SIGSEGV Segmentation violation i.e. illegal memory reference
|
||||
SIGTERM TERMINATION (ANSI) */
|
||||
void installSignalHandler();
|
||||
}
|
||||
|
||||
#endif // CRASH_HANDLER_H_
|
125
g2log/src/crashhandler_unix.cpp
Normal file
125
g2log/src/crashhandler_unix.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "crashhandler.h"
|
||||
#include "g2log.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
// or? gcc on windows I guess,.
|
||||
#error "crashhandler_unix.cpp used but it's a windows system"
|
||||
#endif
|
||||
|
||||
|
||||
#include <unistd.h> // getpid,
|
||||
#include <execinfo.h>
|
||||
#include <ucontext.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace
|
||||
{
|
||||
// simple dump of stack,. then exit through g2log background worker
|
||||
// Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes
|
||||
void crashHandler(int signal_number, siginfo_t *info, void *unused_context)
|
||||
{
|
||||
const size_t max_dump_size = 100;
|
||||
void* dump[max_dump_size];
|
||||
size_t size = backtrace(dump, max_dump_size);
|
||||
// overwrite sigaction with caller's address
|
||||
char** messages = backtrace_symbols(dump, size);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Received fatal signal: " << g2::internal::signalName(signal_number);
|
||||
oss << "(" << signal_number << ")" << std::endl;
|
||||
oss << "\tPID: " << getpid() << std::endl;
|
||||
// dump stack: skip first frame, since that is here
|
||||
for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
|
||||
{
|
||||
oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
|
||||
}
|
||||
free(messages);
|
||||
{ // Local scope, trigger send
|
||||
using namespace g2::internal;
|
||||
std::ostringstream fatal_stream;
|
||||
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
|
||||
fatal_stream << oss.str() << std::endl;
|
||||
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
|
||||
FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
FatalTrigger trigger(fatal_message); std::ostringstream oss;
|
||||
std::cerr << fatal_message.message_ << std::endl << std::flush;
|
||||
} // message sent to LogWorker
|
||||
// wait to die -- will be inside the FatalTrigger
|
||||
}
|
||||
} // 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
|
||||
{
|
||||
std::string signalName(int signal_number)
|
||||
{
|
||||
switch(signal_number)
|
||||
{
|
||||
case SIGABRT: return "SIGABRT";break;
|
||||
case SIGFPE: return "SIGFPE"; break;
|
||||
case SIGSEGV: return "SIGSEGV"; break;
|
||||
case SIGILL: return "SIGILL"; break;
|
||||
case SIGTERM: return "SIGTERM"; break;
|
||||
default:
|
||||
std::ostringstream oss;
|
||||
oss << "UNKNOWN SIGNAL(" << signal_number << ")";
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Triggered by g2log::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(int signal_number)
|
||||
{
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action)); //
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_handler = SIG_DFL; // take default action for the signal
|
||||
sigaction(signal_number, &action, NULL);
|
||||
kill(getpid(), signal_number);
|
||||
}
|
||||
} // end g2::internal
|
||||
|
||||
|
||||
void installSignalHandler()
|
||||
{
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = &crashHandler; // callback to crashHandler for fatal signals
|
||||
// sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
|
||||
// do it verbose style - install all signal actions
|
||||
if(sigaction(SIGABRT, &action, NULL) < 0)
|
||||
perror("sigaction - SIGABRT");
|
||||
if(sigaction(SIGFPE, &action, NULL) < 0)
|
||||
perror("sigaction - SIGFPE");
|
||||
if(sigaction(SIGILL, &action, NULL) < 0)
|
||||
perror("sigaction - SIGILL");
|
||||
if(sigaction(SIGSEGV, &action, NULL) < 0)
|
||||
perror("sigaction - SIGSEGV");
|
||||
if(sigaction(SIGTERM, &action, NULL) < 0)
|
||||
perror("sigaction - SIGTERM");
|
||||
}
|
||||
} // end namespace g2
|
86
g2log/src/crashhandler_win.cpp
Normal file
86
g2log/src/crashhandler_win.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "crashhandler.h"
|
||||
#include "g2log.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
#error "crashhandler_win.cpp used but not on a windows system"
|
||||
#endif
|
||||
|
||||
#include <process.h> // getpid
|
||||
#define getpid _getpid
|
||||
|
||||
namespace
|
||||
{
|
||||
void crashHandler(int signal_number)
|
||||
{
|
||||
using namespace g2::internal;
|
||||
std::ostringstream fatal_stream;
|
||||
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
|
||||
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
|
||||
FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
FatalTrigger trigger(fatal_message); std::ostringstream oss;
|
||||
std::cerr << fatal_message.message_ << std::endl << std::flush;
|
||||
} // scope exit - message sent to LogWorker, wait to die...
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace g2
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
std::string signalName(int signal_number)
|
||||
{
|
||||
switch(signal_number)
|
||||
{
|
||||
case SIGABRT: return "SIGABRT";break;
|
||||
case SIGFPE: return "SIGFPE"; break;
|
||||
case SIGSEGV: return "SIGSEGV"; break;
|
||||
case SIGILL: return "SIGILL"; break;
|
||||
case SIGTERM: return "SIGTERM"; break;
|
||||
default:
|
||||
std::ostringstream oss;
|
||||
oss << "UNKNOWN SIGNAL(" << signal_number << ")";
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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(int signal_number)
|
||||
{
|
||||
// Restore our signalhandling to default
|
||||
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");
|
||||
|
||||
raise(signal_number);
|
||||
}
|
||||
} // end g2::internal
|
||||
|
||||
|
||||
void installSignalHandler()
|
||||
{
|
||||
if(SIG_ERR == signal (SIGABRT, crashHandler))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGFPE, crashHandler))
|
||||
perror("signal - SIGFPE");
|
||||
if(SIG_ERR == signal (SIGSEGV, crashHandler))
|
||||
perror("signal - SIGSEGV");
|
||||
if(SIG_ERR == signal (SIGILL, crashHandler))
|
||||
perror("signal - SIGILL");
|
||||
if(SIG_ERR == signal (SIGTERM, crashHandler))
|
||||
perror("signal - SIGTERM");
|
||||
}
|
||||
} // end namespace g2
|
@ -20,7 +20,10 @@
|
||||
#include <cstdio> // vsnprintf
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
#include "logworker.h"
|
||||
#include "crashhandler.h"
|
||||
#include <signal.h>
|
||||
|
||||
namespace g2
|
||||
{
|
||||
@ -52,10 +55,17 @@ std::string splitFileName(const std::string& str)
|
||||
|
||||
void initializeLogging(LogWorker *bgworker)
|
||||
{
|
||||
static bool once_only_signalhandler = false;
|
||||
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
|
||||
CHECK(!internal::isLoggingInitialized());
|
||||
CHECK(bgworker != nullptr);
|
||||
internal::g_logger_instance = bgworker;
|
||||
|
||||
if(false == once_only_signalhandler)
|
||||
{
|
||||
installSignalHandler();
|
||||
once_only_signalhandler = true;
|
||||
}
|
||||
}
|
||||
|
||||
LogWorker* shutDownLogging()
|
||||
@ -83,11 +93,11 @@ LogContractMessage::~LogContractMessage()
|
||||
std::ostringstream oss;
|
||||
if(0 == expression_.compare(k_fatal_log_expression))
|
||||
{
|
||||
oss << "[ *******\tRUNTIME EXCEPTION caused by LOG(FATAL):\t";
|
||||
oss << "\n[ *******\tEXIT trigger caused by LOG(FATAL): \n\t";
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << "\nRUNTIME EXCEPTION caused by broken Contract: [" << expression_ << "]\t";
|
||||
oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t";
|
||||
}
|
||||
log_entry_ = oss.str();
|
||||
}
|
||||
@ -103,11 +113,12 @@ LogMessage::LogMessage(const std::string &file, const int line, const std::strin
|
||||
|
||||
LogMessage::~LogMessage()
|
||||
{
|
||||
using namespace internal;
|
||||
std::ostringstream oss;
|
||||
const bool fatal = (0 == level_.compare("FATAL"));
|
||||
oss << level_ << " [" << internal::splitFileName(file_);
|
||||
oss << level_ << " [" << splitFileName(file_);
|
||||
if(fatal)
|
||||
oss << " F: " << function_ ;
|
||||
oss << " at: " << function_ ;
|
||||
oss << " L: " << line_ << "]\t";
|
||||
|
||||
const std::string str(stream_.str());
|
||||
@ -117,24 +128,49 @@ LogMessage::~LogMessage()
|
||||
}
|
||||
log_entry_ += oss.str();
|
||||
|
||||
if(!internal::isLoggingInitialized() )
|
||||
if(!isLoggingInitialized() )
|
||||
{
|
||||
std::cerr << "Did you forget to call g2::InitializeLogging(LogWorker*) in your main.cpp?" << std::endl;
|
||||
std::cerr << log_entry_ << std::endl << std::flush;
|
||||
throw std::runtime_error("Logger not initialized with g2::InitializeLogging(LogWorker*) for msg:\n" + log_entry_);
|
||||
}
|
||||
|
||||
internal::g_logger_instance->save(log_entry_); // message saved
|
||||
if(fatal)
|
||||
|
||||
if(fatal) // os_fatal is handled by crashhandlers
|
||||
{
|
||||
{ // local scope - to trigger FatalMessage sending
|
||||
FatalMessage::FatalType fatal_type(FatalMessage::kReasonFatal);
|
||||
FatalMessage fatal_message(log_entry_, fatal_type, SIGABRT);
|
||||
FatalTrigger trigger(fatal_message);
|
||||
std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush;
|
||||
throw std::runtime_error(log_entry_);
|
||||
} // will send to worker
|
||||
}
|
||||
|
||||
|
||||
internal::g_logger_instance->save(log_entry_); // message saved
|
||||
}
|
||||
|
||||
|
||||
// represents the actual fatal message
|
||||
FatalMessage::FatalMessage(std::string message, FatalType type, int signal_id)
|
||||
: message_(message)
|
||||
, type_(type)
|
||||
, signal_id_(signal_id){}
|
||||
|
||||
// used to RAII trigger fatal message sending to LogWorker
|
||||
FatalTrigger::FatalTrigger(const FatalMessage &message)
|
||||
: message_(message){}
|
||||
|
||||
// at destruction, flushes fatal message to LogWorker
|
||||
FatalTrigger::~FatalTrigger()
|
||||
{
|
||||
internal::g_logger_instance->fatal(message_);
|
||||
#if !defined(YALLA) // don't sleep if unit-testing
|
||||
// wait to die
|
||||
while(true){std::this_thread::sleep_for(std::chrono::seconds(1));}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LogMessage::messageSave(const char *printf_like_message, ...)
|
||||
{
|
||||
char finished_message[constants::kMaxMessageSize];
|
||||
|
@ -23,8 +23,14 @@
|
||||
|
||||
class LogWorker;
|
||||
|
||||
#if !(defined(__PRETTY_FUNCTION__))
|
||||
#define __PRETTY_FUNCTION__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
|
||||
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
||||
const int DEBUG = 0, INFO = 1, WARNING = 2, FATAL = 3;
|
||||
static const std::string k_os_signal_fatal_text = "OS_SIGNAL_FATAL";
|
||||
static const std::string k_fatal_log_expression = ""; // using LogContractMessage but no boolean expression
|
||||
|
||||
// GCC Predefined macros: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
|
||||
@ -155,6 +161,28 @@ typedef std::chrono::duration<long,std::ratio<1, 1000> > millisecond;
|
||||
typedef std::chrono::duration<long long,std::ratio<1, 1000000> > microsecond;
|
||||
typedef const std::string& LogEntry;
|
||||
|
||||
/** Trigger for flushing the message queue and exiting the applicaition
|
||||
A thread that causes a FatalMessage will sleep forever until the
|
||||
application has exited (after message flush) */
|
||||
struct FatalMessage
|
||||
{
|
||||
enum FatalType {kReasonFatal, kReasonOS_FATAL_SIGNAL};
|
||||
FatalMessage(std::string message, FatalType type, int signal_id);
|
||||
|
||||
std::string message_;
|
||||
FatalType type_;
|
||||
int signal_id_;
|
||||
};
|
||||
// Will trigger a FatalMessage sending
|
||||
struct FatalTrigger
|
||||
{
|
||||
FatalTrigger(const FatalMessage& message);
|
||||
~FatalTrigger();
|
||||
FatalMessage message_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Log message for 'printf-like' or stream logging, it's a temporary message constructions
|
||||
class LogMessage
|
||||
{
|
||||
@ -168,6 +196,10 @@ class LogMessage
|
||||
// IMPORTANT: You muse enable the compiler flag '-Wall' for this to work!
|
||||
// ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
|
||||
//
|
||||
//If the compiler does not support attributes, disable them
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
// Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first
|
||||
// compiler given argument 'this' this must be supplied as well, hence '2,3'
|
||||
// ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod
|
||||
|
@ -6,6 +6,7 @@
|
||||
* ********************************************* */
|
||||
|
||||
#include "logworker.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
@ -16,8 +17,13 @@
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
|
||||
#if defined(YALLA)
|
||||
#include <stdexcept> // exceptions
|
||||
#endif
|
||||
|
||||
#include "active.h"
|
||||
#include "g2log.h"
|
||||
#include "crashhandler.h"
|
||||
|
||||
using namespace g2::internal;
|
||||
namespace
|
||||
@ -57,6 +63,7 @@ struct LogWorkerImpl
|
||||
~LogWorkerImpl();
|
||||
|
||||
void backgroundFileWrite(g2::internal::LogEntry message);
|
||||
void backgroundExitFatal(g2::internal::FatalMessage fatal_message);
|
||||
|
||||
std::string log_file_with_path_;
|
||||
std::unique_ptr<kjellkod::Active> bg_;
|
||||
@ -64,9 +71,8 @@ struct LogWorkerImpl
|
||||
g2::internal::time_point start_time_;
|
||||
|
||||
private:
|
||||
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; // no assignment, no copy
|
||||
LogWorkerImpl(const LogWorkerImpl& other) = delete;
|
||||
|
||||
LogWorkerImpl& operator=(const LogWorkerImpl&); // c++11 feature not yet in vs2010 = delete;
|
||||
LogWorkerImpl(const LogWorkerImpl& other); // c++11 feature not yet in vs2010 = delete;
|
||||
};
|
||||
|
||||
|
||||
@ -127,9 +133,24 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message)
|
||||
out << "\n" << t.year << "/" << setw(2) << t.month << "/" << setw(2) << t.day;
|
||||
out << " " << setw(2) << t.hour << ":"<< setw(2) << t.minute <<":"<< setw(2) << t.second;
|
||||
out << "." << chrono::duration_cast<microsecond>(timesnapshot - start_time_).count(); //microseconds
|
||||
out << "\t" << message;
|
||||
out << "\t" << message << std::flush;
|
||||
}
|
||||
|
||||
void LogWorkerImpl::backgroundExitFatal(FatalMessage fatal_message)
|
||||
{
|
||||
backgroundFileWrite(fatal_message.message_);
|
||||
|
||||
#if defined(YALLA)
|
||||
// If running unit test - we simplify matters by not sending the signal, but
|
||||
// by just throwing an exception
|
||||
throw std::runtime_error(fatal_message.message_);
|
||||
return;
|
||||
#endif
|
||||
|
||||
out.close();
|
||||
exitWithDefaultSignalHandler(fatal_message.signal_id_);
|
||||
perror("g2log exited after receiving FATAL trigger. Flush message status: "); // should never reach this point
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -144,7 +165,7 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message)
|
||||
LogWorker::~LogWorker()
|
||||
{
|
||||
pimpl_.reset();
|
||||
//std::cout << "\nLogWorker finished with log: " << log_file_with_path_ << std::endl << std::flush;
|
||||
std::cout << "\nExiting, log location: " << log_file_with_path_ << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void LogWorker::save(g2::internal::LogEntry msg)
|
||||
@ -152,6 +173,10 @@ void LogWorkerImpl::backgroundFileWrite(LogEntry message)
|
||||
pimpl_->bg_->send(std::tr1::bind(&LogWorkerImpl::backgroundFileWrite, pimpl_.get(), msg));
|
||||
}
|
||||
|
||||
void LogWorker::fatal(g2::internal::FatalMessage fatal_message)
|
||||
{
|
||||
pimpl_->bg_->send(std::tr1::bind(&LogWorkerImpl::backgroundExitFatal, pimpl_.get(), fatal_message));
|
||||
}
|
||||
|
||||
std::string LogWorker::logFileName() const
|
||||
{
|
||||
|
@ -22,7 +22,12 @@ public:
|
||||
virtual ~LogWorker();
|
||||
|
||||
/// pushes in background thread (asynchronously) input messages to log file
|
||||
void save(g2::internal::LogEntry);
|
||||
void save(g2::internal::LogEntry entry);
|
||||
|
||||
/// Will push a fatal message on the queue, this is the last message to be processed
|
||||
/// this way it's ensured that all existing entries were flushed before 'fatal'
|
||||
/// Will abort the application!
|
||||
void fatal(g2::internal::FatalMessage fatal_message);
|
||||
|
||||
/// basically only needed for unit-testing or specific log management post logging
|
||||
std::string logFileName() const;
|
||||
@ -31,8 +36,8 @@ private:
|
||||
std::unique_ptr<LogWorkerImpl> pimpl_;
|
||||
const std::string log_file_with_path_;
|
||||
|
||||
LogWorker(const LogWorker&) = delete; // no assignment, no copy
|
||||
LogWorker& operator=(const LogWorker&) = delete;
|
||||
LogWorker(const LogWorker&); // c++11 feature not yet in vs2010 = delete;
|
||||
LogWorker& operator=(const LogWorker&); // c++11 feature not yet in vs2010 = delete;
|
||||
};
|
||||
|
||||
|
||||
|
@ -10,13 +10,22 @@
|
||||
#include "logworker.h"
|
||||
#include <iomanip>
|
||||
|
||||
namespace
|
||||
{
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
const std::string path_to_log_file = "./";
|
||||
#else
|
||||
const std::string path_to_log_file = "/tmp/";
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
double pi_d = 3.1415926535897932384626433832795;
|
||||
float pi_f = 3.1415926535897932384626433832795f;
|
||||
|
||||
|
||||
LogWorker logger(argv[0], "/tmp/");
|
||||
LogWorker logger(argv[0], path_to_log_file);
|
||||
g2::initializeLogging(&logger);
|
||||
|
||||
std::cout << "****** A NUMBER of 'runtime exceptions' will be printed on this screen" << std::endl;
|
||||
@ -90,7 +99,7 @@ int main(int argc, char** argv)
|
||||
CHECK(1<2) << "SHOULD NOT SEE THIS MESSAGE";
|
||||
CHECK(1>2) << "Test to see if contract works: onetwothree: " << 123 << ". This should be inside an exception";
|
||||
}
|
||||
catch(std::exception& exc)
|
||||
catch(...)
|
||||
{
|
||||
std::cout << "\n***** All good, the 'exception' was part of the example\n\n\n" << std::endl;
|
||||
return 0;
|
||||
|
@ -29,8 +29,8 @@ class shared_queue
|
||||
mutable std::mutex m_;
|
||||
std::condition_variable data_cond_;
|
||||
|
||||
shared_queue& operator=(const shared_queue&) = delete;
|
||||
shared_queue(const shared_queue& other) = delete;
|
||||
shared_queue& operator=(const shared_queue&); // c++11 feature not yet in vs2010 = delete;
|
||||
shared_queue(const shared_queue& other); // c++11 feature not yet in vs2010 = delete;
|
||||
|
||||
public:
|
||||
shared_queue(){}
|
||||
|
@ -97,7 +97,7 @@ int main(int argc, char** argv)
|
||||
|
||||
oss.str("");
|
||||
oss << "Number of values rounted to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl;
|
||||
oss << "Format: bucket_of_ms, number_of_values_in_bucket";
|
||||
oss << "Format: bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
|
||||
std::cout << oss.str() << std::endl;
|
||||
|
||||
for(auto iter = value_amounts.begin(); iter != value_amounts.end(); ++iter)
|
||||
|
@ -40,8 +40,6 @@ int main(int argc, char** argv)
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
doLogWrites(title);
|
||||
auto application_end_time = std::chrono::steady_clock::now();
|
||||
|
@ -66,6 +66,7 @@ bool writeTextToFile(const std::string& filename, const std::string& msg, const
|
||||
std::cerr << ss_error.str().c_str() << std::endl << std::flush;
|
||||
return false;
|
||||
}
|
||||
|
||||
out << msg;
|
||||
return true;
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time
|
||||
|
||||
bool verifyContent(const std::string &total_text,std::string msg_to_find)
|
||||
{
|
||||
std::string content(total_text);
|
||||
@ -173,6 +175,7 @@ TEST(LogTest, LOGF__FATAL)
|
||||
try
|
||||
{
|
||||
LOGF(FATAL, "This message should throw %d",0);
|
||||
sleep(k_wait_time);
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
@ -200,6 +203,7 @@ TEST(LogTest, LOG_FATAL)
|
||||
try
|
||||
{
|
||||
LOG(FATAL) << "This message should throw";
|
||||
sleep(k_wait_time);
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
@ -227,6 +231,7 @@ TEST(LogTest, LOGF_IF__FATAL)
|
||||
try
|
||||
{
|
||||
LOGF_IF(FATAL, (2<3), "This message%sshould throw"," ");
|
||||
sleep(k_wait_time);
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
@ -255,6 +260,7 @@ TEST(LogTest, LOG_IF__FATAL)
|
||||
{
|
||||
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
|
||||
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw";
|
||||
sleep(k_wait_time);
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
@ -326,6 +332,7 @@ TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg)
|
||||
try
|
||||
{
|
||||
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str());
|
||||
sleep(k_wait_time);
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user