compile error fixes after major g2log-dev influence-refactoring

This commit is contained in:
KjellKod 2013-10-31 03:28:08 -06:00
parent a59c837fd2
commit 4adbd1c065
15 changed files with 522 additions and 699 deletions

View File

@ -1,35 +1,41 @@
#ifndef CRASH_HANDLER_H_
#define CRASH_HANDLER_H_
/** ==========================================================================
* 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 <string>
#include <csignal>
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
// implementationsfilen kan vara den samma
namespace g2 {
namespace internal {
/** \return signal_name. Ref: signum.hpp and \ref installSignalHandler */
std::string signalName(int signal_number);
namespace internal {
/** \return signal_name. Ref: signum.hpp and \ref installSignalHandler */
std::string signalName(int signal_number);
/** Re-"throw" a fatal signal, previously caught. This will exit the application
* This is an internal only function. Do not use it elsewhere. It is triggered
* from g2log, g2LogWorker after flushing messages to file */
void exitWithDefaultSignalHandler(int signal_number);
} // end g2::internal
/** return calling thread's stackdump*/
std::string stackdump();
/** Re-"throw" a fatal signal, previously caught. This will exit the application
* This is an internal only function. Do not use it elsewhere. It is triggered
* from g2log, g2LogWorker after flushing messages to file */
void exitWithDefaultSignalHandler(int signal_number);
} // end g2::internal
// PUBLIC API:
/** Install signal handler that catches FATAL C-runtime or OS signals
See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.hpptm
SIGABRT ABORT (ANSI), abnormal termination
SIGFPE Floating point exception (ANSI)
SIGILL ILlegal instruction (ANSI)
SIGSEGV Segmentation violation i.e. illegal memory reference
SIGTERM TERMINATION (ANSI) */
void installSignalHandler();
// PUBLIC API:
/** Install signal handler that catches FATAL C-runtime or OS signals
See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.hpptm
SIGABRT ABORT (ANSI), abnormal termination
SIGFPE Floating point exception (ANSI)
SIGILL ILlegal instruction (ANSI)
SIGSEGV Segmentation violation i.e. illegal memory reference
SIGTERM TERMINATION (ANSI) */
void installSignalHandler();
}
#endif // CRASH_HANDLER_H_

View File

