mirror of
https://github.com/KjellKod/g3log.git
synced 2025-02-24 23:34:48 +01:00
compile error fixes after major g2log-dev influence-refactoring
This commit is contained in:
parent
a59c837fd2
commit
4adbd1c065
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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'
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <ctime>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user