mirror of
https://github.com/KjellKod/g3log.git
synced 2025-01-19 00:46:03 +01:00
Thanks to Rajesh to push for g3log support for "runtime loading of dynamic libraries" (that will use g3log, initiated in main)
Thanks to Dmitry (d-led) for this working proof-of-concept of this at: https://github.com/d-led/g2log-dll --HG-- rename : g2log/src/g2LogMessageBuilder.cpp => g2log/src/g2logmessagecapture.cpp rename : g2log/src/g2LogMessageBuilder.hpp => g2log/src/g2logmessagecapture.hpp
This commit is contained in:
parent
0bfd6cb97b
commit
f5438d8232
@ -135,9 +135,10 @@ ENDIF()
|
||||
# Create the g2log library
|
||||
include_directories(${LOG_SRC})
|
||||
MESSAGE(" g3logger files: [${SRC_FILES}]")
|
||||
add_library(lib_g3logger ${SRC_FILES})
|
||||
set_target_properties(lib_g3logger PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
add_library(g3logger ${SRC_FILES})
|
||||
set_target_properties(g3logger PROPERTIES LINKER_LANGUAGE CXX)
|
||||
add_library(g3logger_shared SHARED ${SRC_FILES})
|
||||
set_target_properties(g3logger_shared PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@ -197,8 +198,8 @@ ENDIF()
|
||||
include_directories (${DIR_EXAMPLE})
|
||||
add_executable(g2log-FATAL-contract ${DIR_EXAMPLE}/main_contract.cpp)
|
||||
add_executable(g2log-FATAL-sigsegv ${DIR_EXAMPLE}/main_sigsegv.cpp)
|
||||
target_link_libraries(g2log-FATAL-contract lib_g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(g2log-FATAL-sigsegv lib_g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(g2log-FATAL-contract g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(g2log-FATAL-sigsegv g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
endif (USE_SIMPLE_EXAMPLE)
|
||||
|
||||
|
||||
@ -213,13 +214,13 @@ ENDIF()
|
||||
${DIR_PERFORMANCE}/main_threaded_mean.cpp ${DIR_PERFORMANCE}/performance.h)
|
||||
# Turn on G2LOG performance flag
|
||||
set_target_properties(g2log-performance-threaded_mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
|
||||
target_link_libraries(g2log-performance-threaded_mean lib_g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(g2log-performance-threaded_mean g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
# WORST CASE PERFORMANCE TEST
|
||||
add_executable(g2log-performance-threaded_worst ${DIR_PERFORMANCE}/main_threaded_worst.cpp ${DIR_PERFORMANCE}/performance.h)
|
||||
# Turn on G2LOG performance flag
|
||||
set_target_properties(g2log-performance-threaded_worst PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
|
||||
target_link_libraries(g2log-performance-threaded_worst lib_g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(g2log-performance-threaded_worst g3logger ${PLATFORM_LINK_LIBRIES})
|
||||
endif (USE_G2LOG_PERFORMANCE)
|
||||
|
||||
|
||||
@ -277,11 +278,6 @@ ENDIF()
|
||||
SET(all_tests ${all_tests} ${DIR_UNIT_TEST}/${test}.cpp )
|
||||
IF(${test} STREQUAL "test_filechange")
|
||||
add_executable(${test} ${DIR_UNIT_TEST}/${test}.cpp ${helper})
|
||||
|
||||
#ELSEIF(${test} STREQUAL "test_sink")
|
||||
# add_executable(${test} ${DIR_UNIT_TEST}/${test}.cpp ${helper})
|
||||
# MESSAGE("*****************************************************")
|
||||
|
||||
ELSE()
|
||||
add_executable(${test} ../test_main/test_main.cpp ${DIR_UNIT_TEST}/${test}.cpp ${helper})
|
||||
ENDIF(${test} STREQUAL "test_filechange")
|
||||
@ -291,21 +287,24 @@ ENDIF()
|
||||
IF( NOT(MSVC))
|
||||
set_target_properties(${test} PROPERTIES COMPILE_FLAGS "-isystem -pthread ")
|
||||
ENDIF( NOT(MSVC))
|
||||
target_link_libraries(${test} lib_g3logger gtest_170_lib ${PLATFORM_LINK_LIBRIES})
|
||||
target_link_libraries(${test} g3logger gtest_170_lib ${PLATFORM_LINK_LIBRIES})
|
||||
ENDFOREACH(test)
|
||||
|
||||
#
|
||||
# Test for Linux, runtime loading of dynamic libraries
|
||||
#
|
||||
if(NOT WIN32)
|
||||
add_library(tester_sharedlib SHARED ${DIR_UNIT_TEST}/tester_sharedlib.h ${DIR_UNIT_TEST}/tester_sharedlib.cpp)
|
||||
target_link_libraries(tester_sharedlib g3logger_shared)
|
||||
|
||||
add_executable(test_dynamic_loaded_shared_lib ../test_main/test_main.cpp ${DIR_UNIT_TEST}/test_linux_dynamic_loaded_sharedlib.cpp)
|
||||
set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0")
|
||||
set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0")
|
||||
target_link_libraries(test_dynamic_loaded_shared_lib -ldl g3logger_shared gtest_170_lib )
|
||||
endif (NOT WIN32)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ... in progress
|
||||
# add_executable(test_ALL ${all_tests})
|
||||
# set_target_properties(g2log-unit_test_all PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0")
|
||||
# set_target_properties(g2log-unit_test_all PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0")
|
||||
# target_link_libraries(${test_ALL} lib_g3logger gtest_170_lib ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
endif (USE_G2LOG_UNIT_TEST)
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include "crashhandler.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
#include "g2LogMessageBuilder.hpp"
|
||||
#include "g2logmessagecapture.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
@ -20,7 +20,7 @@
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
|
||||
#ifdef __clang__
|
||||
#include <sys/ucontext.h>
|
||||
@ -33,21 +33,23 @@ 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 crashHandler(int signal_number, siginfo_t *info, void *unused_context) {
|
||||
using namespace g2::internal;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Received fatal signal: " << g2::internal::signalName(signal_number);
|
||||
oss << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
|
||||
oss << stackdump();
|
||||
//oss << stackdump();
|
||||
|
||||
{ // Local scope, trigger send
|
||||
std::ostringstream fatal_stream;
|
||||
fatal_stream << oss.str() << std::endl;
|
||||
fatal_stream << "\n***** SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
g2::FatalMessageBuilder trigger(fatal_stream.str(), signal_number);
|
||||
} // message sent to g2LogWorker by FatalMessageBuilder
|
||||
// wait to die -- will be inside the FatalMessageBuilder
|
||||
fatal_stream << "\n***** SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
LogCapture trigger(FATAL_SIGNAL, signal_number);
|
||||
trigger.stream() << fatal_stream.str();
|
||||
} // message sent to g2LogWorker
|
||||
// wait to die
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
@ -71,6 +73,7 @@ namespace g2 {
|
||||
// ,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 stackdump() {
|
||||
const size_t max_dump_size = 50;
|
||||
void* dump[max_dump_size];
|
||||
@ -121,14 +124,18 @@ namespace g2 {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
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 << ")";
|
||||
@ -139,10 +146,11 @@ namespace g2 {
|
||||
// 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(int signal_number) {
|
||||
std::cerr << "Exiting - FATAL SIGNAL: " << signal_number << " " << std::flush;
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof (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);
|
||||
@ -151,12 +159,11 @@ namespace g2 {
|
||||
}
|
||||
} // 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
|
||||
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;
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================*/
|
||||
* 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.
|
||||
* ============================================================================*/
|
||||
|
||||
#include "crashhandler.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
#include "g2LogMessageBuilder.hpp"
|
||||
#include "g2logmessagecapture.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
@ -19,83 +19,79 @@
|
||||
#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;
|
||||
namespace {
|
||||
|
||||
//FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
g2::FatalMessageBuilder trigger(fatal_stream.str(), signal_number);
|
||||
//std::ostringstream oss;
|
||||
//std::cerr << fatal_message.message_ << std::endl << std::flush;
|
||||
} // scope exit - message sent to LogWorker, wait to die...
|
||||
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;
|
||||
|
||||
LogCapture trigger(FATAL_SIGNAL, signal_number);
|
||||
trigger.stream() << fatal_stream.str();
|
||||
} // scope exit - message sent to LogWorker, wait to die...
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace g2
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
std::string stackdump()
|
||||
{
|
||||
std::string temp;
|
||||
return temp;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
std::string stackdump() {
|
||||
std::string temp;
|
||||
return temp;
|
||||
}
|
||||
|
||||
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");
|
||||
// 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
|
||||
|
||||
raise(signal_number);
|
||||
}
|
||||
} // end g2::internal
|
||||
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");
|
||||
}
|
||||
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
|
||||
|
@ -1,99 +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.
|
||||
* ============================================================================*/
|
||||
|
||||
#include "g2LogMessageBuilder.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
#include "g2log.hpp"
|
||||
#include "std2_make_unique.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
#include <csignal>
|
||||
|
||||
#include <iostream>
|
||||
namespace {
|
||||
const int kMaxMessageSize = 2048;
|
||||
const std::string kTruncatedWarningText = "[...truncated...]";
|
||||
}
|
||||
namespace g2 {
|
||||
using namespace internal;
|
||||
|
||||
LogMessageBuilder::LogMessageBuilder(const std::string& file, const int line,
|
||||
const std::string& function, const LEVELS& level)
|
||||
: _message(std2::make_unique<LogMessage>(file, line, function, level)) {
|
||||
|
||||
if (_message.get()->wasFatal()) {
|
||||
addStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
LogMessageBuilder::~LogMessageBuilder() {
|
||||
_message.get()->write().append(stream().str());
|
||||
if (_message.get()->wasFatal()) {
|
||||
_message.get()->write().append(_stackTrace); // empty or not
|
||||
FatalMessageBuilder trigger(_message, SIGABRT);
|
||||
return; // FatalMessageBuilder will send to worker at scope exit
|
||||
}
|
||||
|
||||
saveMessage(_message); // message saved to g2LogWorker
|
||||
}
|
||||
|
||||
LogMessageBuilder& LogMessageBuilder::setExpression(const std::string& boolean_expression) {
|
||||
_message.get()->setExpression(boolean_expression);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LogMessageBuilder& LogMessageBuilder::addStackTrace() {
|
||||
_stackTrace = {"\n*******\tSTACKDUMP *******\n"};
|
||||
_stackTrace.append(internal::stackdump());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
std::ostringstream& LogMessageBuilder::stream() {
|
||||
return _stream;
|
||||
}
|
||||
|
||||
void LogMessageBuilder::messageSave(const char *printf_like_message, ...) {
|
||||
char finished_message[kMaxMessageSize];
|
||||
va_list arglist;
|
||||
va_start(arglist, printf_like_message);
|
||||
const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
|
||||
va_end(arglist);
|
||||
if (nbrcharacters <= 0) {
|
||||
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
|
||||
stream() << '"' << printf_like_message << '"' << std::endl;
|
||||
} else if (nbrcharacters > kMaxMessageSize) {
|
||||
stream() << finished_message << kTruncatedWarningText;
|
||||
} else {
|
||||
stream() << finished_message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// FatalMessageBuilder
|
||||
|
||||
FatalMessageBuilder::FatalMessageBuilder(const std::string& exit_message, int signal_id)
|
||||
: _fatal_message(std2::make_unique<FatalMessage>(LogMessage{exit_message}, signal_id))
|
||||
{ }
|
||||
|
||||
|
||||
FatalMessageBuilder:: FatalMessageBuilder(LogMessagePtr details, int signal_id)
|
||||
: _fatal_message(std2::make_unique<FatalMessage>(*(details._move_only.get()), signal_id))
|
||||
{}
|
||||
|
||||
|
||||
FatalMessageBuilder::~FatalMessageBuilder() {
|
||||
// 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);
|
||||
|
||||
}
|
||||
} // g2
|
@ -1,51 +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.
|
||||
* ============================================================================*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
|
||||
namespace g2 {
|
||||
|
||||
// At RAII scope end this struct will trigger a FatalMessage sending
|
||||
struct FatalMessageBuilder {
|
||||
//explicit FatalMessageBuilder(const FatalMessage& exit_message);
|
||||
FatalMessageBuilder(const std::string& exit_message, int fatal_signal);
|
||||
FatalMessageBuilder(LogMessagePtr details, int signal_id);
|
||||
virtual ~FatalMessageBuilder();
|
||||
|
||||
FatalMessagePtr _fatal_message;
|
||||
int _fatal_signal;
|
||||
};
|
||||
|
||||
struct LogMessageBuilder {
|
||||
LogMessageBuilder(const std::string& file, const int line, const std::string& function, const LEVELS& level);
|
||||
virtual ~LogMessageBuilder();
|
||||
|
||||
LogMessageBuilder& setExpression(const std::string& boolean_expression);
|
||||
LogMessageBuilder& addStackTrace();
|
||||
|
||||
std::ostringstream& stream();
|
||||
|
||||
|
||||
// Use "-Wall" to generate warnings in case of illegal printf format.
|
||||
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
|
||||
#endif
|
||||
void messageSave(const char *printf_like_message, ...)
|
||||
__attribute__((format(printf, 2, 3))); // ref: http://www.codemaestro.com/reviews/18
|
||||
|
||||
private:
|
||||
LogMessagePtr _message;
|
||||
std::ostringstream _stream;
|
||||
std::string _stackTrace;
|
||||
};
|
||||
} // g2
|
@ -48,20 +48,21 @@ namespace g2 {
|
||||
// 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, []() {
|
||||
installSignalHandler(); });
|
||||
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
|
||||
CHECK(!internal::isLoggingInitialized());
|
||||
CHECK(bgworker != nullptr);
|
||||
|
||||
// Save the first uninitialized message, if any
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -75,35 +76,58 @@ namespace g2 {
|
||||
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. *
|
||||
*/
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
/** 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 &&
|
||||
(dynamic_cast<void*>(active) != dynamic_cast<void*>(g_logger_instance))) {
|
||||
LOG(WARNING) << "\n\t\tShutting 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";
|
||||
return false;
|
||||
if (isLoggingInitialized() && nullptr != active &&
|
||||
(dynamic_cast<void*> (active) != dynamic_cast<void*> (g_logger_instance))) {
|
||||
LOG(WARNING) << "\n\t\tShutting 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";
|
||||
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)) {
|
||||
message.get()->write().append(stack_trace);
|
||||
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
|
||||
@ -112,32 +136,31 @@ namespace g2 {
|
||||
* The first initialized log entry will also save the first uninitialized log message, if any
|
||||
* @param log_entry to save to logger
|
||||
*/
|
||||
void saveMessage(LogMessagePtr incoming) {
|
||||
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;
|
||||
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 fatalCallToLogger(FatalMessagePtr message) {
|
||||
void fatalCallToLogger(FatalMessagePtr message) {
|
||||
if (!isLoggingInitialized()) {
|
||||
std::ostringstream error;
|
||||
error << "FATAL CALL but logger is NOT initialized\n"
|
||||
@ -146,9 +169,7 @@ namespace g2 {
|
||||
std::cerr << error.str() << std::flush;
|
||||
internal::exitWithDefaultSignalHandler(message.get()->_signal_id);
|
||||
}
|
||||
|
||||
g_logger_instance->fatal(message);
|
||||
|
||||
while (true) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
@ -158,7 +179,6 @@ namespace g2 {
|
||||
// By default this function pointer goes to \ref fatalCallToLogger;
|
||||
std::function<void(FatalMessagePtr) > g_fatal_to_g2logworker_function_ptr = fatalCallToLogger;
|
||||
|
||||
|
||||
/** 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
|
||||
@ -168,7 +188,6 @@ namespace g2 {
|
||||
g_fatal_to_g2logworker_function_ptr(FatalMessagePtr{std::move(message)});
|
||||
}
|
||||
|
||||
|
||||
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
|
||||
* This function switches the function pointer so that only
|
||||
* 'unitTest' mock-fatal calls are made.
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2LogMessageBuilder.hpp"
|
||||
#include "g2logmessagecapture.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
|
||||
#if !(defined(__PRETTY_FUNCTION__))
|
||||
@ -45,65 +45,60 @@
|
||||
* --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod
|
||||
*/
|
||||
namespace g2 {
|
||||
class LogWorker;
|
||||
struct LogMessage;
|
||||
struct FatalMessage;
|
||||
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);
|
||||
/** 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);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
void pushMessageToLogger(LogMessagePtr log_entry);
|
||||
|
||||
// Save the created FatalMessage to any existing sinks and exit with
|
||||
// the originating fatal signal,. or SIGABRT if it originated from a broken contract
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace internal {
|
||||
/// @returns true if logger is initialized
|
||||
bool isLoggingInitialized();
|
||||
|
||||
// Save the created LogMessage to any existing sinks
|
||||
void saveMessage(LogMessagePtr log_entry);
|
||||
|
||||
// Save the created FatalMessage to any existing sinks and exit with
|
||||
// the originating fatal signal,. or SIGABRT if it originated from a broken contract
|
||||
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);
|
||||
|
||||
|
||||
|
||||
/** By default the g2log will call g2LogWorker::fatal(...) which will
|
||||
* abort() the system after flushing the logs to file. This makes unit
|
||||
* test of FATAL level cumbersome. A work around is to change the
|
||||
* 'fatal call' which can be done here
|
||||
*
|
||||
* The bool return values in the fatal_call is whether or not the fatal_call should
|
||||
*
|
||||
*/
|
||||
void changeFatalInitHandlerForUnitTesting(std::function<void(FatalMessagePtr) > fatal_call);
|
||||
} // internal
|
||||
/** By default the g2log will call g2LogWorker::fatal(...) which will
|
||||
* abort() the system after flushing the logs to file. This makes unit
|
||||
* test of FATAL level cumbersome. A work around is to change the
|
||||
* 'fatal call' which can be done here
|
||||
*
|
||||
* The bool return values in the fatal_call is whether or not the fatal_call should
|
||||
*
|
||||
*/
|
||||
void changeFatalInitHandlerForUnitTesting(std::function<void(FatalMessagePtr) > fatal_call);
|
||||
} // internal
|
||||
} // g2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define INTERNAL_LOG_MESSAGE(level) g2::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
|
||||
|
||||
#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
|
||||
|
||||
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
|
||||
g2::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, g2::internal::CONTRACT).setExpression(boolean_expression).addStackTrace()
|
||||
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) \
|
||||
@ -165,16 +160,16 @@ And here is possible output
|
||||
: Width trick: 10
|
||||
: A string \endverbatim */
|
||||
#define LOGF(level, printf_like_message, ...) \
|
||||
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
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).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
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).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
|
||||
|
||||
|
||||
|
@ -1,57 +1,48 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* 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
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2log.hpp"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
namespace g2
|
||||
{
|
||||
namespace internal {
|
||||
bool wasFatal(const LEVELS& level) {
|
||||
return level.value >= FATAL.value;
|
||||
}
|
||||
|
||||
// All levels are by default ON: i.e. for DEBUG, INFO, WARNING, FATAL
|
||||
constexpr const int g_level_size{ FATAL.value + 1 };
|
||||
std::atomic<bool> g_log_level_status[4]{{true}, {true}, {true},{true}};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
static_assert(4 == g_level_size, "Mismatch between number of logging levels and their use");
|
||||
#endif
|
||||
} // internal
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
bool wasFatal(const LEVELS& level) {
|
||||
return level.value >= FATAL.value;
|
||||
}
|
||||
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
void setLogLevel(LEVELS log_level, bool enabled)
|
||||
{
|
||||
// MSC: remove when constexpr available. see static_assert above
|
||||
assert(internal::g_level_size == 4 && "Mismatch between number of logging levels and their use");
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
internal::g_log_level_status[level].store(enabled, std::memory_order_release);
|
||||
}
|
||||
#endif
|
||||
// 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}};
|
||||
#endif
|
||||
} // internal
|
||||
|
||||
|
||||
bool logLevel(LEVELS log_level)
|
||||
{
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
bool status = (internal::g_log_level_status[level].load(std::memory_order_acquire));
|
||||
return status;
|
||||
|
||||
void setLogLevel(LEVELS log_level, bool enabled) {
|
||||
assert(internal::g_level_size == 4 && "Mismatch between number of logging levels and their use");
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
internal::g_log_level_status[level].store(enabled, std::memory_order_release);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool logLevel(LEVELS log_level) {
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
bool status = (internal::g_log_level_status[level].load(std::memory_order_acquire));
|
||||
return status;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
} // g2
|
||||
|
@ -1,56 +1,49 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
* 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
|
||||
* ********************************************* */
|
||||
|
||||
#ifndef G2_LOG_LEVELS_HPP_
|
||||
#define G2_LOG_LEVELS_HPP_
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
||||
struct LEVELS
|
||||
{
|
||||
const int value;
|
||||
const char* text;
|
||||
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)
|
||||
: value(other.value), text(other.text.c_str()) {}
|
||||
|
||||
LEVELS(int id, const char* idtext) : value(id), text(idtext) {}
|
||||
const int value;
|
||||
const std::string text;
|
||||
};
|
||||
|
||||
#if _MSC_VER
|
||||
// Visual Studio does not support constexpr
|
||||
// what is below is ugly and not right but it works in this purpose.
|
||||
// if there are any issues with it then just please remove all instances of constexpr
|
||||
#ifndef constexpr
|
||||
#define constexpr
|
||||
#endif
|
||||
#endif
|
||||
constexpr const LEVELS DEBUG{0, "DEBUG"},
|
||||
INFO{DEBUG.value+1, "INFO"},
|
||||
WARNING{INFO.value+1, "WARNING"},
|
||||
// Insert here *any* extra logging levels that is needed
|
||||
// 1) Remember to update the FATAL initialization below
|
||||
// 2) Remember to update the initialization of "g2loglevels.cpp/g_log_level_status"
|
||||
FATAL{WARNING.value+1, "FATAL"};
|
||||
const LEVELS DEBUG{0, {"DEBUG"}}, INFO{DEBUG.value + 1, {"INFO"}},
|
||||
WARNING{INFO.value + 1, {"WARNING"}},
|
||||
// Insert here *any* extra logging levels that is needed
|
||||
// 1) Remember to update the FATAL initialization below
|
||||
// 2) Remember to update the initialization of "g2loglevels.cpp/g_log_level_status"
|
||||
FATAL{WARNING.value + 1, {"FATAL"}};
|
||||
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
constexpr const LEVELS CONTRACT{100, "CONTRACT"}, FATAL_SIGNAL{101, "FATAL_SIGNAL"};
|
||||
|
||||
bool wasFatal(const LEVELS& level);
|
||||
}
|
||||
namespace internal {
|
||||
const LEVELS CONTRACT{100, {"CONTRACT"}}, FATAL_SIGNAL{101, {"FATAL_SIGNAL"}};
|
||||
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
|
||||
|
||||
|
||||
#endif // G2_LOG_LEVELS_HPP_
|
||||
|
@ -110,37 +110,7 @@ namespace g2 {
|
||||
, _level(other._level)
|
||||
, _expression(std::move(other._expression))
|
||||
, _message(std::move(other._message)) {
|
||||
}
|
||||
|
||||
|
||||
// LogMessage& LogMessage::operator=(const LogMessage& other) {
|
||||
// _timestamp = other._timestamp;
|
||||
// _microseconds = other._microseconds;
|
||||
// _file = other._file;
|
||||
// _line=other._line;
|
||||
// _function=other._function;
|
||||
// _level.value = other._level.value;
|
||||
// _level.text = other._level.text;
|
||||
// _expression= other._expression;
|
||||
// stream().str(other.stream().str());
|
||||
// }
|
||||
|
||||
|
||||
// LogMessage& LogMessage::operator=(LogMessage&& other) {
|
||||
// _timestamp = other._timestamp;
|
||||
// _microseconds = other._microseconds;
|
||||
// _file = std::move(other._file);
|
||||
// _line = other._line;
|
||||
// _function = std::move(other._function);
|
||||
// _level = other._level;
|
||||
// _expression = std::move(other._expression);
|
||||
// std::move(_stream, other._stream);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
FatalMessage::FatalMessage(const LogMessage& details, int signal_id)
|
||||
: LogMessage(details), _signal_id(signal_id) { }
|
||||
|
13
g2log/src/g2logmessagecapture.cpp
Normal file
13
g2log/src/g2logmessagecapture.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================*/
|
||||
|
||||
#include "g2logmessagecapture.hpp"
|
||||
#include "g2log.hpp"
|
||||
|
||||
LogCapture::~LogCapture() {
|
||||
using namespace g2::internal;
|
||||
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
|
||||
}
|
92
g2log/src/g2logmessagecapture.hpp
Normal file
92
g2log/src/g2logmessagecapture.hpp
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.
|
||||
* ============================================================================*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
#include "g2log.hpp"
|
||||
|
||||
struct LogCapture {
|
||||
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
||||
LogCapture(const LEVELS& level, int fatal_signal)
|
||||
: LogCapture("", 0, "", level, "", fatal_signal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple struct for capturing log/fatal entries. At destruction the captured message is forwarded to background worker.
|
||||
* -- As a safety precaution: No memory allocated here will be moved into the background worker in case of dynamic loaded library reasons
|
||||
* -- instead the arguments are copied inside of g2log.cpp::saveMessage
|
||||
* @file, line, function are given in g2log.hpp from macros
|
||||
* @level INFO/DEBUG/WARNING/FATAL
|
||||
* @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 = "", int fatal_signal = SIGABRT)
|
||||
: _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {
|
||||
|
||||
if (g2::internal::wasFatal(level)) {
|
||||
_stack_trace = {"\n*******\tSTACKDUMP *******\n"};
|
||||
_stack_trace.append(g2::internal::stackdump());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// At destruction the message will be forwarded to the g2log worker.
|
||||
// in case of dynamically (at runtime) loaded libraries the important thing to know is that
|
||||
// all strings are copied so the original are not destroyed at the receiving end, only the copy
|
||||
~LogCapture();
|
||||
|
||||
|
||||
|
||||
|
||||
// Use "-Wall" to generate warnings in case of illegal printf format.
|
||||
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
|
||||
#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))) // ref: http://www.codemaestro.com/reviews/18
|
||||
{
|
||||
static const int kMaxMessageSize = 2048;
|
||||
static const std::string kTruncatedWarningText = "[...truncated...]";
|
||||
char finished_message[kMaxMessageSize];
|
||||
va_list arglist;
|
||||
va_start(arglist, printf_like_message);
|
||||
const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
if (nbrcharacters <= 0) {
|
||||
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
|
||||
stream() << '"' << printf_like_message << '"' << std::endl;
|
||||
} else if (nbrcharacters > kMaxMessageSize) {
|
||||
stream() << finished_message << kTruncatedWarningText;
|
||||
} else {
|
||||
stream() << finished_message;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream& stream() {
|
||||
return _stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostringstream _stream;
|
||||
std::string _stack_trace;
|
||||
const char* _file;
|
||||
const int _line;
|
||||
const char* _function;
|
||||
const LEVELS& _level;
|
||||
const char* _expression;
|
||||
const int _fatal_signal;
|
||||
|
||||
};
|
||||
//} // g2
|
43
g2log/test_unit/test_linux_dynamic_loaded_sharedlib.cpp
Normal file
43
g2log/test_unit/test_linux_dynamic_loaded_sharedlib.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <g2log.hpp>
|
||||
#include <g2logworker.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "std2_make_unique.hpp"
|
||||
#include "tester_sharedlib.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
struct LogMessageCounter {
|
||||
std::vector<std::string>& bank;
|
||||
LogMessageCounter(std::vector<std::string>& storeMessages) : bank(storeMessages) {
|
||||
}
|
||||
|
||||
void countMessages(std::string msg) {
|
||||
bank.push_back(msg);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
|
||||
std::vector<std::string> receiver;
|
||||
|
||||
{ // scope to flush logs at logworker exit
|
||||
auto worker = g2::LogWorker::createWithNoSink();
|
||||
auto handle = worker->addSink(std2::make_unique<LogMessageCounter>(std::ref(receiver)), &LogMessageCounter::countMessages);
|
||||
g2::initializeLogging(worker.get());
|
||||
|
||||
void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
EXPECT_FALSE(nullptr == libHandle);
|
||||
LibraryFactory* factory = reinterpret_cast<LibraryFactory*> ((dlsym(libHandle, "testRealFactory")));
|
||||
EXPECT_FALSE(nullptr == factory);
|
||||
SomeLibrary* loadedLibrary = factory->CreateLibrary();
|
||||
|
||||
for (size_t i = 0; i < 300; ++i) {
|
||||
loadedLibrary->action();
|
||||
}
|
||||
|
||||
delete loadedLibrary;
|
||||
dlclose(libHandle);
|
||||
} // scope exit. All log entries must be flushed now
|
||||
const int numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library
|
||||
EXPECT_EQ(receiver.size(), numberOfMessages);
|
||||
}
|
28
g2log/test_unit/tester_sharedlib.cpp
Normal file
28
g2log/test_unit/tester_sharedlib.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
#include <g2log.hpp>
|
||||
#include "tester_sharedlib.h"
|
||||
|
||||
struct RuntimeLoadedLib : public SomeLibrary {
|
||||
|
||||
RuntimeLoadedLib() {
|
||||
LOG(INFO) << "Library was created";
|
||||
LOGF(INFO, "Ready for testing");
|
||||
}
|
||||
|
||||
~RuntimeLoadedLib() {
|
||||
LOG(DEBUG) << "Library destroyed";
|
||||
}
|
||||
|
||||
void action() {
|
||||
LOG(WARNING) << "Action, action, action. Safe for LOG calls by runtime dynamically loaded libraries";
|
||||
}
|
||||
};
|
||||
|
||||
struct RealLibraryFactory : public LibraryFactory {
|
||||
SomeLibrary* CreateLibrary() {
|
||||
return new RuntimeLoadedLib;
|
||||
}
|
||||
};
|
||||
|
||||
RealLibraryFactory testRealFactory;
|
||||
|
14
g2log/test_unit/tester_sharedlib.h
Normal file
14
g2log/test_unit/tester_sharedlib.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
struct SomeLibrary {
|
||||
|
||||
SomeLibrary() {};
|
||||
|
||||
virtual ~SomeLibrary() {};
|
||||
virtual void action() = 0;
|
||||
};
|
||||
|
||||
class LibraryFactory {
|
||||
public:
|
||||
virtual SomeLibrary* CreateLibrary() = 0;
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================*/
|
||||
* 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.
|
||||
* ============================================================================*/
|
||||
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@ -23,50 +23,42 @@ namespace testing_helpers {
|
||||
int g_mockFatal_signal = -1;
|
||||
bool g_mockFatalWasCalled = false;
|
||||
|
||||
|
||||
std::string mockFatalMessage() {
|
||||
return g_mockFatal_message;
|
||||
}
|
||||
|
||||
|
||||
int mockFatalSignal() {
|
||||
return g_mockFatal_signal;
|
||||
}
|
||||
|
||||
|
||||
bool mockFatalWasCalled() {
|
||||
return g_mockFatalWasCalled;
|
||||
}
|
||||
|
||||
|
||||
void mockFatalCall(FatalMessagePtr fatal_message) {
|
||||
g_mockFatal_message = fatal_message.get()->toString();
|
||||
g_mockFatal_signal = fatal_message.get()->_signal_id;
|
||||
g_mockFatalWasCalled = true;
|
||||
LogMessagePtr message{fatal_message.release()};
|
||||
g2::internal::saveMessage(message); //fatal_message.copyToLogMessage());
|
||||
g2::internal::pushMessageToLogger(message); //fatal_message.copyToLogMessage());
|
||||
}
|
||||
|
||||
|
||||
void clearMockFatal() {
|
||||
g_mockFatal_message.clear();
|
||||
g_mockFatal_signal = -1;
|
||||
g_mockFatalWasCalled = false;
|
||||
}
|
||||
|
||||
|
||||
bool removeFile(std::string path_to_file) {
|
||||
return (0 == std::remove(path_to_file.c_str()));
|
||||
}
|
||||
|
||||
|
||||
bool verifyContent(const std::string &total_text, std::string msg_to_find) {
|
||||
std::string content(total_text);
|
||||
size_t location = content.find(msg_to_find);
|
||||
return (location != std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
std::string readFileToText(std::string filename) {
|
||||
std::ifstream in;
|
||||
in.open(filename.c_str(), std::ios_base::in);
|
||||
@ -80,12 +72,11 @@ namespace testing_helpers {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
size_t LogFileCleaner::size() {
|
||||
return logs_to_clean_.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
LogFileCleaner::~LogFileCleaner() {
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
{
|
||||
@ -98,7 +89,6 @@ namespace testing_helpers {
|
||||
} // mutex
|
||||
}
|
||||
|
||||
|
||||
void LogFileCleaner::addLogToClean(std::string path_to_log) {
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
{
|
||||
@ -107,11 +97,12 @@ namespace testing_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
ScopedLogger::ScopedLogger() : _currentWorker(g2::LogWorker::createWithNoSink()) {}
|
||||
ScopedLogger::~ScopedLogger() {}
|
||||
|
||||
ScopedLogger::ScopedLogger() : _currentWorker(g2::LogWorker::createWithNoSink()) { }
|
||||
ScopedLogger::~ScopedLogger() { }
|
||||
g2::LogWorker* ScopedLogger::get() { return _currentWorker.get(); }
|
||||
|
||||
g2::LogWorker* ScopedLogger::get() {
|
||||
return _currentWorker.get();
|
||||
}
|
||||
|
||||
RestoreFileLogger::RestoreFileLogger(std::string directory)
|
||||
: _scope(new ScopedLogger), _handle(_scope->get()->addSink(std2::make_unique<g2::FileSink>("UNIT_TEST_LOGGER", directory), &g2::FileSink::fileWrite)) {
|
||||
@ -123,16 +114,15 @@ namespace testing_helpers {
|
||||
auto filename = _handle->call(&FileSink::fileName);
|
||||
if (!filename.valid()) ADD_FAILURE();
|
||||
_log_file = filename.get();
|
||||
|
||||
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
g2::setLogLevel(INFO, true);
|
||||
g2::setLogLevel(DEBUG, true);
|
||||
g2::setLogLevel(WARNING, true);
|
||||
g2::setLogLevel(FATAL, true);
|
||||
g2::setLogLevel(INFO, true);
|
||||
g2::setLogLevel(DEBUG, true);
|
||||
g2::setLogLevel(WARNING, true);
|
||||
g2::setLogLevel(FATAL, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
RestoreFileLogger::~RestoreFileLogger() {
|
||||
g2::internal::shutDownLogging();
|
||||
reset();
|
||||
@ -141,21 +131,20 @@ namespace testing_helpers {
|
||||
ADD_FAILURE();
|
||||
}
|
||||
|
||||
|
||||
std::string RestoreFileLogger::logFile() {
|
||||
if (_scope) {
|
||||
// beware for race condition
|
||||
// example:
|
||||
// LOG(INFO) << ...
|
||||
// auto file = logger.logFile()
|
||||
// auto content = ReadContentFromFile(file)
|
||||
// ... it is not guaranteed that the content will contain (yet) the LOG(INFO)
|
||||
std::future<std::string> filename = _handle->call(&g2::FileSink::fileName);
|
||||
_log_file = filename.get();
|
||||
}
|
||||
return _log_file;
|
||||
}
|
||||
|
||||
if (_scope) {
|
||||
// beware for race condition
|
||||
// example:
|
||||
// LOG(INFO) << ...
|
||||
// auto file = logger.logFile()
|
||||
// auto content = ReadContentFromFile(file)
|
||||
// ... it is not guaranteed that the content will contain (yet) the LOG(INFO)
|
||||
std::future<std::string> filename = _handle->call(&g2::FileSink::fileName);
|
||||
_log_file = filename.get();
|
||||
}
|
||||
return _log_file;
|
||||
}
|
||||
|
||||
// Beware of race between LOG(...) and this function.
|
||||
// since LOG(...) passes two queues but the handle::call only passes one queue
|
||||
// the handle::call can happen faster
|
||||
@ -166,12 +155,4 @@ namespace testing_helpers {
|
||||
auto file = filename.get();
|
||||
return readFileToText(file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // testing_helpers
|
||||
|
Loading…
x
Reference in New Issue
Block a user