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:
KjellKod 2014-03-08 23:33:49 -07:00
parent 0bfd6cb97b
commit f5438d8232
16 changed files with 500 additions and 509 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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());
}

View File

@ -0,0 +1,92 @@
/** ==========================================================================
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================*/
#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

View 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);
}

View 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;

View File

@ -0,0 +1,14 @@
#pragma once
struct SomeLibrary {
SomeLibrary() {};
virtual ~SomeLibrary() {};
virtual void action() = 0;
};
class LibraryFactory {
public:
virtual SomeLibrary* CreateLibrary() = 0;
};

View File

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