mirror of
https://github.com/KjellKod/g3log.git
synced 2025-03-04 07:27:25 +01:00
Dynamic sinks... all unit test working (although they should be consolidated). name-file manual merge from g2log-dev
This commit is contained in:
parent
ad998db248
commit
a59c837fd2
@ -1,34 +1,35 @@
|
||||
#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>
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
/// @return signal_name. Ref: signum.h 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);
|
||||
/** 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
|
||||
// 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): http://en.wikipedia.org/wiki/SIGFPE
|
||||
SIGFPE Floating point exception (ANSI)
|
||||
SIGILL ILlegal instruction (ANSI)
|
||||
SIGSEGV Segmentation violation i.e. illegal memory reference
|
||||
SIGTERM TERMINATION (ANSI) */
|
||||
void installSignalHandler();
|
||||
|
||||
} // g2::internal
|
||||
} // g2
|
||||
SIGTERM TERMINATION (ANSI) */
|
||||
void installSignalHandler();
|
||||
}
|
||||
|
||||
#endif // CRASH_HANDLER_H_
|
||||
|
@ -1,16 +1,15 @@
|
||||
/** ==========================================================================
|
||||
* 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.h"
|
||||
#include "g2log.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
||||
// or? gcc on windows I guess,.
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__)) // windows and not mingw
|
||||
#error "crashhandler_unix.cpp used but it's a windows system"
|
||||
#endif
|
||||
|
||||
@ -21,78 +20,92 @@
|
||||
#include <cxxabi.h>
|
||||
#include <cstdlib>
|
||||
|
||||
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
|
||||
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;
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
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';
|
||||
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;
|
||||
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(...)
|
||||
}
|
||||
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
|
||||
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
|
||||
free(messages);
|
||||
{ // Local scope, trigger send
|
||||
using namespace g2::internal;
|
||||
std::string fatal_content = {"\n\n***** FATAL TRIGGER RECEIVED ******* \n"};
|
||||
fatal_content.append(oss.str()).append("\n");
|
||||
fatal_content.append({"\n***** RETHROWING SIGNAL "});
|
||||
fatal_content.append(signalName(signal_number)).append("(");
|
||||
fatal_content.append(std::to_string(signal_number)).append({")\n"});
|
||||
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_content, FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
FatalTrigger trigger(fatal_message);
|
||||
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
|
||||
}
|
||||
} // message sent to g2LogWorker
|
||||
// wait to die -- will be inside the FatalTrigger
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
@ -102,67 +115,73 @@ void crashHandler(int signal_number, siginfo_t *info, void *unused_context) {
|
||||
|
||||
// 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;
|
||||
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::string error = {"UNKNOWN SIGNAL("};
|
||||
error.append(std::to_string(signal_number)).append(")");
|
||||
return error;
|
||||
}
|
||||
}
|
||||
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 << "\nExiting - 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
|
||||
}
|
||||
// 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));
|
||||
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;
|
||||
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)
|
||||
// do it verbose style - install all signal actions
|
||||
if(sigaction(SIGABRT, &action, NULL) < 0)
|
||||
perror("sigaction - SIGABRT");
|
||||
if (sigaction(SIGFPE, &action, NULL) < 0)
|
||||
if(sigaction(SIGFPE, &action, NULL) < 0)
|
||||
perror("sigaction - SIGFPE");
|
||||
if (sigaction(SIGILL, &action, NULL) < 0)
|
||||
if(sigaction(SIGILL, &action, NULL) < 0)
|
||||
perror("sigaction - SIGILL");
|
||||
if (sigaction(SIGSEGV, &action, NULL) < 0)
|
||||
if(sigaction(SIGSEGV, &action, NULL) < 0)
|
||||
perror("sigaction - SIGSEGV");
|
||||
if (sigaction(SIGTERM, &action, NULL) < 0)
|
||||
if(sigaction(SIGTERM, &action, NULL) < 0)
|
||||
perror("sigaction - SIGTERM");
|
||||
}
|
||||
} // g2::internal
|
||||
} // g2
|
||||
}
|
||||
} // end namespace g2
|
||||
|
@ -1,11 +1,11 @@
|
||||
/** ==========================================================================
|
||||
* 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.h"
|
||||
#include "g2log.hpp"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
@ -17,67 +17,76 @@
|
||||
#include <process.h> // getpid
|
||||
#define getpid _getpid
|
||||
|
||||
namespace {
|
||||
void crashHandler(int signal_number) {
|
||||
using namespace g2::internal;
|
||||
std::ostringstream fatal_stream;
|
||||
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
|
||||
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
namespace
|
||||
{
|
||||
void crashHandler(int signal_number)
|
||||
{
|
||||
using namespace g2::internal;
|
||||
std::ostringstream fatal_stream;
|
||||
fatal_stream << "\n\n***** FATAL TRIGGER RECEIVED ******* " << std::endl;
|
||||
fatal_stream << "\n***** RETHROWING SIGNAL " << signalName(signal_number) << "(" << signal_number << ")" << std::endl;
|
||||
|
||||
FatalMessage fatal_message(fatal_stream.str(), FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
FatalTrigger trigger(fatal_message);
|
||||
std::ostringstream oss;
|
||||
std::cerr << fatal_message.message_ << std::endl << std::flush;
|
||||
FatalMessage fatal_message(fatal_stream.str(),FatalMessage::kReasonOS_FATAL_SIGNAL, signal_number);
|
||||
FatalTrigger trigger(fatal_message); std::ostringstream oss;
|
||||
std::cerr << fatal_message.message_ << std::endl << std::flush;
|
||||
} // scope exit - message sent to LogWorker, wait to die...
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
std::string signalName(int signal_number) {
|
||||
switch (signal_number) {
|
||||
case SIGABRT: return {"SIGABRT"}; break;
|
||||
case SIGFPE: return {"SIGFPE"}; break;
|
||||
case SIGSEGV: return {"SIGSEGV"}; break;
|
||||
case SIGILL: return {"SIGILL"}; break;
|
||||
case SIGTERM: return {"SIGTERM"}; break;
|
||||
default:
|
||||
std::string out = {"UNKNOWN SIGNAL("}
|
||||
out.append(std::to_string(signal_number).append(")");
|
||||
return oss.str();
|
||||
}
|
||||
namespace g2
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
std::string signalName(int signal_number)
|
||||
{
|
||||
switch(signal_number)
|
||||
{
|
||||
case SIGABRT: return "SIGABRT";break;
|
||||
case SIGFPE: return "SIGFPE"; break;
|
||||
case SIGSEGV: return "SIGSEGV"; break;
|
||||
case SIGILL: return "SIGILL"; break;
|
||||
case SIGTERM: return "SIGTERM"; break;
|
||||
default:
|
||||
std::ostringstream oss;
|
||||
oss << "UNKNOWN SIGNAL(" << signal_number << ")";
|
||||
return oss.str();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Triggered by g2log::LogWorker after receiving a FATAL trigger
|
||||
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
|
||||
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
|
||||
void exitWithDefaultSignalHandler(int signal_number) {
|
||||
// Restore our signalhandling to default
|
||||
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if (SIG_ERR == signal(SIGILL, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
void exitWithDefaultSignalHandler(int signal_number)
|
||||
{
|
||||
// Restore our signalhandling to default
|
||||
if(SIG_ERR == signal (SIGABRT, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGFPE, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGSEGV, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGILL, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGTERM, SIG_DFL))
|
||||
perror("signal - SIGABRT");
|
||||
|
||||
raise(signal_number);
|
||||
}
|
||||
void installSignalHandler() {
|
||||
if (SIG_ERR == signal(SIGABRT, crashHandler))
|
||||
perror("signal - SIGABRT");
|
||||
if (SIG_ERR == signal(SIGFPE, crashHandler))
|
||||
perror("signal - SIGFPE");
|
||||
if (SIG_ERR == signal(SIGSEGV, crashHandler))
|
||||
perror("signal - SIGSEGV");
|
||||
if (SIG_ERR == signal(SIGILL, crashHandler))
|
||||
perror("signal - SIGILL");
|
||||
if (SIG_ERR == signal(SIGTERM, crashHandler))
|
||||
perror("signal - SIGTERM");
|
||||
} // end g2::internal
|
||||
|
||||
|
||||
void installSignalHandler()
|
||||
{
|
||||
if(SIG_ERR == signal (SIGABRT, crashHandler))
|
||||
perror("signal - SIGABRT");
|
||||
if(SIG_ERR == signal (SIGFPE, crashHandler))
|
||||
perror("signal - SIGFPE");
|
||||
if(SIG_ERR == signal (SIGSEGV, crashHandler))
|
||||
perror("signal - SIGSEGV");
|
||||
if(SIG_ERR == signal (SIGILL, crashHandler))
|
||||
perror("signal - SIGILL");
|
||||
if(SIG_ERR == signal (SIGTERM, crashHandler))
|
||||
perror("signal - SIGTERM");
|
||||
}
|
||||
} // g2::internal
|
||||
} // g2
|
||||
} // end namespace g2
|
||||
|
@ -1,99 +1,86 @@
|
||||
/** ==========================================================================
|
||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
#include "g2filesink.hpp"
|
||||
#include "g2filesinkhelper.ipp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace g2 {
|
||||
using namespace internal;
|
||||
|
||||
g2FileSink::g2FileSink(const std::string& log_prefix, const std::string& log_directory)
|
||||
: _log_file_with_path(log_directory)
|
||||
, _log_prefix_backup(log_prefix)
|
||||
, _outptr(new std::ofstream)
|
||||
, _steady_start_time(std::chrono::steady_clock::now()) // TODO: ha en timer function steadyTimer som har koll på start
|
||||
{
|
||||
_log_prefix_backup = prefixSanityFix(log_prefix);
|
||||
if (!isValidFilename(_log_prefix_backup)) {
|
||||
using namespace internal;
|
||||
FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory)
|
||||
: _log_file_with_path(log_directory)
|
||||
, _log_prefix_backup(log_prefix)
|
||||
, _outptr(new std::ofstream)
|
||||
, _steady_start_time(std::chrono::steady_clock::now())
|
||||
{
|
||||
_log_prefix_backup = prefixSanityFix(log_prefix);
|
||||
if (!isValidFilename(_log_prefix_backup)) {
|
||||
std::cerr << "g2log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
std::string file_name = createLogFileName(_log_prefix_backup);
|
||||
_log_file_with_path = pathSanityFix(_log_file_with_path, file_name);
|
||||
_outptr = createLogFile(_log_file_with_path);
|
||||
std::string file_name = createLogFileName(_log_prefix_backup);
|
||||
_log_file_with_path = pathSanityFix(_log_file_with_path, file_name);
|
||||
_outptr = createLogFile(_log_file_with_path);
|
||||
|
||||
if (!_outptr) {
|
||||
if (!_outptr) {
|
||||
std::cerr << "Cannot write log file to location, attempting current directory" << std::endl;
|
||||
_log_file_with_path = "./" + file_name;
|
||||
_outptr = createLogFile(_log_file_with_path);
|
||||
}
|
||||
assert(_outptr && "cannot open log file at startup");
|
||||
addLogFileHeader();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
assert(_outptr && "cannot open log file at startup");
|
||||
addLogFileHeader();
|
||||
}
|
||||
FileSink::~FileSink() {
|
||||
std::string exit_msg{"\n\t\tg2log g2FileSink shutdown at: "};
|
||||
exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted));
|
||||
filestream() << exit_msg << std::flush;
|
||||
|
||||
g2FileSink::~g2FileSink() {
|
||||
std::string exit_msg {"\n\t\tg2log g2FileSink shutdown at: "};
|
||||
exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted));
|
||||
filestream() << exit_msg << std::flush;
|
||||
|
||||
exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"});
|
||||
std::cerr << exit_msg << std::flush;
|
||||
}
|
||||
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 g2FileSink::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 g2FileSink::backgroundExitFatal(internal::FatalMessage fatal_message) {
|
||||
//
|
||||
// backgroundFileWrite(fatal_message.message_);
|
||||
// backgroundFileWrite("Log flushed successfully to disk \nExiting");
|
||||
// std::cerr << "g2log exiting after receiving fatal event" << std::endl;
|
||||
// std::cerr << "Log file at: [" << log_file_with_path_ << "]\n" << std::endl << std::flush;
|
||||
// filestream().close();
|
||||
// exitWithDefaultSignalHandler(fatal_message.signal_id_);
|
||||
// perror("g2log exited after receiving FATAL trigger. Flush message status: "); // should never reach this point
|
||||
// }
|
||||
|
||||
std::string g2FileSink::changeLogFile(const std::string& directory) {
|
||||
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) {
|
||||
std::string FileSink::changeLogFile(const std::string& directory) {
|
||||
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);
|
||||
return ""; // no success
|
||||
}
|
||||
return {}; // no success
|
||||
}
|
||||
|
||||
addLogFileHeader();
|
||||
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());
|
||||
ss_change.str("");
|
||||
addLogFileHeader();
|
||||
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());
|
||||
ss_change.str("");
|
||||
|
||||
std::string old_log = _log_file_with_path;
|
||||
_log_file_with_path = prospect_log;
|
||||
_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());
|
||||
return _log_file_with_path;
|
||||
}
|
||||
|
||||
std::string g2FileSink::fileName() {
|
||||
return _log_file_with_path;
|
||||
}
|
||||
|
||||
void g2FileSink::addLogFileHeader() {
|
||||
filestream() << header();
|
||||
}
|
||||
std::string old_log = _log_file_with_path;
|
||||
_log_file_with_path = prospect_log;
|
||||
_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());
|
||||
return _log_file_with_path;
|
||||
}
|
||||
std::string FileSink::fileName() {
|
||||
return _log_file_with_path;
|
||||
}
|
||||
void FileSink::addLogFileHeader() {
|
||||
filestream() << header();
|
||||
}
|
||||
|
||||
} // g2
|
@ -1,6 +1,12 @@
|
||||
#ifndef PRIVATE_G2_FILE_SINK_H_
|
||||
#define PRIVATE_G2_FILE_SINK_H_
|
||||
|
||||
/** ==========================================================================
|
||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
@ -8,31 +14,30 @@
|
||||
#include "g2time.hpp"
|
||||
|
||||
namespace g2 {
|
||||
struct g2FileSink {
|
||||
g2FileSink(const std::string& log_prefix, const std::string& log_directory);
|
||||
virtual ~g2FileSink();
|
||||
|
||||
void fileWrite(internal::LogEntry message);
|
||||
std::string changeLogFile(const std::string& directory);
|
||||
std::string fileName();
|
||||
class FileSink {
|
||||
public:
|
||||
FileSink(const std::string& log_prefix, const std::string& log_directory);
|
||||
virtual ~FileSink();
|
||||
|
||||
void fileWrite(internal::LogEntry message);
|
||||
std::string changeLogFile(const std::string& directory);
|
||||
std::string fileName();
|
||||
|
||||
|
||||
private:
|
||||
//void backgroundExitFatal(internal::FatalMessage fatal_message);
|
||||
void addLogFileHeader();
|
||||
private:
|
||||
std::string _log_file_with_path;
|
||||
std::string _log_prefix_backup; // needed in case of future log file changes of directory
|
||||
std::unique_ptr<std::ofstream> _outptr;
|
||||
g2::steady_time_point _steady_start_time;
|
||||
|
||||
std::string _log_file_with_path;
|
||||
std::string _log_prefix_backup; // needed in case of future log file changes of directory
|
||||
std::unique_ptr<std::ofstream> _outptr;
|
||||
g2::steady_time_point _steady_start_time;
|
||||
|
||||
|
||||
g2FileSink& operator=(const g2FileSink&); // c++11 feature not yet in vs2010 = delete;
|
||||
g2FileSink(const g2FileSink& other); // c++11 feature not yet in vs2010 = delete;
|
||||
void addLogFileHeader();
|
||||
std::ofstream & filestream() {return *(_outptr.get()); }
|
||||
|
||||
std::ofstream & filestream() {
|
||||
return *(_outptr.get());
|
||||
}
|
||||
};
|
||||
|
||||
FileSink& operator=(const FileSink&) = delete;
|
||||
FileSink(const FileSink& other) = delete;
|
||||
|
||||
};
|
||||
} // g2
|
||||
#endif // pimple
|
||||
|
@ -1,5 +1,10 @@
|
||||
#ifndef G2_FILESINK_HELPER_IPP_
|
||||
#define G2_FILESINK_HELPER_IPP_
|
||||
/** ==========================================================================
|
||||
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <memory>
|
||||
@ -10,21 +15,12 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
//#include <fstream>
|
||||
//#include <algorithm>
|
||||
//#include <future>
|
||||
//#include <cassert>
|
||||
//#include <chrono>
|
||||
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S";
|
||||
|
||||
// check for filename validity - filename should not be part of PATH
|
||||
|
||||
bool isValidFilename(const std::string& prefix_filename) {
|
||||
std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
|
||||
size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
|
||||
@ -80,7 +76,7 @@ namespace g2 {
|
||||
std::string header() {
|
||||
std::ostringstream ss_entry;
|
||||
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
|
||||
ss_entry << "\t\tg2log created log file at: " << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
|
||||
ss_entry << "\t\tg2log created log at: " << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
|
||||
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss.uuu* LEVEL FILE:LINE] message\n\n"; // TODO: if(header)
|
||||
return ss_entry.str();
|
||||
}
|
||||
@ -121,4 +117,3 @@ namespace g2 {
|
||||
|
||||
}
|
||||
}
|
||||
#endif // G2_FILESINK_HELPER_IPP_
|
@ -43,7 +43,7 @@ struct PretendToBeCopyable
|
||||
|
||||
void operator()() { move_only_(); } // execute
|
||||
private:
|
||||
Moveable move_only_;
|
||||
mutable Moveable move_only_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -16,218 +16,132 @@
|
||||
* 5. Various Q&A at StackOverflow
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2log.h"
|
||||
#include "g2log.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept> // exceptions
|
||||
#include <cstdio> // vsnprintf
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <csignal>
|
||||
|
||||
#include "g2logworker.h"
|
||||
#include "g2logworker.hpp"
|
||||
#include "crashhandler.hpp"
|
||||
#include <signal.h>
|
||||
#include <thread>
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2time.hpp"
|
||||
|
||||
namespace g2
|
||||
{
|
||||
namespace constants
|
||||
{
|
||||
const int kMaxMessageSize = 2048;
|
||||
const std::string kTruncatedWarningText = "[...truncated...]";
|
||||
}
|
||||
namespace internal
|
||||
{
|
||||
static g2LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
|
||||
static std::mutex g_logging_init_mutex;
|
||||
bool isLoggingInitialized(){return g_logger_instance != nullptr; }
|
||||
|
||||
/** 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.h" --> hello.h
|
||||
* "this.is.not-a-path.h" -->"this.is.not-a-path.h" */
|
||||
std::string splitFileName(const std::string& str)
|
||||
{
|
||||
size_t found;
|
||||
found = str.find_last_of("(/\\");
|
||||
return str.substr(found+1);
|
||||
}
|
||||
|
||||
} // end namespace g2::internal
|
||||
|
||||
|
||||
void initializeLogging(g2LogWorker *bgworker)
|
||||
{
|
||||
static bool once_only_signalhandler = false;
|
||||
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
|
||||
CHECK(!internal::isLoggingInitialized());
|
||||
CHECK(bgworker != nullptr);
|
||||
internal::g_logger_instance = bgworker;
|
||||
|
||||
if(false == once_only_signalhandler)
|
||||
namespace internal
|
||||
{
|
||||
internal::installSignalHandler();
|
||||
once_only_signalhandler = true;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
g2LogWorker* shutDownLogging() // safe to call multiple times
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
|
||||
g2LogWorker *backup = internal::g_logger_instance;
|
||||
internal::g_logger_instance = nullptr;
|
||||
return backup;
|
||||
}
|
||||
bool isLoggingInitialized(){
|
||||
return g_logger_instance != nullptr;
|
||||
}
|
||||
|
||||
g2LogWorker* HackToBeRemoved(){return internal::g_logger_instance;}
|
||||
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
|
||||
|
||||
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// The default, initial, handling to send a 'fatal' event to g2logworker
|
||||
// the caller will stay here, eternally, until the software is aborted
|
||||
void callFatalInitial(FatalMessage message)
|
||||
{
|
||||
internal::g_logger_instance->fatal(message);
|
||||
}
|
||||
// By default this function pointer goes to \ref callFatalInitial;
|
||||
void (*g_fatal_to_g2logworker_function_ptr)(FatalMessage) = callFatalInitial;
|
||||
|
||||
|
||||
|
||||
// Replaces the g2log.cpp/g_fatal_to_g2logworker_function_ptr through
|
||||
// g2log::changeFatalInitHandler
|
||||
void unitTestFatalInitHandler(g2::internal::FatalMessage fatal_message)
|
||||
{
|
||||
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_);
|
||||
}
|
||||
|
||||
// In case of unit-testing - a replacement 'fatal function' can be called
|
||||
void changeFatalInitHandlerForUnitTesting()
|
||||
{
|
||||
g_fatal_to_g2logworker_function_ptr = unitTestFatalInitHandler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{}
|
||||
|
||||
LogContractMessage::~LogContractMessage()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if(0 == expression_.compare(k_fatal_log_expression))
|
||||
// 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)
|
||||
{
|
||||
oss << "\n[ *******\tEXIT trigger caused by LOG(FATAL): \n\t";
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t";
|
||||
}
|
||||
log_entry_ = oss.str();
|
||||
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);
|
||||
#endif
|
||||
CHECK(!internal::isLoggingInitialized());
|
||||
CHECK(bgworker != nullptr);
|
||||
internal::g_logger_instance = bgworker;
|
||||
}
|
||||
|
||||
LogMessage::LogMessage(const std::string &file, const int line, const std::string& function, const std::string &level)
|
||||
: file_(file)
|
||||
, line_(line)
|
||||
, function_(function)
|
||||
, level_(level)
|
||||
|
||||
{}
|
||||
|
||||
|
||||
LogMessage::~LogMessage()
|
||||
{
|
||||
using namespace internal;
|
||||
std::ostringstream oss;
|
||||
const bool fatal = (0 == level_.compare("FATAL"));
|
||||
oss << level_ << " [" << splitFileName(file_);
|
||||
if(fatal)
|
||||
oss << " at: " << function_ ;
|
||||
oss << " L: " << line_ << "]\t";
|
||||
|
||||
const std::string str(stream_.str());
|
||||
if(!str.empty())
|
||||
g2LogWorker* shutDownLogging()
|
||||
{
|
||||
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_);
|
||||
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
|
||||
g2LogWorker *backup = internal::g_logger_instance;
|
||||
internal::g_logger_instance = nullptr;
|
||||
return backup;
|
||||
}
|
||||
|
||||
|
||||
if(fatal) // os_fatal is handled by crashhandlers
|
||||
namespace internal
|
||||
{
|
||||
{ // 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::g_logger_instance->save(log_entry_); // message saved
|
||||
}
|
||||
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_);
|
||||
}
|
||||
|
||||
// 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)
|
||||
g_fatal_to_g2logworker_function_ptr(message_);
|
||||
while(true){std::this_thread::sleep_for(std::chrono::seconds(1));}
|
||||
}
|
||||
// By default this function pointer goes to \ref fatalCall;
|
||||
void (*g_fatal_to_g2logworker_function_ptr)(FatalMessage) = fatalCallToLogger;
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace g2::internal
|
||||
} // end of namespace g2
|
||||
// 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
|
||||
} // g2
|
||||
|
@ -1,242 +0,0 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
*
|
||||
* Filename:g2log.h Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||
* at least in "spirit" from the following sources
|
||||
* 1. kjellkod.cc ;)
|
||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
||||
* 5. Various Q&A at StackOverflow
|
||||
* ********************************************* */
|
||||
|
||||
|
||||
#ifndef G2LOG_H
|
||||
#define G2LOG_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
#include <chrono>
|
||||
#include "g2logmessage.hpp"
|
||||
|
||||
|
||||
class g2LogWorker;
|
||||
|
||||
#if !(defined(__PRETTY_FUNCTION__))
|
||||
#define __PRETTY_FUNCTION__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
|
||||
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
||||
const int DEBUG = 0, INFO = 1, WARNING = 2, FATAL = 3;
|
||||
static const std::string k_fatal_log_expression = ""; // using LogContractMessage but no boolean expression
|
||||
|
||||
// GCC Predefined macros: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
|
||||
// and http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
|
||||
//
|
||||
// The ## macro is helpful as it gives compile time error in case there's a typo
|
||||
// Example: MYLEVEL doesn't exist so LOG(MYLEVEL) << "bla bla bla"; would
|
||||
// generate a compile error when it is rolled out by the
|
||||
// macro as G2_LOG_MYLEVEL, since "#define G2_LOG_MYLEVEL" doesn't exist
|
||||
|
||||
|
||||
// BELOW -- LOG stream syntax
|
||||
#define G2_LOG_DEBUG g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"DEBUG")
|
||||
#define G2_LOG_INFO g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"INFO")
|
||||
#define G2_LOG_WARNING g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"WARNING")
|
||||
#define G2_LOG_FATAL g2::internal::LogContractMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,k_fatal_log_expression)
|
||||
|
||||
// LOG(level) is the API for the stream log
|
||||
#define LOG(level) G2_LOG_##level.messageStream()
|
||||
|
||||
// conditional stream log
|
||||
#define LOG_IF(level, boolean_expression) \
|
||||
if(true == boolean_expression) \
|
||||
G2_LOG_##level.messageStream()
|
||||
|
||||
// Design By Contract, stream API. Throws std::runtime_eror if contract breaks
|
||||
#define CHECK(boolean_expression) \
|
||||
if (false == (boolean_expression)) \
|
||||
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, #boolean_expression).messageStream()
|
||||
|
||||
|
||||
// BELOW -- LOG "printf" syntax
|
||||
/**
|
||||
* For details please see this
|
||||
* 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
|
||||
%[flags][width][.precision][length]specifier
|
||||
SPECIFIERS
|
||||
----------
|
||||
%c character
|
||||
%d signed integers
|
||||
%i signed integers
|
||||
%e scientific notation, with a lowercase “e”
|
||||
%E scientific notation, with a uppercase “E”
|
||||
%f floating point
|
||||
%g use %e or %f, whichever is shorter
|
||||
%G use %E or %f, whichever is shorter
|
||||
%o octal
|
||||
%s a string of characters
|
||||
%u unsigned integer
|
||||
%x unsigned hexadecimal, with lowercase letters
|
||||
%X unsigned hexadecimal, with uppercase letters
|
||||
%p a pointer
|
||||
%n the argument shall be a pointer to an integer into which is placed the number of characters written so far
|
||||
|
||||
For flags, width, precision etc please see the above references.
|
||||
EXAMPLES:
|
||||
{
|
||||
LOGF(INFO, "Characters: %c %c \n", 'a', 65);
|
||||
LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long
|
||||
LOGF(INFO, "Preceding with blanks: %10d \n", 1977);
|
||||
LOGF(INFO, "Preceding with zeros: %010d \n", 1977);
|
||||
LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
|
||||
LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
|
||||
LOGF(INFO, "Width trick: %*d \n", 5, 10);
|
||||
LOGF(INFO, "%s \n", "A string");
|
||||
return 0;
|
||||
}
|
||||
And here is possible output
|
||||
: Characters: a A
|
||||
: Decimals: 1977 650000
|
||||
: Preceding with blanks: 1977
|
||||
: Preceding with zeros: 0000001977
|
||||
: Some different radixes: 100 64 144 0x64 0144
|
||||
: floats: 3.14 +3e+000 3.141600E+000
|
||||
: Width trick: 10
|
||||
: A string \endverbatim */
|
||||
#define G2_LOGF_INFO g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"INFO")
|
||||
#define G2_LOGF_DEBUG g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"DEBUG")
|
||||
#define G2_LOGF_WARNING g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"WARNING")
|
||||
#define G2_LOGF_FATAL g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,k_fatal_log_expression)
|
||||
|
||||
// LOGF(level,msg,...) is the API for the "printf" like log
|
||||
#define LOGF(level, printf_like_message, ...) \
|
||||
G2_LOGF_##level.messageSave(printf_like_message, __VA_ARGS__);
|
||||
|
||||
// conditional log printf syntax
|
||||
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
|
||||
if(true == boolean_expression) \
|
||||
G2_LOG_##level.messageSave(printf_like_message, __VA_ARGS__);
|
||||
|
||||
// Design By Contract, printf-like API syntax with variadic input parameters. Throws std::runtime_eror if contract breaks */
|
||||
#define CHECK_F(boolean_expression, printf_like_message, ...) \
|
||||
if (false == (boolean_expression)) \
|
||||
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,#boolean_expression).messageSave(printf_like_message, __VA_ARGS__);
|
||||
|
||||
|
||||
/** 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
|
||||
{
|
||||
/** 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
|
||||
* The \ref pointer to the g2LogWorker is owned by the instantiated \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* HackToBeRemoved();
|
||||
|
||||
// defined here but should't not have to be used outside the g2log
|
||||
namespace internal
|
||||
{
|
||||
/** By default the g2log will call g2LogWorker::fatal(...) which will abort() the system after flushing
|
||||
* the logs to file. This makes unit test of FATAL level cumbersome. A work around is to change the 'fatal call'
|
||||
* which can be done here */
|
||||
void changeFatalInitHandlerForUnitTesting();
|
||||
|
||||
// TODO: LogEntry och FatalMessage borde kunna slås ihop till samma!
|
||||
|
||||
/** 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_;
|
||||
};
|
||||
// Will trigger a FatalMessage sending
|
||||
struct FatalTrigger
|
||||
{
|
||||
FatalTrigger(const FatalMessage& message);
|
||||
~FatalTrigger();
|
||||
FatalMessage message_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Log message for 'printf-like' or stream logging, it's a temporary message constructions
|
||||
class LogMessage
|
||||
{
|
||||
public:
|
||||
LogMessage(const std::string &file, const int line, const std::string& function, const std::string &level);
|
||||
virtual ~LogMessage(); // at destruction will 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
|
||||
//
|
||||
//If the compiler does not support attributes, disable them
|
||||
#ifndef __GNUC__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
// Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first
|
||||
// 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 std::string 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_;
|
||||
};
|
||||
} // end namespace internal
|
||||
} // end namespace g2
|
||||
|
||||
#endif // G2LOG_H
|
190
g2log/src/g2log.hpp
Normal file
190
g2log/src/g2log.hpp
Normal file
@ -0,0 +1,190 @@
|
||||
/** ==========================================================================
|
||||
* 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.
|
||||
* ============================================================================
|
||||
*
|
||||
* Filename:g2log.hpp Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||
* at least in "spirit" from the following sources
|
||||
* 1. kjellkod.cc ;)
|
||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
||||
* 5. Various Q&A at StackOverflow
|
||||
* ********************************************* */
|
||||
|
||||
|
||||
#ifndef G2LOG_H
|
||||
#define G2LOG_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include "g2logmessage.hpp"
|
||||
|
||||
class g2LogWorker;
|
||||
|
||||
#if !(defined(__PRETTY_FUNCTION__))
|
||||
#define __PRETTY_FUNCTION__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/** 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
|
||||
{
|
||||
|
||||
/** 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
|
||||
* 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();
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
/** 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 g2
|
||||
|
||||
|
||||
#define INTERNAL_LOG_MESSAGE(level) g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
|
||||
|
||||
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
|
||||
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, boolean_expression)
|
||||
|
||||
|
||||
|
||||
// LOG(level) is the API for the stream log
|
||||
#define LOG(level) if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream()
|
||||
|
||||
// 'Conditional' stream log
|
||||
#define LOG_IF(level, boolean_expression) \
|
||||
if(true == boolean_expression) \
|
||||
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageStream()
|
||||
|
||||
// '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()
|
||||
|
||||
|
||||
/** For details please see this
|
||||
* 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
|
||||
%[flags][width][.precision][length]specifier
|
||||
SPECIFIERS
|
||||
----------
|
||||
%c character
|
||||
%d signed integers
|
||||
%i signed integers
|
||||
%e scientific notation, with a lowercase “e”
|
||||
%E scientific notation, with a uppercase “E”
|
||||
%f floating point
|
||||
%g use %e or %f, whichever is shorter
|
||||
%G use %E or %f, whichever is shorter
|
||||
%o octal
|
||||
%s a string of characters
|
||||
%u unsigned integer
|
||||
%x unsigned hexadecimal, with lowercase letters
|
||||
%X unsigned hexadecimal, with uppercase letters
|
||||
%p a pointer
|
||||
%n the argument shall be a pointer to an integer into which is placed the number of characters written so far
|
||||
|
||||
For flags, width, precision etc please see the above references.
|
||||
EXAMPLES:
|
||||
{
|
||||
LOGF(INFO, "Characters: %c %c \n", 'a', 65);
|
||||
LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long
|
||||
LOGF(INFO, "Preceding with blanks: %10d \n", 1977);
|
||||
LOGF(INFO, "Preceding with zeros: %010d \n", 1977);
|
||||
LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
|
||||
LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
|
||||
LOGF(INFO, "Width trick: %*d \n", 5, 10);
|
||||
LOGF(INFO, "%s \n", "A string");
|
||||
return 0;
|
||||
}
|
||||
And here is possible output
|
||||
: Characters: a A
|
||||
: Decimals: 1977 650000
|
||||
: Preceding with blanks: 1977
|
||||
: Preceding with zeros: 0000001977
|
||||
: Some different radixes: 100 64 144 0x64 0144
|
||||
: floats: 3.14 +3e+000 3.141600E+000
|
||||
: Width trick: 10
|
||||
: A string \endverbatim */
|
||||
#define LOGF(level, printf_like_message, ...) \
|
||||
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
|
||||
// Conditional log printf syntax
|
||||
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
|
||||
if(true == boolean_expression) \
|
||||
if(g2::logLevel(level)) INTERNAL_LOG_MESSAGE(level).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
|
||||
// Design By Contract, printf-like API syntax with variadic input parameters.
|
||||
// Throws std::runtime_eror if contract breaks
|
||||
#define CHECK_F(boolean_expression, printf_like_message, ...) \
|
||||
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).messageSave(printf_like_message, ##__VA_ARGS__)
|
||||
|
||||
#endif // G2LOG_H
|
45
g2log/src/g2loglevels.cpp
Normal file
45
g2log/src/g2loglevels.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2loglevels.cpp Part of Framework for Logging and Design By Contract
|
||||
* Created: 2012 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2loglevels.hpp"
|
||||
#include <atomic>
|
||||
|
||||
|
||||
namespace g2
|
||||
{
|
||||
namespace internal {
|
||||
std::atomic<bool> g_log_level_status[4]; // DEBUG, INFO, WARNING, FATAL
|
||||
} // internal
|
||||
|
||||
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
void setLogLevel(LEVELS log_level, bool enabled)
|
||||
{
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
(internal::g_log_level_status[level]).store(enabled, std::memory_order_release);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool logLevel(LEVELS log_level)
|
||||
{
|
||||
#ifdef G2_DYNAMIC_LOGGING
|
||||
int level = log_level.value;
|
||||
CHECK((level >= DEBUG.value) && (level <= FATAL.value));
|
||||
bool status = (internal::g_log_level_status[level]).load(std::memory_order_acquire);
|
||||
return status;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // g2
|
36
g2log/src/g2loglevels.hpp
Normal file
36
g2log/src/g2loglevels.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2loglevels.hpp Part of Framework for Logging and Design By Contract
|
||||
* Created: 2012 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#ifndef G2_LOG_LEVELS_HPP_
|
||||
#define G2_LOG_LEVELS_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
||||
struct LEVELS
|
||||
{
|
||||
const int value;
|
||||
const std::string text;
|
||||
} const DEBUG = {0, "DEBUG"}, INFO = {1, "INFO"}, WARNING = {2, "WARNING"}, FATAL = {3, "FATAL"};
|
||||
|
||||
namespace g2 {
|
||||
#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
|
||||
// WARNING: This should be used AFTER \ref g2::initializeLogging
|
||||
void setLogLevel(LEVELS level, bool enabled_status);
|
||||
#endif
|
||||
bool logLevel(LEVELS level);
|
||||
|
||||
} // g2
|
||||
|
||||
|
||||
#endif // G2_LOG_LEVELS_HPP_
|
202
g2log/src/g2logmessage.cpp
Normal file
202
g2log/src/g2logmessage.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/** ==========================================================================
|
||||
* 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, "")
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{}
|
||||
|
||||
// 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::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
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{}
|
||||
|
||||
LogContractMessage::~LogContractMessage()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "\n[ *******\tEXIT trigger caused by broken Contract: CHECK(" << expression_ << ")\n\t";
|
||||
log_entry_ = oss.str();
|
||||
}
|
||||
|
||||
} // internal
|
||||
} // g2
|
@ -1,19 +1,111 @@
|
||||
/*
|
||||
* File: g2logmessage.h
|
||||
* Author: kjell
|
||||
*
|
||||
* Created on July 21, 2013, 1:00 PM
|
||||
*/
|
||||
/** ==========================================================================
|
||||
* 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 G2LOGMESSAGE_H
|
||||
#define G2LOGMESSAGE_H
|
||||
|
||||
namespace g2 {
|
||||
namespace internal {
|
||||
typedef const std::string& LogEntry;
|
||||
}
|
||||
}
|
||||
#ifndef G2_LOG_MESSAGE_HPP
|
||||
#define G2_LOG_MESSAGE_HPP
|
||||
|
||||
|
||||
#endif /* G2LOGMESSAGE_H */
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
#include <memory>
|
||||
|
||||
#include "g2log.hpp"
|
||||
#include "g2loglevels.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();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
@ -9,15 +9,15 @@
|
||||
* PUBLIC DOMAIN and Not under copywrite protection. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2logworker.h"
|
||||
#include "g2logworker.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "active.hpp"
|
||||
#include "g2log.h"
|
||||
#include "g2log.hpp"
|
||||
#include "g2time.hpp"
|
||||
#include "g2future.h"
|
||||
#include "crashhandler.hpp"
|
||||
@ -109,5 +109,5 @@ void g2LogWorker::addWrappedSink(std::shared_ptr<g2::internal::SinkWrapper> sink
|
||||
|
||||
DefaultFileLogger::DefaultFileLogger(const std::string& log_prefix, const std::string& log_directory)
|
||||
: worker(g2LogWorker::createWithNoSink())
|
||||
, sink(worker->addSink(std2::make_unique<g2::g2FileSink>(log_prefix, log_directory), &g2FileSink::fileWrite)) {
|
||||
, sink(worker->addSink(std2::make_unique<g2::FileSink>(log_prefix, log_directory), &FileSink::fileWrite)) {
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "g2log.h"
|
||||
#include "g2log.hpp"
|
||||
#include "g2sinkwrapper.h"
|
||||
#include "g2sinkhandle.h"
|
||||
#include "g2filesink.hpp"
|
||||
@ -31,7 +31,7 @@ namespace g2 {
|
||||
struct DefaultFileLogger {
|
||||
DefaultFileLogger(const std::string& log_prefix, const std::string& log_directory);
|
||||
std::unique_ptr<g2LogWorker> worker;
|
||||
std::unique_ptr<g2::SinkHandle<g2::g2FileSink>> sink;
|
||||
std::unique_ptr<g2::SinkHandle<g2::FileSink>> sink;
|
||||
};
|
||||
}
|
||||
|
@ -44,10 +44,7 @@ namespace g2 {
|
||||
{ }
|
||||
|
||||
virtual ~Sink() {
|
||||
std::cout << "Sink<T> in destructor\n";
|
||||
_bg.reset();
|
||||
std::cout << "Sink<T> Active object was reset. in destructor\n";
|
||||
|
||||
_bg.reset(); // TODO: to remove
|
||||
}
|
||||
|
||||
void send(LogEntry msg) override {
|
||||
|
@ -4,8 +4,8 @@
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
#include "g2logworker.h"
|
||||
#include "g2log.h"
|
||||
#include "g2logworker.hpp"
|
||||
#include "g2log.hpp"
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
|
||||
@ -52,7 +52,7 @@ int main(int argc, char** argv)
|
||||
g2::initializeLogging(logger_n_handle.worker.get());
|
||||
|
||||
|
||||
std::future<std::string> log_file_name = logger_n_handle.sink->call(&g2::g2FileSink::fileName);
|
||||
std::future<std::string> log_file_name = logger_n_handle.sink->call(&g2::FileSink::fileName);
|
||||
std::cout << "*** This is an example of g2log " << std::endl;
|
||||
std::cout << "*** It WILL exit by a FATAL trigger in the end" << std::endl;
|
||||
std::cout << "*** Please see the generated log and compare to " << std::endl;
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
|
||||
#include <thread>
|
||||
#include "g2log.h"
|
||||
#include "g2logworker.h"
|
||||
#include "g2log.hpp"
|
||||
#include "g2logworker.hpp"
|
||||
#include "testing_helpers.h"
|
||||
|
||||
using namespace testing_helpers;
|
||||
@ -24,9 +24,8 @@ using namespace testing_helpers;
|
||||
|
||||
namespace { // anonymous
|
||||
const char* name_path_1 = "./some_fake_DirectoryOrName_1_";
|
||||
const char* name_path_2 = "./some_fake_DirectoryOrName_3_";
|
||||
g2LogWorker* g_logger_ptr = nullptr;
|
||||
g2::SinkHandle<g2::g2FileSink>* g_filesink_handler = nullptr;
|
||||
g2::SinkHandle<g2::FileSink>* g_filesink_handler = nullptr;
|
||||
LogFileCleaner* g_cleaner_ptr = nullptr;
|
||||
|
||||
bool isTextAvailableInContent(const std::string &total_text, std::string msg_to_find) {
|
||||
@ -54,7 +53,7 @@ namespace { // anonymous
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
{
|
||||
add_count = std::to_string(++count) + "_";
|
||||
auto future_new_log = g_filesink_handler->call(&g2::g2FileSink::changeLogFile, new_file_to_create + add_count);
|
||||
auto future_new_log = g_filesink_handler->call(&g2::FileSink::changeLogFile, new_file_to_create + add_count);
|
||||
auto new_log = future_new_log.get();
|
||||
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
|
||||
return new_log;
|
||||
@ -63,14 +62,14 @@ namespace { // anonymous
|
||||
}
|
||||
|
||||
std::string setLogName(std::string new_file_to_create) {
|
||||
auto future_new_log = g_filesink_handler->call(&g2::g2FileSink::changeLogFile, new_file_to_create);
|
||||
auto future_new_log = g_filesink_handler->call(&g2::FileSink::changeLogFile, new_file_to_create);
|
||||
auto new_log = future_new_log.get();
|
||||
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
|
||||
return new_log;
|
||||
}
|
||||
|
||||
std::string getLogName() {
|
||||
return g_filesink_handler->call(&g2::g2FileSink::fileName).get();
|
||||
return g_filesink_handler->call(&g2::FileSink::fileName).get();
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
@ -90,7 +89,7 @@ TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) {
|
||||
}
|
||||
|
||||
TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
|
||||
auto old_log = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
|
||||
auto old_log = g_filesink_handler->call(&g2::FileSink::fileName).get();
|
||||
if (!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log);
|
||||
|
||||
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
|
||||
@ -131,7 +130,7 @@ int main(int argc, char *argv[]) {
|
||||
auto logger = g2LogWorker::createWithDefaultLogger("ReplaceLogFile", name_path_1);
|
||||
g_logger_ptr = logger.worker.get();
|
||||
g_filesink_handler = logger.sink.get();
|
||||
last_log_file = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
|
||||
last_log_file = g_filesink_handler->call(&g2::FileSink::fileName).get();
|
||||
cleaner.addLogToClean(last_log_file);
|
||||
|
||||
|
||||
@ -141,7 +140,7 @@ int main(int argc, char *argv[]) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return_value = RUN_ALL_TESTS();
|
||||
|
||||
last_log_file = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
|
||||
last_log_file = g_filesink_handler->call(&g2::FileSink::fileName).get();
|
||||
std::cout << "log file at: " << last_log_file << std::endl;
|
||||
//g2::shutDownLogging();
|
||||
}
|
||||
|
@ -5,8 +5,8 @@
|
||||
* ============================================================================*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "g2log.h"
|
||||
#include "g2logworker.h"
|
||||
#include "g2log.hpp"
|
||||
#include "g2logworker.hpp"
|
||||
#include "testing_helpers.h"
|
||||
|
||||
#include <memory>
|
||||
@ -17,85 +17,89 @@
|
||||
#include <chrono>
|
||||
|
||||
namespace {
|
||||
const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time
|
||||
const std::string log_directory = "./";
|
||||
const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time
|
||||
const std::string log_directory = "./";
|
||||
|
||||
bool verifyContent(const std::string &total_text, std::string msg_to_find) {
|
||||
std::string content(total_text);
|
||||
size_t location = content.find(msg_to_find);
|
||||
return (location != std::string::npos);
|
||||
}
|
||||
|
||||
std::string readFileToText(std::string filename) {
|
||||
std::ifstream in;
|
||||
in.open(filename.c_str(), std::ios_base::in);
|
||||
if (!in.is_open()) {
|
||||
return {}; // error just return empty string - test will 'fault'
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << in.rdbuf();
|
||||
return oss.str();
|
||||
}
|
||||
bool verifyContent(const std::string &total_text, std::string msg_to_find) {
|
||||
std::string content(total_text);
|
||||
size_t location = content.find(msg_to_find);
|
||||
return (location != std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
std::string readFileToText(std::string filename) {
|
||||
std::ifstream in;
|
||||
in.open(filename.c_str(), std::ios_base::in);
|
||||
if (!in.is_open()) {
|
||||
return
|
||||
{ }; // error just return empty string - test will 'fault'
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << in.rdbuf();
|
||||
return oss.str();
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
|
||||
using namespace testing_helpers;
|
||||
// LOG
|
||||
TEST(IO_RestoreFileLogger, Expecting_Fine_To_ShutDownMultipleTimes)
|
||||
{
|
||||
RestoreFileLogger logger1("");
|
||||
// här kraschar det
|
||||
// 1. gör även ett test med massvis med sinks. tex när de destrueras så sätter de
|
||||
// en atomic boolean till true
|
||||
// 2. cleanup av kod.
|
||||
// 3. message borde vara en strukt istället. Se på den nya koden.
|
||||
// 4. kan jag förenkla min kod?
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds(1000));
|
||||
RestoreFileLogger logger2("./");
|
||||
|
||||
|
||||
TEST(IO_RestoreFileLogger, Expecting_Fine_To_ShutDownMultipleTimes) {
|
||||
{
|
||||
//RestoreFileLogger logger1("");
|
||||
ScopedLogger scope;
|
||||
}
|
||||
{
|
||||
ScopedLogger scope;
|
||||
//RestoreFileLogger logger2("./");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(LOGTest, LOG) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG(INFO) << "test LOG(INFO)";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, "test LOG(INFO)"));
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG(INFO) << "test LOG(INFO)";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, "test LOG(INFO)"));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
const std::string t_info = "test INFO ";
|
||||
const std::string t_info2 = "test INFO 123";
|
||||
const std::string t_debug = "test DEBUG ";
|
||||
const std::string t_debug2 = "test DEBUG 1.123456";
|
||||
const std::string t_warning = "test WARNING ";
|
||||
const std::string t_warning2 = "test WARNING yello";
|
||||
const std::string t_info = "test INFO ";
|
||||
const std::string t_info2 = "test INFO 123";
|
||||
const std::string t_debug = "test DEBUG ";
|
||||
const std::string t_debug2 = "test DEBUG 1.123456";
|
||||
const std::string t_warning = "test WARNING ";
|
||||
const std::string t_warning2 = "test WARNING yello";
|
||||
}
|
||||
|
||||
// printf-type log
|
||||
|
||||
TEST(LogTest, LOG_F) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::cout << "logfilename: " << logger.logFile() << std::flush << std::endl;
|
||||
|
||||
LOGF(INFO, std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF(DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
LOGF(WARNING, std::string(t_warning + "%s").c_str(), "yello");
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_debug2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_warning2));
|
||||
TEST(LogTest, LOG_F) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::cout << "logfilename: " << logger.logFile() << std::flush << std::endl;
|
||||
|
||||
LOGF(INFO, std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF(DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
LOGF(WARNING, std::string(t_warning + "%s").c_str(), "yello");
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_debug2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_warning2));
|
||||
}
|
||||
|
||||
|
||||
@ -103,218 +107,230 @@ TEST(LogTest, LOG_F) {
|
||||
|
||||
// stream-type log
|
||||
|
||||
|
||||
TEST(LogTest, LOG) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG(INFO) << t_info << 123;
|
||||
LOG(DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
LOG(WARNING) << t_warning << "yello";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_debug2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_warning2));
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG(INFO) << t_info << 123;
|
||||
LOG(DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
LOG(WARNING) << t_warning << "yello";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_debug2));
|
||||
ASSERT_TRUE(verifyContent(file_content, t_warning2));
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOG_F_IF) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF_IF(DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_FALSE(verifyContent(file_content, t_debug2));
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF_IF(DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_FALSE(verifyContent(file_content, t_debug2));
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOG_IF) {
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG_IF(INFO, (2 == 2)) << t_info << 123;
|
||||
LOG_IF(DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_FALSE(verifyContent(file_content, t_debug2));
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreFileLogger logger(log_directory);
|
||||
LOG_IF(INFO, (2 == 2)) << t_info << 123;
|
||||
LOG_IF(DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
ASSERT_FALSE(verifyContent(file_content, t_debug2));
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOGF__FATAL) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOGF(FATAL, "This message should throw %d", 0);
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
std::cerr << file_content << std::endl << std::flush;
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOGF(FATAL, "This message should throw %d", 0);
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
std::cerr << file_content << std::endl << std::flush;
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOG_FATAL) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG(FATAL) << "This message should throw";
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG(FATAL) << "This message should throw";
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOGF_IF__FATAL) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOGF_IF(FATAL, (2 < 3), "This message%sshould throw", " ");
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOGF_IF(FATAL, (2 < 3), "This message%sshould throw", " ");
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOG_IF__FATAL) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
|
||||
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw";
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw") &&
|
||||
(false == verifyContent(file_content, "This message should NOT be written"))) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
|
||||
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw";
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw") &&
|
||||
(false == verifyContent(file_content, "This message should NOT be written"))) {
|
||||
SUCCEED();
|
||||
return;
|
||||
} else {
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception at ALL";
|
||||
}
|
||||
|
||||
|
||||
TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
logger.reset();
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
logger.reset();
|
||||
SUCCEED();
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
logger.reset();
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
logger.reset();
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
// CHECK_F
|
||||
|
||||
|
||||
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
CHECK(1 == 2);
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
try {
|
||||
CHECK(1 == 2);
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL")) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
|
||||
|
||||
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This message is added to throw %s and %s";
|
||||
std::string msg2 = "This message is added to throw message and log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str());
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2)) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This message is added to throw %s and %s";
|
||||
std::string msg2 = "This message is added to throw message and log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str());
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2)) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
|
||||
|
||||
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This message is added to throw %s and %s";
|
||||
std::string msg2 = "This message is added to throw message and log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK(1 >= 2) << msg2;
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2)) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This message is added to throw %s and %s";
|
||||
std::string msg2 = "This message is added to throw message and log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK(1 >= 2) << msg2;
|
||||
} catch (std::exception const &e) {
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if (verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2)) {
|
||||
SUCCEED();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ADD_FAILURE() << "Didn't throw exception as expected";
|
||||
}
|
||||
|
||||
|
||||
TEST(CHECK, CHECK_ThatWontThrow) {
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This %s should never appear in the %s";
|
||||
std::string msg2 = "This message should never appear in the log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK(1 == 1);
|
||||
CHECK_F(1 == 1, msg.c_str(), "message", "log");
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
ADD_FAILURE() << "Should never have thrown";
|
||||
}
|
||||
RestoreFileLogger logger(log_directory);
|
||||
std::string msg = "This %s should never appear in the %s";
|
||||
std::string msg2 = "This message should never appear in the log";
|
||||
std::string arg1 = "message";
|
||||
std::string arg2 = "log";
|
||||
try {
|
||||
CHECK(1 == 1);
|
||||
CHECK_F(1 == 1, msg.c_str(), "message", "log");
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
ADD_FAILURE() << "Should never have thrown";
|
||||
}
|
||||
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
ASSERT_FALSE(verifyContent(file_content, msg2));
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
ASSERT_FALSE(verifyContent(file_content, msg2));
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <chrono>
|
||||
|
||||
#include "testing_helpers.h"
|
||||
#include "g2logworker.h"
|
||||
#include "g2logworker.hpp"
|
||||
|
||||
namespace {
|
||||
g2LogWorker* g_logger_ptr = nullptr;
|
||||
@ -18,8 +18,12 @@ using namespace testing_helpers;
|
||||
using namespace std;
|
||||
|
||||
TEST(Sink, TestSetup) {
|
||||
ScopedLogger scope;
|
||||
ASSERT_EQ(g_logger_ptr, scope._previousWorker);
|
||||
{
|
||||
ScopedLogger scope;
|
||||
ASSERT_EQ(g_logger_ptr, scope._previousWorker);
|
||||
}
|
||||
ScopedLogger scope;
|
||||
ASSERT_EQ(g_logger_ptr, scope._previousWorker);
|
||||
}
|
||||
|
||||
TEST(Sink, OneSink) {
|
||||
|
@ -2,52 +2,20 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
#include "testing_helpers.h"
|
||||
#include "g2log.h"
|
||||
#include "g2logworker.h"
|
||||
#include "g2log.hpp"
|
||||
#include "g2logworker.hpp"
|
||||
#include "g2filesink.hpp"
|
||||
#include "std2_make_unique.hpp"
|
||||
|
||||
using namespace std;
|
||||
namespace {
|
||||
g2LogWorker* oldworker = nullptr;
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace testing_helpers {
|
||||
|
||||
bool removeFile(std::string path_to_file) {
|
||||
return (0 == std::remove(path_to_file.c_str()));
|
||||
}
|
||||
|
||||
|
||||
RestoreFileLogger::RestoreFileLogger(std::string directory)
|
||||
: logger_(g2LogWorker::createWithNoSink()) {
|
||||
using namespace g2;
|
||||
auto filehandler = logger_->addSink(std2::make_unique<g2FileSink>("UNIT_TEST_LOGGER", directory), &g2FileSink::fileWrite);
|
||||
|
||||
oldworker = g2::shutDownLogging();
|
||||
initializeLogging(logger_.get());
|
||||
internal::changeFatalInitHandlerForUnitTesting();
|
||||
LOG(INFO) << "Restore logger test ";
|
||||
auto filename = filehandler->call(&g2FileSink::fileName);
|
||||
if (!filename.valid()) ADD_FAILURE();
|
||||
log_file_ = filename.get();
|
||||
}
|
||||
|
||||
RestoreFileLogger::~RestoreFileLogger() {
|
||||
g2::shutDownLogging();
|
||||
reset();
|
||||
if (nullptr != oldworker) g2::initializeLogging(oldworker);
|
||||
if (!removeFile(log_file_))
|
||||
ADD_FAILURE();
|
||||
}
|
||||
|
||||
void RestoreFileLogger::reset() {
|
||||
logger_.reset();
|
||||
}
|
||||
|
||||
size_t LogFileCleaner::size() {
|
||||
|
||||
size_t LogFileCleaner::size() {
|
||||
return logs_to_clean_.size();
|
||||
}
|
||||
|
||||
@ -70,6 +38,9 @@ namespace testing_helpers {
|
||||
logs_to_clean_.push_back(path_to_log);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ScopedLogger::ScopedLogger()
|
||||
: _previousWorker(g2::shutDownLogging())
|
||||
, _currentWorker(g2LogWorker::createWithNoSink()) {
|
||||
@ -77,17 +48,41 @@ namespace testing_helpers {
|
||||
}
|
||||
|
||||
ScopedLogger::~ScopedLogger() {
|
||||
g2::shutDownLogging();
|
||||
|
||||
if (nullptr != oldworker) {
|
||||
g2::initializeLogging(oldworker);
|
||||
}
|
||||
auto* current = g2::shutDownLogging();
|
||||
CHECK(current == _currentWorker.get());
|
||||
if (nullptr != _previousWorker) {
|
||||
g2::initializeLogging(_previousWorker);
|
||||
}
|
||||
}
|
||||
|
||||
g2LogWorker* ScopedLogger::get() {
|
||||
return _currentWorker.get();
|
||||
return _currentWorker.get();
|
||||
}
|
||||
|
||||
|
||||
RestoreFileLogger::RestoreFileLogger(std::string directory)
|
||||
: scope_(new ScopedLogger)
|
||||
{
|
||||
using namespace g2;
|
||||
auto filehandler = scope_->get()->addSink(std2::make_unique<FileSink>("UNIT_TEST_LOGGER", directory), &FileSink::fileWrite);
|
||||
|
||||
internal::changeFatalInitHandlerForUnitTesting();
|
||||
LOG(INFO) << "Restore logger test ";
|
||||
auto filename = filehandler->call(&FileSink::fileName);
|
||||
if (!filename.valid()) ADD_FAILURE();
|
||||
log_file_ = filename.get();
|
||||
}
|
||||
|
||||
RestoreFileLogger::~RestoreFileLogger() {
|
||||
scope_.reset();
|
||||
if (!removeFile(log_file_))
|
||||
ADD_FAILURE();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // testing_helpers
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include "g2logworker.h"
|
||||
#include "g2logworker.hpp"
|
||||
|
||||
namespace testing_helpers {
|
||||
|
||||
@ -54,35 +54,12 @@ private:
|
||||
std::mutex g_mutex;
|
||||
public:
|
||||
size_t size();
|
||||
|
||||
LogFileCleaner() {
|
||||
}
|
||||
LogFileCleaner() {}
|
||||
virtual ~LogFileCleaner();
|
||||
void addLogToClean(std::string path_to_log);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** RAII temporarily replace of logger
|
||||
* and restoration of original logger at scope end*/
|
||||
struct RestoreFileLogger {
|
||||
explicit RestoreFileLogger(std::string directory);
|
||||
~RestoreFileLogger();
|
||||
void reset();
|
||||
std::unique_ptr<g2LogWorker> logger_;
|
||||
|
||||
template<typename Call, typename ... Args >
|
||||
typename std::result_of<Call(Args...)>::type callToLogger(Call call, Args&&... args) {
|
||||
auto func = std::bind(call, logger_.get(), std::forward<Args>(args)...);
|
||||
return func();
|
||||
}
|
||||
|
||||
std::string logFile() { return log_file_; }
|
||||
private:
|
||||
std::string log_file_;
|
||||
};
|
||||
|
||||
|
||||
struct ScopedLogger {
|
||||
ScopedLogger();
|
||||
virtual ~ScopedLogger();
|
||||
@ -93,6 +70,33 @@ struct ScopedLogger {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** RAII temporarily replace of logger
|
||||
* and restoration of original logger at scope end*/
|
||||
struct RestoreFileLogger {
|
||||
explicit RestoreFileLogger(std::string directory);
|
||||
~RestoreFileLogger();
|
||||
|
||||
std::unique_ptr<ScopedLogger> scope_;
|
||||
void reset(){ scope_.reset();}
|
||||
|
||||
|
||||
template<typename Call, typename ... Args >
|
||||
typename std::result_of<Call(Args...)>::type callToLogger(Call call, Args&&... args) {
|
||||
auto func = std::bind(call, scope_->get(), std::forward<Args>(args)...);
|
||||
return func();
|
||||
}
|
||||
|
||||
std::string logFile() { return log_file_; }
|
||||
private:
|
||||
std::string log_file_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef std::shared_ptr<std::atomic<bool>> AtomicBoolPtr;
|
||||
typedef std::shared_ptr<std::atomic<int>> AtomicIntPtr;
|
||||
struct ScopedSetTrue {
|
||||
|
Loading…
x
Reference in New Issue
Block a user