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:
Kjell Hedstrom 2011-11-10 22:23:33 +01:00
parent 9dbc9023fc
commit b7240a610b
16 changed files with 515 additions and 124 deletions

Binary file not shown.

View File

@ -1,16 +1,25 @@
# CMakeLists.txt cmake configuration for g2log test # CMakeLists.txt cmake configuration for g2log test
# g2log is a KjellKod Logger
# 2011 @author Kjell Hedström, hedstrom@kjellkod.cc */ # 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 # 1. Install gtest
# cmake # cmake
# make # make
# make install (possibly as root) # make install (possibly as root)
# #
#
#
# 2. update path to libraries # 2. update path to libraries
# sudo /sbin/ldconfig -v | grep gtest # sudo /sbin/ldconfig -v | grep gtest
# #
@ -19,9 +28,7 @@
# libgtest.so.0 -> libgtest.so.0.0.0 # libgtest.so.0 -> libgtest.so.0.0.0
# libgtest_main.so.0 -> libgtest_main.so.0.0.0 # libgtest_main.so.0 -> libgtest_main.so.0.0.0
# #
# # 3. LINUX:To try this out from folder g2log:
#
# 3. To try this out from folder g2log:
# mkdir build # mkdir build
# cd build # cd build
# cmake .. # create makefiles in g2log/build directory # cmake .. # create makefiles in g2log/build directory
@ -31,12 +38,24 @@
# ============================================================================ # ============================================================================
cmake_minimum_required (VERSION 2.6) cmake_minimum_required (VERSION 2.6)
project (kjellkod_logger) project (g2log_by_kjellkod)
set(LOG_SRC ${kjellkod_logger_SOURCE_DIR}/src) set(LOG_SRC ${g2log_by_kjellkod_SOURCE_DIR}/src)
MESSAGE(" LOG_SRC = : ${LOG_SRC}") MESSAGE(" LOG_SRC = : ${LOG_SRC}")
include_directories(${LOG_SRC})
SET(ACTIVE_CPP0xx_DIR "Release")
IF(UNIX) 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(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 # SETUP for GTEST
set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped) set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped)
@ -44,80 +63,95 @@ IF(UNIX)
include_directories(${GTEST_INCLUDE_DIRECTORIES}) include_directories(${GTEST_INCLUDE_DIRECTORIES})
add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc) add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc)
enable_testing(true) 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 # add a ActiveObject library
set(ACTIVE_DIR ${LOG_SRC}) add_library(lib_activeobject ${LOG_SRC}/active.cpp ${LOG_SRC}/active.h ${LOG_SRC}/shared_queue.h)
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)
set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX)
# add a g2log library
include_directories(src) include_directories(src)
include_directories(${LOG_SRC}) include_directories(${LOG_SRC})
#MESSAGE(" LOG_SRC = : ${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 ) MESSAGE(" g2logger files: [${G2_LOG_FILES}]")
set_target_properties(lib_logger PROPERTIES LINKER_LANGUAGE CXX) add_library(lib_g2logger ${G2_LOG_FILES})
target_link_libraries(lib_logger lib_activeobject) set_target_properties(lib_g2logger PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(lib_g2logger lib_activeobject)
# create the the example EXECUTABLE # create the the example EXECUTABLE
add_executable(g2log-example src/main.cpp) add_executable(g2log-example src/main.cpp)
# link executable with the src library target_link_libraries(g2log-example lib_activeobject lib_g2logger ${PLATFORM_LINK_LIBRIES})
target_link_libraries(g2log-example lib_activeobject lib_logger justthread rt)
# Below are g2log unit testTEST # Below are g2log unit testTEST
# and PERFORMANCE comparisons between g2log and google's glog # 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 ----- # ---- Below g2log Performance -----
# create the the g2log MEAN_PERFORMANCE executable # create the the g2log MEAN_PERFORMANCE executable
add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h) add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h)
set_target_properties(g2log-performance-mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1") 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 # create the the g2log TWO_THREADS_MEAN_PERFORMANCE executable
add_executable(g2log-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) 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") 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 # create the the g2log TWO_THREADS_WORST_CASE_PERFORMANCE executable
add_executable(g2log-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) 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") 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 ----- # ---- Below GOOGLE glog Performance -----
# create the the GOOGLE MEAN_PERFORMANCE executable # create the the GOOGLE MEAN_PERFORMANCE executable
# Generate the DEFINE (for glog) needed to differentiate between the glog and the g2log test # 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) 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") 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 # create the the GOOGLE MEAN_PERFORMANCE executable
add_executable(google_glog-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h) 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") 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 # create the the GOOGLE MEAN_PERFORMANCE executable
add_executable(google_glog-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h) 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") 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) ENDIF(UNIX)

View File

@ -34,8 +34,8 @@ typedef std::function<void()> Callback;
class Active { class Active {
private: private:
Active(const Active&) = delete; Active(const Active&); // c++11 feature not yet in vs2010 = delete;
Active& operator=(const Active&) = delete; Active& operator=(const Active&); // c++11 feature not yet in vs2010 = delete;
Active(); // Construction ONLY through factory createActive(); Active(); // Construction ONLY through factory createActive();

33
g2log/src/crashhandler.h Normal file
View 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_

View 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

View 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

View File

@ -20,7 +20,10 @@
#include <cstdio> // vsnprintf #include <cstdio> // vsnprintf
#include <cassert> #include <cassert>
#include <mutex> #include <mutex>
#include "logworker.h" #include "logworker.h"
#include "crashhandler.h"
#include <signal.h>
namespace g2 namespace g2
{ {
@ -52,10 +55,17 @@ std::string splitFileName(const std::string& str)
void initializeLogging(LogWorker *bgworker) void initializeLogging(LogWorker *bgworker)
{ {
static bool once_only_signalhandler = false;
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex); std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
CHECK(!internal::isLoggingInitialized()); CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr); CHECK(bgworker != nullptr);
internal::g_logger_instance = bgworker; internal::g_logger_instance = bgworker;
if(false == once_only_signalhandler)
{
installSignalHandler();
once_only_signalhandler = true;
}
} }
LogWorker* shutDownLogging() LogWorker* shutDownLogging()
@ -83,11 +93,11 @@ LogContractMessage::~LogContractMessage()
std::ostringstream oss; std::ostringstream oss;
if(0 == expression_.compare(k_fatal_log_expression)) 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 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(); log_entry_ = oss.str();
} }
@ -103,11 +113,12 @@ LogMessage::LogMessage(const std::string &file, const int line, const std::strin
LogMessage::~LogMessage() LogMessage::~LogMessage()
{ {
using namespace internal;
std::ostringstream oss; std::ostringstream oss;
const bool fatal = (0 == level_.compare("FATAL")); const bool fatal = (0 == level_.compare("FATAL"));
oss << level_ << " [" << internal::splitFileName(file_); oss << level_ << " [" << splitFileName(file_);
if(fatal) if(fatal)
oss << " F: " << function_ ; oss << " at: " << function_ ;
oss << " L: " << line_ << "]\t"; oss << " L: " << line_ << "]\t";
const std::string str(stream_.str()); const std::string str(stream_.str());
@ -117,24 +128,49 @@ LogMessage::~LogMessage()
} }
log_entry_ += oss.str(); 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 << "Did you forget to call g2::InitializeLogging(LogWorker*) in your main.cpp?" << std::endl;
std::cerr << log_entry_ << std::endl << std::flush; std::cerr << log_entry_ << std::endl << std::flush;
throw std::runtime_error("Logger not initialized with g2::InitializeLogging(LogWorker*) for msg:\n" + log_entry_); 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; 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, ...) void LogMessage::messageSave(const char *printf_like_message, ...)
{ {
char finished_message[constants::kMaxMessageSize]; char finished_message[constants::kMaxMessageSize];

View File

@ -23,8 +23,14 @@
class LogWorker; 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 // 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; 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 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 // 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 std::chrono::duration<long long,std::ratio<1, 1000000> > microsecond;
typedef const std::string& LogEntry; 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 // Log message for 'printf-like' or stream logging, it's a temporary message constructions
class LogMessage class LogMessage
{ {
@ -168,6 +196,10 @@ class LogMessage
// IMPORTANT: You muse enable the compiler flag '-Wall' for this to work! // IMPORTANT: You muse enable the compiler flag '-Wall' for this to work!
// ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html // 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 // 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' // compiler given argument 'this' this must be supplied as well, hence '2,3'
// ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod // ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod

View File

@ -6,6 +6,7 @@
* ********************************************* */ * ********************************************* */
#include "logworker.h" #include "logworker.h"
#include <iostream> #include <iostream>
#include <functional> #include <functional>
#include <string> #include <string>
@ -16,8 +17,13 @@
#include <iomanip> #include <iomanip>
#include <ctime> #include <ctime>
#if defined(YALLA)
#include <stdexcept> // exceptions
#endif
#include "active.h" #include "active.h"
#include "g2log.h" #include "g2log.h"
#include "crashhandler.h"
using namespace g2::internal; using namespace g2::internal;
namespace namespace
@ -57,6 +63,7 @@ struct LogWorkerImpl
~LogWorkerImpl(); ~LogWorkerImpl();
void backgroundFileWrite(g2::internal::LogEntry message); void backgroundFileWrite(g2::internal::LogEntry message);
void backgroundExitFatal(g2::internal::FatalMessage fatal_message);
std::string log_file_with_path_; std::string log_file_with_path_;
std::unique_ptr<kjellkod::Active> bg_; std::unique_ptr<kjellkod::Active> bg_;
@ -64,9 +71,8 @@ struct LogWorkerImpl
g2::internal::time_point start_time_; g2::internal::time_point start_time_;
private: private:
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; // no assignment, no copy LogWorkerImpl& operator=(const LogWorkerImpl&); // c++11 feature not yet in vs2010 = delete;
LogWorkerImpl(const LogWorkerImpl& other) = 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 << "\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 << " " << setw(2) << t.hour << ":"<< setw(2) << t.minute <<":"<< setw(2) << t.second;
out << "." << chrono::duration_cast<microsecond>(timesnapshot - start_time_).count(); //microseconds 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() LogWorker::~LogWorker()
{ {
pimpl_.reset(); 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) 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)); 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 std::string LogWorker::logFileName() const
{ {

View File

@ -22,7 +22,12 @@ public:
virtual ~LogWorker(); virtual ~LogWorker();
/// pushes in background thread (asynchronously) input messages to log file /// 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 /// basically only needed for unit-testing or specific log management post logging
std::string logFileName() const; std::string logFileName() const;
@ -31,8 +36,8 @@ private:
std::unique_ptr<LogWorkerImpl> pimpl_; std::unique_ptr<LogWorkerImpl> pimpl_;
const std::string log_file_with_path_; const std::string log_file_with_path_;
LogWorker(const LogWorker&) = delete; // no assignment, no copy LogWorker(const LogWorker&); // c++11 feature not yet in vs2010 = delete;
LogWorker& operator=(const LogWorker&) = delete; LogWorker& operator=(const LogWorker&); // c++11 feature not yet in vs2010 = delete;
}; };

View File

@ -10,13 +10,22 @@
#include "logworker.h" #include "logworker.h"
#include <iomanip> #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) int main(int argc, char** argv)
{ {
double pi_d = 3.1415926535897932384626433832795; double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f; float pi_f = 3.1415926535897932384626433832795f;
LogWorker logger(argv[0], "/tmp/"); LogWorker logger(argv[0], path_to_log_file);
g2::initializeLogging(&logger); g2::initializeLogging(&logger);
std::cout << "****** A NUMBER of 'runtime exceptions' will be printed on this screen" << std::endl; 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) << "SHOULD NOT SEE THIS MESSAGE";
CHECK(1>2) << "Test to see if contract works: onetwothree: " << 123 << ". This should be inside an exception"; 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; std::cout << "\n***** All good, the 'exception' was part of the example\n\n\n" << std::endl;
return 0; return 0;

View File

@ -29,8 +29,8 @@ class shared_queue
mutable std::mutex m_; mutable std::mutex m_;
std::condition_variable data_cond_; std::condition_variable data_cond_;
shared_queue& operator=(const shared_queue&) = delete; shared_queue& operator=(const shared_queue&); // c++11 feature not yet in vs2010 = delete;
shared_queue(const shared_queue& other) = delete; shared_queue(const shared_queue& other); // c++11 feature not yet in vs2010 = delete;
public: public:
shared_queue(){} shared_queue(){}

View File

@ -97,7 +97,7 @@ int main(int argc, char** argv)
oss.str(""); 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 << "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; std::cout << oss.str() << std::endl;
for(auto iter = value_amounts.begin(); iter != value_amounts.end(); ++iter) for(auto iter = value_amounts.begin(); iter != value_amounts.end(); ++iter)

View File

@ -40,8 +40,6 @@ int main(int argc, char** argv)
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
#endif #endif
auto start_time = std::chrono::steady_clock::now(); auto start_time = std::chrono::steady_clock::now();
doLogWrites(title); doLogWrites(title);
auto application_end_time = std::chrono::steady_clock::now(); auto application_end_time = std::chrono::steady_clock::now();

View File

@ -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; std::cerr << ss_error.str().c_str() << std::endl << std::flush;
return false; return false;
} }
out << msg; out << msg;
return true; return true;
} }

View File

@ -16,6 +16,8 @@
namespace 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) bool verifyContent(const std::string &total_text,std::string msg_to_find)
{ {
std::string content(total_text); std::string content(total_text);
@ -173,6 +175,7 @@ TEST(LogTest, LOGF__FATAL)
try try
{ {
LOGF(FATAL, "This message should throw %d",0); LOGF(FATAL, "This message should throw %d",0);
sleep(k_wait_time);
} }
catch (std::exception const &e) catch (std::exception const &e)
{ {
@ -200,6 +203,7 @@ TEST(LogTest, LOG_FATAL)
try try
{ {
LOG(FATAL) << "This message should throw"; LOG(FATAL) << "This message should throw";
sleep(k_wait_time);
} }
catch (std::exception const &e) catch (std::exception const &e)
{ {
@ -227,6 +231,7 @@ TEST(LogTest, LOGF_IF__FATAL)
try try
{ {
LOGF_IF(FATAL, (2<3), "This message%sshould throw"," "); LOGF_IF(FATAL, (2<3), "This message%sshould throw"," ");
sleep(k_wait_time);
} }
catch (std::exception const &e) 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(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"; LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw";
sleep(k_wait_time);
} }
catch (std::exception const &e) catch (std::exception const &e)
{ {
@ -326,6 +332,7 @@ TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg)
try try
{ {
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str()); CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str());
sleep(k_wait_time);
} }
catch (std::exception const &e) catch (std::exception const &e)
{ {