@ -1,11 +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 "g2log.hpp"
#include "g2logmessage.hpp"
#include <csignal>
#include <cstring>
@ -13,99 +14,42 @@
#error "crashhandler_unix.cpp used but it's a windows system"
#endif
#include <unistd.h> // getpid,
#include <execinfo.h>
#include <ucontext.h>
#include <cxxabi.h>
#include <cstdlib>
#include <sstream>
#include <iostream>
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)
{
const size_t max_dump_size = 50;
void* dump[max_dump_size];
size_t size = backtrace(dump, max_dump_size);
char** messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address
std::ostringstream oss;
oss << "Received fatal signal: " << g2::internal::signalName(signal_number);
oss << "(" << signal_number << ")" << std::endl;
oss << "\tPID: " << getpid() << std::endl;
// dump stack: skip first frame, since that is here
for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
{
char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
// find parantheses and +address offset surrounding mangled name
for (char *p = messages[idx]; *p; ++p)
{
if (*p == '(')
{
mangled_name = p;
}
else if (*p == '+')
{
offset_begin = p;
}
else if (*p == ')')
{
offset_end = p;
break;
}
}
// if the line could be processed, attempt to demangle the symbol
if (mangled_name && offset_begin && offset_end &&
mangled_name < offset_begin)
{
*mangled_name++ = '\0';
*offset_begin++ = '\0';
*offset_end++ = '\0';
int status;
char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
// if demangling is successful, output the demangled function name
if (status == 0)
{
oss << "\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
oss << offset_begin << offset_end << std::endl;
}
// otherwise, output the mangled function name
else
{
oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
oss << offset_begin << offset_end << std::endl;
}
free(real_name); // mallocated by abi::__cxa_demangle(...)
}
else
{
// no demangling done -- just dump the whole line
oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
}
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
free(messages);
{ // Local scope, trigger send
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 fatal_stream;
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
fatal_stream << oss.str() << std::endl;
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
FatalTrigger trigger(fatal_message); std::ostringstream oss;
std::cerr << fatal_message.message_ << std::endl << std::flush;
} // message sent to g2LogWorker
// wait to die -- will be inside the FatalTrigger
}
std::ostringstream oss;
oss << "Received fatal signal: " << g2::internal::signalName(signal_number);
oss << "(" << signal_number << ")" << std::endl;
oss << "\tPID: " << getpid() << std::endl;
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;
FatalMessage fatal_message(fatal_stream.str(), signal_number);
const auto& crashMessage = fatal_message._crash_message;
std::cerr << crashMessage.toString() << std::endl << std::flush;
FatalTrigger trigger(fatal_message);
std::ostringstream oss;
} // message sent to g2LogWorker by FatalTrigger
// wait to die -- will be inside the FatalTrigger
}
} // end anonymous namespace
@ -115,73 +59,119 @@ namespace
// Redirecting and using signals. In case of fatal signals g2log should log the fatal signal
// and flush the log queue and then "rethrow" the signal to exit
namespace g2
{
// References:
// sigaction : change the default action if a specific signal is received
// http://linux.die.net/man/2/sigaction
// http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html
//
// signal: http://linux.die.net/man/7/signal and
// http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp
//
// memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here
// ,plenty of examples when both or either are used
// http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number
namespace internal
{
std::string signalName(int signal_number)
{
switch(signal_number)
{
case SIGABRT: return "SIGABRT";break;
case SIGFPE: return "SIGFPE"; break;
case SIGSEGV: return "SIGSEGV"; break;
case SIGILL: return "SIGILL"; break;
case SIGTERM: return "SIGTERM"; break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << signal_number << ")";
return oss.str();
namespace g2 {
// References:
// sigaction : change the default action if a specific signal is received
// http://linux.die.net/man/2/sigaction
// http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html
//
// signal: http://linux.die.net/man/7/signal and
// http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp
//
// memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here
// ,plenty of examples when both or either are used
// http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number
namespace internal {
std::string stackdump() {
std::ostringstream oss;
const size_t max_dump_size = 50;
void* dump[max_dump_size];
size_t size = backtrace(dump, max_dump_size);
char** messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address
// dump stack: skip first frame, since that is here
for (size_t idx = 1; idx < size && messages != nullptr; ++idx) {
char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;
// find parantheses and +address offset surrounding mangled name
for (char *p = messages[idx]; *p; ++p) {
if (*p == '(') {
mangled_name = p;
} else if (*p == '+') {
offset_begin = p;
} else if (*p == ')') {
offset_end = p;
break;
}
}
// if the line could be processed, attempt to demangle the symbol
if (mangled_name && offset_begin && offset_end &&
mangled_name < offset_begin) {
*mangled_name++ = '\0';
*offset_begin++ = '\0';
*offset_end++ = '\0';
int status;
char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
// if demangling is successful, output the demangled function name
if (status == 0) {
oss << "\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
oss << offset_begin << offset_end << std::endl;
}// otherwise, output the mangled function name
else {
oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
oss << offset_begin << offset_end << std::endl;
}
free(real_name); // mallocated by abi::__cxa_demangle(...)
} else {
// no demangling done -- just dump the whole line
oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
}
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
free(messages);
return oss.str();
}
}
// 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;
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->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)); //
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_DFL; // take default action for the signal
sigaction(signal_number, &action, NULL);
kill(getpid(), signal_number);
abort(); // should never reach this
}
} // end g2::internal
void installSignalHandler() {
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);
kill(getpid(), signal_number);
abort(); // should never reach this
}
} // end g2::internal
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;
void installSignalHandler()
{
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &crashHandler; // callback to crashHandler for fatal signals
// sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction
action.sa_flags = SA_SIGINFO;
// do it verbose style - install all signal actions
if(sigaction(SIGABRT, &action, NULL) < 0)
perror("sigaction - SIGABRT");
if(sigaction(SIGFPE, &action, NULL) < 0)
perror("sigaction - SIGFPE");
if(sigaction(SIGILL, &action, NULL) < 0)
perror("sigaction - SIGILL");
if(sigaction(SIGSEGV, &action, NULL) < 0)
perror("sigaction - SIGSEGV");
if(sigaction(SIGTERM, &action, NULL) < 0)
perror("sigaction - SIGTERM");
}
// do it verbose style - install all signal actions
if (sigaction(SIGABRT, &action, NULL) < 0)
perror("sigaction - SIGABRT");
if (sigaction(SIGFPE, &action, NULL) < 0)
perror("sigaction - SIGFPE");
if (sigaction(SIGILL, &action, NULL) < 0)
perror("sigaction - SIGILL");
if (sigaction(SIGSEGV, &action, NULL) < 0)
perror("sigaction - SIGSEGV");
if (sigaction(SIGTERM, &action, NULL) < 0)
perror("sigaction - SIGTERM");
}
} // end namespace g2

View File

