Dynamic sinks... all unit test working (although they should be consolidated). name-file manual merge from g2log-dev

This commit is contained in:
KjellKod 2013-10-04 22:14:35 -06:00
parent ad998db248
commit a59c837fd2
23 changed files with 1344 additions and 1076 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ struct PretendToBeCopyable
void operator()() { move_only_(); } // execute
private:
Moveable move_only_;
mutable Moveable move_only_;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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