@ -42,22 +42,25 @@ FileSink::~FileSink() {
exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"});
std::cerr << exit_msg << std::flush;
}
void FileSink::fileWrite(internal::LogEntry message) {
std::ofstream & out(filestream());
auto system_time = systemtime_now();
auto steady_time = std::chrono::steady_clock::now();
out << "\n" << localtime_formatted(system_time, date_formatted);
out << " " << localtime_formatted(system_time, time_formatted); // TODO: time kommer från LogEntry
out << "." << std::chrono::duration_cast<std::chrono::microseconds>(steady_time - _steady_start_time).count();
out << "\t" << message << std::flush;
void FileSink::fileWrite(LogMessage message) {
std::ofstream& out(filestream());
out << "msg1: \n" << message.toString() << std::flush;
out << "msg2:\n" << message.timestamp() << "." << message.microseconds() << message.level();
// << " [" << message.file() << "/" << message.function() << " L" << message.line() << "] "
// << message.expression() << "\t" << '"' << message.message() << '"' << std::flush;
}
std::string FileSink::changeLogFile(const std::string& directory) {
auto now = g2::systemtime_now();
auto now_formatted = g2::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
std::string file_name = createLogFileName(_log_prefix_backup);
std::string prospect_log = directory + file_name;
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
if (nullptr == log_stream) {
fileWrite("Unable to change log file. Illegal filename or busy? Unsuccessful log name was:" + prospect_log);
filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
return {}; // no success
}
@ -65,7 +68,7 @@ std::string FileSink::changeLogFile(const std::string& directory) {
std::ostringstream ss_change;
ss_change << "\n\tChanging log file from : " << _log_file_with_path;
ss_change << "\n\tto new location: " << prospect_log << "\n";
fileWrite(ss_change.str().c_str());
filestream() << now_formatted << ss_change.str();
ss_change.str("");
std::string old_log = _log_file_with_path;
@ -73,7 +76,7 @@ std::string FileSink::changeLogFile(const std::string& directory) {
_outptr = std::move(log_stream);
ss_change << "\n\tNew log file. The previous log file was at: ";
ss_change << old_log;
fileWrite(ss_change.str());
filestream() << now_formatted << ss_change.str();
return _log_file_with_path;
}
std::string FileSink::fileName() {

View File

@ -20,7 +20,7 @@ public:
FileSink(const std::string& log_prefix, const std::string& log_directory);
virtual ~FileSink();
void fileWrite(internal::LogEntry message);
void fileWrite(LogMessage message);
std::string changeLogFile(const std::string& directory);
std::string fileName();

View File

@ -17,131 +17,49 @@
* ********************************************* */
#include "g2log.hpp"
#include "g2log.ipp"
#include <cstdio> // vsnprintf
#include <cassert>
#include <mutex>
#include <csignal>
#include "g2logworker.hpp"
#include "crashhandler.hpp"
#include "g2loglevels.hpp"
#include "g2time.hpp"
namespace g2
{
namespace internal
{
std::once_flag g_start_time_flag;
g2::steady_time_point g_start_time;
std::once_flag g_initialize_flag;
g2LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
std::mutex g_logging_init_mutex;
bool isLoggingInitialized(){
return g_logger_instance != nullptr;
}
long microsecondsCounter()
{
std::call_once(g_start_time_flag, [](){g_start_time = std::chrono::steady_clock::now();});
steady_time_point now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(g_start_time - now).count();
}
void saveMessage(const LogEntry& log_entry) {g_logger_instance->save(log_entry);}
} // internal
// signalhandler and internal clock is only needed to install once
// for unit testing purposes the initializeLogging might be called
// several times... for all other practical use, it shouldn't!
void initializeLogging(g2LogWorker *bgworker)
{
std::call_once(internal::g_initialize_flag, [](){installSignalHandler();});
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
namespace g2 {
namespace internal {
std::once_flag g_initialize_flag;
std::mutex g_logging_init_mutex;
// ref: g2log.ipp
bool isLoggingInitialized() {
return g_logger_instance != nullptr;
}
/// Used for test purposes. Should rarely, if ever, be used in production code
g2LogWorker* shutDownLogging() {
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
g2LogWorker *backup = internal::g_logger_instance;
internal::g_logger_instance = nullptr;
return backup;
}
} // g2::internal
void initializeLogging(g2LogWorker *bgworker) {
std::call_once(internal::g_initialize_flag, []() {
installSignalHandler(); });
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
#ifdef G2_DYNAMIC_LOGGING
setLogLevel(DEBUG, true);
setLogLevel(INFO, true);
setLogLevel(WARNING, true);
setLogLevel(FATAL, true);
setLogLevel(DEBUG, true);
setLogLevel(INFO, true);
setLogLevel(WARNING, true);
setLogLevel(FATAL, true);
#endif
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
internal::g_logger_instance = bgworker;
}
g2LogWorker* shutDownLogging()
{
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
g2LogWorker *backup = internal::g_logger_instance;
internal::g_logger_instance = nullptr;
return backup;
}
namespace internal
{
void fatalCallToLogger(FatalMessage message)
{
// real fatal call to logger
internal::g_logger_instance->fatal(message);
}
void fatalCallForUnitTest(g2::internal::FatalMessage fatal_message)
{
// mock fatal call, not to logger: used by unit test
assert(internal::g_logger_instance != nullptr);
internal::g_logger_instance->save(fatal_message.message_); // calling 'save' instead of 'fatal'
throw std::runtime_error(fatal_message.message_);
}
// By default this function pointer goes to \ref fatalCall;
void (*g_fatal_to_g2logworker_function_ptr)(FatalMessage) = fatalCallToLogger;
// The default, initial, handling to send a 'fatal' event to g2logworker
// the caller will stay here, eternally, until the software is aborted
void fatalCall(FatalMessage message)
{
g_fatal_to_g2logworker_function_ptr(message);
while(true){std::this_thread::sleep_for(std::chrono::seconds(1));}
}
// REPLACE fatalCallToLogger for fatalCallForUnitTest
// This function switches the function pointer so that only
// 'unitTest' mock-fatal calls are made.
void changeFatalInitHandlerForUnitTesting()
{
g_fatal_to_g2logworker_function_ptr = fatalCallForUnitTest;
}
// represents the actual fatal message
FatalMessage::FatalMessage(std::string message, FatalType type, int signal_id)
: message_(message)
, type_(type)
, signal_id_(signal_id){}
// used to RAII trigger fatal message sending to g2LogWorker
FatalTrigger::FatalTrigger(const FatalMessage &message)
: message_(message){}
// at destruction, flushes fatal message to g2LogWorker
FatalTrigger::~FatalTrigger()
{
// either we will stay here eternally, or it's in unit-test mode
// then we throw a std::runtime_error (and never hit sleep)
fatalCall(message_);
}
} // g2::internal
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
internal::g_logger_instance = bgworker;
}
} // g2

View File

@ -24,10 +24,9 @@
#include <cstdarg>
#include "g2loglevels.hpp"
#include "g2logmessage.hpp"
#include "g2LogMessageBuilder.hpp"
class g2LogWorker;
#if !(defined(__PRETTY_FUNCTION__))
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif
@ -35,101 +34,80 @@ class g2LogWorker;
/** namespace for LOG() and CHECK() frameworks
* Histroy lesson: Why the names 'g2' and 'g2log'?:
* The framework was made in my own free time as PUBLIC DOMAIN but the
* first commercial project to use it used 'g2' as an internal denominator for
* the current project. g2 as in 'generation 2'. I decided to keep the g2 and g2log names
* to give credit to the people in that project (you know who you are :) and I guess also
* for 'sentimental' reasons. That a big influence was google's glog is just a happy
* concidence or subconscious choice. Either way g2log became the name for this logger.
*
* --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod
*/
namespace g2
{
* History lesson: Why the names 'g2' and 'g2log'?:
* The framework was made in my own free time as PUBLIC DOMAIN but the
* first commercial project to use it used 'g2' as an internal denominator for
* the current project. g2 as in 'generation 2'. I decided to keep the g2 and g2log names
* to give credit to the people in that project (you know who you are :) and I guess also
* for 'sentimental' reasons. That a big influence was google's glog is just a happy
* concidence or subconscious choice. Either way g2log became the name for this logger.
*
* --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod
*/
namespace g2 {
struct LogMessage;
/** 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(g2LogWorker *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(g2LogWorker *logger);
/** Shutdown the logging by making the pointer to the background logger to nullptr
namespace internal {
// Save the created LogMessage to any existing sinks
void saveMessage(const g2::LogMessage& log_entry);
/** FOR TESTING PURPOSES
* Shutdown the logging by making the pointer to the background logger to nullptr
* The \ref pointer to the g2LogWorker is owned by the instantniater \ref initializeLogging
* and is not deleted. By restoring the ptr to nullptr we can re-initialize it later again.
*
* This is kept for test reasons and should normally not be used */
g2LogWorker* shutDownLogging();
g2LogWorker* shutDownLogging();
bool isLoggingInitialized();
// defined here but should't not have to be used outside the g2log
namespace internal
{
typedef const std::string& LogEntry;
bool isLoggingInitialized();
long microsecondsCounter();
void saveMessage(const LogEntry& log_entry);
/** FOR TESTING PURPOSES: @ref g2log.ipp
* 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 */
void changeFatalInitHandlerForUnitTesting();
/** 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 */
void changeFatalInitHandlerForUnitTesting();
/** Trigger for flushing the message queue and exiting the application
A thread that causes a FatalMessage will sleep forever until the
application has exited (after message flush) */
struct FatalMessage
{
enum FatalType {kReasonFatal, kReasonOS_FATAL_SIGNAL};
FatalMessage(std::string message, FatalType type, int signal_id);
std::string message_;
FatalType type_;
int signal_id_;
};
// At RAII scope end this struct will trigger a FatalMessage sending
struct FatalTrigger
{
FatalTrigger(const FatalMessage& message);
~FatalTrigger();
FatalMessage message_;
};
} // end namespace internal
} // end namespace internal
} // end namespace g2
#define INTERNAL_LOG_MESSAGE(level) g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
#define INTERNAL_LOG_MESSAGE(level) g2::internal::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
//LogMessageBuilder(const char* file, const int line, const char* function, const LEVELS& level){}
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, boolean_expression)
g2::internal::LogMessageBuilder(__FILE__, __LINE__, __PRETTY_FUNCTION__, ::FATAL)
//.setExpression(boolean_expression)
// LOG(level) is the API for the stream log
#define LOG(level) if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream()
#define LOG(level) if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
// 'Conditional' stream log
#define LOG_IF(level, boolean_expression) \
if(true == boolean_expression) \
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream()
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
// 'Design By Contract' stream API. For Broken Contracts:
// unit testing: it will throw std::runtime_error when a contract breaks
// I.R.L : it will exit the application by using fatal signal SIGABRT
#define CHECK(boolean_expression) \
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).messageStream()
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
/** For details please see this
* REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format
* \verbatim
*
* REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format
* \verbatim
*
There are different %-codes for different variable types, as well as options to
limit the length of the variables and whatnot.
Code Format

View File

@ -16,6 +16,10 @@
namespace g2
{
namespace internal {
bool wasFatal(const LEVELS& level) {
return level.value > WARNING.value;
}
std::atomic<bool> g_log_level_status[4]; // DEBUG, INFO, WARNING, FATAL
} // internal

View File

@ -19,9 +19,16 @@ struct LEVELS
{
const int value;
const std::string text;
} const DEBUG = {0, "DEBUG"}, INFO = {1, "INFO"}, WARNING = {2, "WARNING"}, FATAL = {3, "FATAL"};
};
const LEVELS DEBUG = {0, "DEBUG"}, INFO = {1, "INFO"}, WARNING = {2, "WARNING"}, FATAL = {3, "FATAL"};
namespace g2 {
namespace internal {
const LEVELS CONTRACT = {4, "CONTRACT"}, FATAL_SIGNAL{5, "FATAL_SIGNAL"};
bool wasFatal(const LEVELS& level);
}
#ifdef G2_DYNAMIC_LOGGING
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
// Obviously: 'enabled_status' set to 'false' - means to disable that log level

View File

@ -1,202 +1,146 @@
/** ==========================================================================
* 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:g2logmessage.cpp Part of Framework for Logging and Design By Contract
* Created: 2012 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
* 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:g2logmessage.cpp Part of Framework for Logging and Design By Contract
* Created: 2012 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#include "g2logmessage.hpp"
#include <csignal>
#include <stdexcept> // exceptions
#include <thread>
#include <chrono>
#include <mutex>
#include "g2log.hpp"
#include "g2time.hpp"
struct LogEntryMessageImpl
{
LogEntryMessageImpl(const std::string &file, const int line, const std::string& function,
const LEVELS& level, const std::string contract_expression);
const std::string file_;
const int line_;
const std::string function_;
const LEVELS level_;
const std::time_t timestamp_;
const long microseconds_;
const std::string message_;
const std::string expression_; // only with content for CHECK(...) calls
std::ostringstream stream_;
};
LogEntryMessageImpl::LogEntryMessageImpl(const std::string &file, const int line,
const std::string& function, const LEVELS& level,
const std::string contract_expression)
: file_(file), line_(line), function_(function), level_(level)
, timestamp_(g2::systemtime_now())
, microseconds_(g2::internal::microsecondsCounter())
, expression_(contract_expression)
{}
std::string LogEntryMessage::line(){return std::to_string(pimpl_->line_);} //ltorreturn {""};} // // pimpl_->line_;
std::string LogEntryMessage::file(){return pimpl_->file_;}
std::string LogEntryMessage::function(){return pimpl_->function_;}
std::string LogEntryMessage::level(){return pimpl_->level_.text;}
std::string LogEntryMessage::timestamp()
{
using namespace g2;
std::ostringstream oss;
// YYYY/MM/DD HH:MM:SS -- ref g2time.hpp/cpp
oss << localtime_formatted(pimpl_->timestamp_, internal::date_formatted);
oss << " ";
oss << localtime_formatted(pimpl_->timestamp_, internal::time_formatted);
return oss.str();
}
std::string LogEntryMessage::microseconds(){return std::to_string(pimpl_->microseconds_);}
std::string LogEntryMessage::message(){return pimpl_->stream_.str();}
std::string LogEntryMessage::expression(){return pimpl_->expression_;}
std::ostringstream& LogEntryMessage::saveMessageByStream(){return pimpl_->stream_;}
LogEntryMessage::LogEntryMessage(const std::string &file, const int line, const std::string& function,
const LEVELS& level, const std::string contract_expression)
: pimpl_(new LogEntryMessageImpl(file, line, function, level, contract_expression))
{ }
LogEntryMessage::LogEntryMessage(const std::string &file, const int line, const std::string& function,
const LEVELS& level)
: LogEntryMessage(file, line, function, level, "")
{ }
#include "crashhandler.hpp"
#include <stdexcept> // exceptions
#include "g2log.hpp"
#include "g2log.ipp"
namespace g2 {
namespace constants
{
const int kMaxMessageSize = 2048;
const std::string kTruncatedWarningText = "[...truncated...]";
}
/**
Thanks to: http://www.cplusplus.com/reference/string/string/find_last_of/
* Splits string at the last '/' or '\\' separator
* example: "/mnt/something/else.cpp" --> "else.cpp"
* "c:\\windows\\hello.hpp" --> hello.hpp
* "this.is.not-a-path.hpp" -->"this.is.not-a-path.hpp" */
std::string splitFileName(const std::string& str)
{
size_t found;
found = str.find_last_of("(/\\");
return str.substr(found+1);
}
namespace internal {
LogMessage::LogMessage(const std::string &file, const int line, const std::string& function, const LEVELS &level)
: file_(file)
, line_(line)
, function_(function)
, level_(level)
{}
std::string LogMessage::line() const {
return std::to_string(_pimpl->_line);
}
// TODO: Madness duh! refactor this till sanity!
// All logmessage text should be done in the bacground thread
// Only ONE logmessage? Is needed "expression can just be empty for non-expression types"
LogMessage::~LogMessage()
{
using namespace internal;
std::string LogMessage::file() const {
return _pimpl->_file;
}
std::string LogMessage::function() const {
return _pimpl->_function;
}
std::string LogMessage::level() const {
return _pimpl->_level.text;
}
std::string LogMessage::timestamp(const std::string & time_look) const {
std::ostringstream oss;
const bool fatal = (FATAL.value == level_.value);
oss << level_.text << " [" << splitFileName(file_);
if(fatal)
oss << "\n[ *******\tEXIT trigger caused by LOG(FATAL): \n\t";
oss << " at: " << function_ ; // previousl only at FATAL.. make configurable but always on FATAL
oss << " L: " << line_ << "]\t";
const std::string str(stream_.str());
if(!str.empty())
{
oss << '"' << str << '"';
}
log_entry_ += oss.str();
if(!isLoggingInitialized() )
{
std::cerr << "Did you forget to call g2::InitializeLogging(g2LogWorker*) in your main.cpp?" << std::endl;
std::cerr << log_entry_ << std::endl << std::flush;
throw std::runtime_error("Logger not initialized with g2::InitializeLogging(g2LogWorker*) for msg:\n" + log_entry_);
}
if(fatal) // os_fatal is handled by crashhandlers
{
{ // local scope - to trigger FatalMessage sending
FatalMessage::FatalType fatal_type(FatalMessage::kReasonFatal);
FatalMessage fatal_message(log_entry_, fatal_type, SIGABRT);
FatalTrigger trigger(fatal_message);
std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush;
} // will send to worker
}
internal::saveMessage(log_entry_); // message saved to g2LogWorker
}
oss << localtime_formatted(_pimpl->_timestamp, time_look);
return oss.str();
}
void LogMessage::messageSave(const char *printf_like_message, ...)
{
char finished_message[constants::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 > constants::kMaxMessageSize)
{
stream_ << finished_message << constants::kTruncatedWarningText;
}
else
{
stream_ << finished_message;
}
}
std::string LogMessage::microseconds() const {
return std::to_string(_pimpl->_microseconds);
}
LogContractMessage::LogContractMessage(const std::string &file, const int line,
const std::string& function, const std::string &boolean_expression)
: LogMessage(file, line, function, FATAL)
, expression_(boolean_expression)
{}
std::string LogMessage::message() const {
return _pimpl->_stream.str();
}
LogContractMessage::~LogContractMessage()
{
std::string LogMessage::expression() const {
return _pimpl->_expression;
}
bool LogMessage::wasFatal() const {
return internal::wasFatal(_pimpl->_level);
}
// YYYY/MM/DD HH:MM:SS -- ref g2time.hpp/cpp
//out << "\n" << localtime_formatted(system_time, date_formatted);
//out << " " << localtime_formatted(system_time, time_formatted); // TODO: time kommer från LogEntry
//out << "." << std::chrono::duration_cast<std::chrono::microseconds>(steady_time - _steady_start_time).count();
//out << "\t" << message << std::flush;
std::string LogMessage::toString() const {
std::ostringstream oss;
oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t";
log_entry_ = oss.str();
}
oss << "\n" << timestamp() << "." << microseconds() << "\t";
} // internal
oss << level() << " [" << file();
oss << " L: " << line() << "]\t";
// Non-fatal Log Message
if (false == wasFatal()) {
oss << '"' << message() << '"';
return oss.str();
}
if (internal::FATAL_SIGNAL.value == _pimpl->_level.value) {
oss << "\n\n***** FATAL SIGNAL RECEIVED ******* " << std::endl;
oss << '"' << message() << '"';
return oss.str();
}
// Not crash scenario but LOG or CONTRACT
auto level_value = _pimpl->_level.value;
if (FATAL.value == level_value) {
oss << "\n\t*******\tEXIT trigger caused LOG(FATAL) entry: \n\t";
oss << '"' << message() << '"';
oss << "\n*******\tSTACKDUMP *******\n" << internal::stackdump();
} else if (internal::CONTRACT.value == level_value) {
oss << "\n\t *******\tEXIT trigger caused by broken Contract: CHECK(" << _pimpl->_expression << ")\n\t";
oss << '"' << message() << '"';
oss << "\n*******\tSTACKDUMP *******\n" << internal::stackdump();
} else {
oss << "\n\t*******\tUNKNOWN Log Message Type\n" << '"' << message() << '"';
}
return oss.str();
}
std::ostringstream& LogMessage::stream() {
return _pimpl->_stream;
}
LogMessage(std::shared_ptr<LogMessageImpl> details)
: _pimpl(details) { }
namespace internal {
FatalMessage::FatalMessage(const std::string& crash_message, int signal_id)
: FatalMessage({std::make_shared<LogMessageImpl>(crash_message)}, signal_id) { }
FatalMessage(const LogMessage& message, int signal_id)
: _crash_message(message), signal_id_(signal_id) { }
FatalTrigger::FatalTrigger(const FatalMessage& exit_message) : _fatal_message(exit_message) { }
FatalTrigger::~FatalTrigger() {
// 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);
}
} // internal
} // g2

View File

@ -1,16 +1,15 @@
/** ==========================================================================
* 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:g2logmessage.hpp Part of Framework for Logging and Design By Contract
* Created: 2012 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
* Filename:g2logmessage.hpp Part of Framework for Logging and Design By Contract
* Created: 2012 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#ifndef G2_LOG_MESSAGE_HPP
#define G2_LOG_MESSAGE_HPP
#pragma once
#include <string>
@ -21,91 +20,53 @@
#include "g2log.hpp"
#include "g2loglevels.hpp"
#include "g2time.hpp"
struct LogEntryMessageImpl;
// Notice: Currently the LogEntryMessage does not use polymorphism to
// implement the "expression" part even though it is only used for CHECK(...)
// contracts and not by normal messages
class LogEntryMessage
{
std::unique_ptr<LogEntryMessageImpl> pimpl_;
std::string file();
std::string line();
std::string function();
std::string level();
std::string timestamp();
std::string microseconds();
std::string message();
std::string expression();
namespace g2 {
struct LogMessageImpl;
std::ostringstream& saveMessageByStream();
#ifndef __GNUC__
#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
#endif
// Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first
// compiler given argument 'this'. This must be supplied as well, hence '2,3'
// ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod
void saveMessage(const char *printf_like_message, ...)
__attribute__((format(printf,2,3) ));
public:
// replace for factory function instead
// createContractMessage
// createMessage
LogEntryMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level);
LogEntryMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level, const std::string boolean_expression);
struct LogMessage {
mutable std::shared_ptr<LogMessageImpl> _pimpl;
std::string file() const;
std::string line() const;
std::string function() const;
std::string level() const;
std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
std::string microseconds() const;
std::string message() const;
std::string expression() const;
bool wasFatal() const;
// convert all content to ONE string
std::string toString() const;
std::ostringstream& stream();
explicit LogMessage(std::shared_ptr<LogMessageImpl> details);
~LogMessage() = default;
};
namespace internal {
/** Trigger for flushing the message queue and exiting the application
* A thread that causes a FatalMessage will sleep forever until the
* application has exited (after message flush) */
struct FatalMessage {
FatalMessage(const std::string& message, int signal_id);
FatalMessage(const LogMessage& message, int signal_id);
~FatalMessage() = default;
mutable LogMessage _crash_message;
int signal_id_;
};
namespace g2 { namespace internal {
// temporary message construct to capture log input and push to g2logworker
class LogMessage
{
public:
LogMessage(const std::string &file, const int line, const std::string& function, const LEVELS& level);
virtual ~LogMessage(); // flush the message
std::ostringstream& messageStream(){return stream_;}
// The __attribute__ generates compiler warnings if illegal "printf" format
// IMPORTANT: You muse enable the compiler flag '-Wall' for this to work!
// ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
#ifndef __GNUC__
#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
#endif
// Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first
// compiler given argument 'this'. This must be supplied as well, hence '2,3'
// ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod
void messageSave(const char *printf_like_message, ...)
__attribute__((format(printf,2,3) ));
protected:
const std::string file_;
const int line_;
const std::string function_;
const LEVELS& level_;
std::ostringstream stream_;
std::string log_entry_;
};
// 'Design-by-Contract' temporary messsage construction
class LogContractMessage : public LogMessage
{
public:
LogContractMessage(const std::string &file, const int line,
const std::string &function, const std::string &boolean_expression);
virtual ~LogContractMessage(); // at destruction will flush the message
protected:
const std::string expression_;
};
} // internal
} // g2
#endif //G2_LOG_MESSAGE_HPP
// At RAII scope end this struct will trigger a FatalMessage sending
struct FatalTrigger {
explicit FatalTrigger(const FatalMessage& exit_message);
~FatalTrigger();
FatalMessage _fatal_message;
};
} // internal
} // g2

View File

@ -39,25 +39,25 @@ struct g2LogWorkerImpl {
std::cout << "g2logworker active object destroyed. done sending exit messages to all sinks\n";
}
void bgSave(g2::internal::LogEntry msg) {
void bgSave(const g2::LogMessage& msg) {
for (auto& sink : _sinks) {
sink->send(msg);
}
if (_sinks.empty()) {
std::string err_msg{"g2logworker has no sinks. Message: ["};
err_msg.append(msg).append({"]\n"});
err_msg.append(msg.toString()).append({"]\n"});
std::cerr << err_msg;
}
}
void bgFatal(g2::internal::FatalMessage fatal_message) {
auto entry = fatal_message.message_;
auto entry = fatal_message._crash_message;
entry.stream() << "\nExiting after fatal event. Log flushed sucessfully to disk.\n";
bgSave(entry);
std::string end_message{"Exiting after fatal event. Log flushed sucessfully to disk.\n"};
bgSave(end_message);
std::cerr << "g2log sinks are flushed. Now exiting after receiving fatal event\n" << std::flush;
std::cerr << entry.toString() << std::endl;
std::cerr << "g2log sinks are flushed. Now exiting after receiving fatal event\n" << std::flush;
_sinks.clear(); // flush all queues
exitWithDefaultSignalHandler(fatal_message.signal_id_);
@ -77,9 +77,9 @@ g2LogWorker::g2LogWorker()
g2LogWorker::~g2LogWorker() {
_pimpl->_bg->send([this]{_pimpl->_sinks.clear();});
}
void g2LogWorker::save(g2::internal::LogEntry msg) {
_pimpl->_bg->send([this, msg] { _pimpl->bgSave(msg); });
// todo move operator
void g2LogWorker::save(g2::LogMessage msg) {
_pimpl->_bg->send([this, msg] { _pimpl->bgSave(msg); }); // TODO std::move
}
void g2LogWorker::fatal(g2::internal::FatalMessage fatal_message) {

View File

@ -56,7 +56,7 @@ public:
/// pushes in background thread (asynchronously) input messages to log file
void save(g2::internal::LogEntry entry);
void save(g2::LogMessage entry);
/// Will push a fatal message on the queue, this is the last message to be processed
/// this way it's ensured that all existing entries were flushed before 'fatal'

View File

@ -14,50 +14,63 @@
#include "g2sinkwrapper.h"
#include "active.hpp"
#include "g2future.h"
#include "g2logmessage.hpp"
namespace g2 {
namespace internal {
typedef std::function<void(LogEntry) > AsyncMessageCall;
namespace internal {
typedef std::function<void(LogMessage) > AsyncMessageCall;
/// The asynchronous Sink has an active object, incoming requests for actions
// will be processed in the background by the specific object the Sink represents.
//
// The Sink will wrap either
// a Sink with Message object receiving call
// or a Sink with a LogEntry (string) receving call
//
// The Sink can also be used through the SinkHandler to call Sink specific function calls
// Ref: send(Message) deals with incoming log entries (converted if necessary to string)
// Ref: send(Call call, Args... args) deals with calls
// to the real sink's API
/// The asynchronous Sink has an active object, incoming requests for actions
// will be processed in the background by the specific object the Sink represents.
//
// Ref: send(LogEntry) deals with incoming log entries
// Ref: send(Call call, Args... args) deals with calls
// to the real sink's API
template<class T>
struct Sink : public SinkWrapper {
std::shared_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call;
template<class T>
struct Sink : public SinkWrapper {
std::shared_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call;
template<typename DefaultLogCall >
Sink(std::shared_ptr<T> sink, DefaultLogCall call)
: SinkWrapper { },
_real_sink{sink},
_bg(kjellkod::Active::createActive()),
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1))
{ }
template<typename DefaultLogCall >
Sink(std::shared_ptr<T> sink, DefaultLogCall call)
: SinkWrapper (),
_real_sink{sink},
_bg(kjellkod::Active::createActive()),
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
}
virtual ~Sink() {
_bg.reset(); // TODO: to remove
}
Sink(std::shared_ptr<T> sink, void(T::*Call)(std::string) )
: SinkWrapper(),
_real_sink {sink},
_bg(kjellkod::Active::createActive()) {
auto adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1);
_default_log_call = [ = ](const LogMessage &m){adapter(m.toString());};
}
void send(LogEntry msg) override {
_bg->send([this, msg]{_default_log_call(msg);});
}
virtual ~Sink() {
_bg.reset(); // TODO: to remove
}
template<typename Call, typename... Args>
auto send(Call call, Args... args)-> std::future < decltype(bind(call, _real_sink.get(), args...)()) > {
return g2::spawn_task(std::bind(call, _real_sink.get(), args...), _bg.get());
}
};
}
}
void send(LogMessage msg) override {
_bg->send([this, msg] {
_default_log_call(msg);
});
}
template<typename Call, typename... Args>
auto send(Call call, Args... args)-> std::future < decltype(bind(call, _real_sink.get(), args...)()) > {
return g2::spawn_task(std::bind(call, _real_sink.get(), args...), _bg.get());
}
};
} // internal
} // g2
#endif /* G2SINK_IPP */

View File

@ -15,10 +15,10 @@ namespace g2 {
struct SinkWrapper {
virtual ~SinkWrapper() { }
virtual void send(LogEntry msg) = 0;
virtual void send(LogMessage msg) = 0;
};
}
}
#endif /* G2SINKWRAPPER_IPP */
#endif /* G2SINKWRAPPER_IPP */

View File

@ -16,7 +16,6 @@
#include <string>
#include <chrono>
#include <thread>
#include <ctime>
#include <cassert>
#include <iomanip>