incremental
diff --git a/g3log/crashhandler_unix.cpp b/g3log/crashhandler_unix.cpp
new file mode 100644
index 0000000..3e1b7fe
--- /dev/null
+++ b/g3log/crashhandler_unix.cpp
@@ -0,0 +1,274 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#include "g3log/crashhandler.hpp"
+#include "g3log/logmessage.hpp"
+#include "g3log/logcapture.hpp"
+#include "g3log/loglevels.hpp"
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
+#error "crashhandler_unix.cpp used but it's a windows system"
+#endif
+
+
+#include <csignal>
+#include <cstring>
+#include <unistd.h>
+#include <execinfo.h>
+#include <cxxabi.h>
+#include <cstdlib>
+#include <sstream>
+#include <iostream>
+#include <thread>
+#include <atomic>
+#include <map>
+#include <mutex>
+
+// Linux/Clang, OSX/Clang, OSX/gcc
+#if (defined(__clang__) || defined(__APPLE__))
+#include <sys/ucontext.h>
+#else
+#include <ucontext.h>
+#endif
+
+
+namespace {
+
+ const std::map<int, std::string> kSignals = {
+ {SIGABRT, "SIGABRT"},
+ {SIGFPE, "SIGFPE"},
+ {SIGILL, "SIGILL"},
+ {SIGSEGV, "SIGSEGV"},
+ {SIGTERM, "SIGTERM"},
+ };
+
+ std::map<int, std::string> gSignals = kSignals;
+
+
+ bool shouldDoExit() {
+ static std::atomic<uint64_t> firstExit{0};
+ auto const count = firstExit.fetch_add(1, std::memory_order_relaxed);
+ return (0 == count);
+ }
+
+ void restoreSignalHandler(int signal_number) {
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ 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);
+#endif
+ }
+
+
+ // Dump of stack,. then exit through g3log 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 signalHandler(int signal_number, siginfo_t* info, void* unused_context) {
+
+ // Only one signal will be allowed past this point
+ if (false == shouldDoExit()) {
+ while (true) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ }
+ }
+
+ using namespace g3::internal;
+ {
+ const auto dump = stackdump();
+ std::ostringstream fatal_stream;
+ const auto fatal_reason = exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
+ fatal_stream << "Received fatal signal: " << fatal_reason;
+ fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
+ fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
+ LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
+ trigger.stream() << fatal_stream.str();
+ } // message sent to g3LogWorker
+ // wait to die
+ }
+
+
+
+ //
+ // Installs FATAL signal handler that is enough to handle most fatal events
+ // on *NIX systems
+ void installSignalHandler() {
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ struct sigaction action;
+ memset(&action, 0, sizeof (action));
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = &signalHandler; // 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
+ for (const auto& sig_pair : gSignals) {
+ if (sigaction(sig_pair.first, &action, nullptr) < 0) {
+ const std::string error = "sigaction - " + sig_pair.second;
+ perror(error.c_str());
+ }
+ }
+#endif
+ }
+
+
+
+} // end anonymous namespace
+
+
+
+
+
+
+// Redirecting and using signals. In case of fatal signals g3log should log the fatal signal
+// and flush the log queue and then "rethrow" the signal to exit
+namespace g3 {
+ // 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 {
+
+ bool shouldBlockForFatalHandling() {
+ return true; // For windows we will after fatal processing change it to false
+ }
+
+
+ /// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one
+ /// i.e. the latter case is only for Windows and test purposes
+ std::string stackdump(const char* rawdump) {
+ if (nullptr != rawdump && !std::string(rawdump).empty()) {
+ return {rawdump};
+ }
+
+ 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, static_cast<int>(size)); // overwrite sigaction with caller's address
+
+ // dump stack: skip first frame, since that is here
+ std::ostringstream oss;
+ for (size_t idx = 1; idx < size && messages != nullptr; ++idx) {
+ char* mangled_name = 0, *offset_begin = 0, *offset_end = 0;
+ // find parantheses and +address offset surrounding mangled name
+ for (char* p = messages[idx]; *p; ++p) {
+ if (*p == '(') {
+ mangled_name = p;
+ } else if (*p == '+') {
+ offset_begin = p;
+ } else if (*p == ')') {
+ offset_end = p;
+ break;
+ }
+ }
+
+ // if the line could be processed, attempt to demangle the symbol
+ if (mangled_name && offset_begin && offset_end &&
+ mangled_name < offset_begin) {
+ *mangled_name++ = '\0';
+ *offset_begin++ = '\0';
+ *offset_end++ = '\0';
+
+ int status;
+ char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
+ // if demangling is successful, output the demangled function name
+ if (status == 0) {
+ oss << "\n\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
+ oss << offset_begin << offset_end << std::endl;
+ }// otherwise, output the mangled function name
+ else {
+ oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
+ oss << offset_begin << offset_end << std::endl;
+ }
+ free(real_name); // mallocated by abi::__cxa_demangle(...)
+ } else {
+ // no demangling done -- just dump the whole line
+ oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
+ }
+ } // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
+ free(messages);
+ return oss.str();
+ }
+
+
+
+ /// string representation of signal ID
+ std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
+
+ int signal_number = static_cast<int>(fatal_id);
+ 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 << ") for " << level.text;
+ return oss.str();
+ }
+ }
+
+
+
+ // Triggered by g3log->g3LogWorker 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(const LEVELS& level, g3::SignalType fatal_signal_id) {
+ const int signal_number = static_cast<int>(fatal_signal_id);
+ restoreSignalHandler(signal_number);
+ std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush;
+
+
+ kill(getpid(), signal_number);
+ exit(signal_number);
+
+ }
+ } // end g3::internal
+
+
+ // This will override the default signal handler setup and instead
+ // install a custom set of signals to handle
+ void overrideSetupSignals(const std::map<int, std::string> overrideSignals) {
+ static std::mutex signalLock;
+ std::lock_guard<std::mutex> guard(signalLock);
+ for (const auto& sig : gSignals) {
+ restoreSignalHandler(sig.first);
+ }
+
+ gSignals = overrideSignals;
+ installCrashHandler(); // installs all the signal handling for gSignals
+ }
+
+ // restores the signal handler back to default
+ void restoreSignalHandlerToDefault() {
+ overrideSetupSignals(kSignals);
+ }
+
+
+ // installs the signal handling for whatever signal set that is currently active
+ // If you want to setup your own signal handling then
+ // You should instead call overrideSetupSignals()
+ void installCrashHandler() {
+ installSignalHandler();
+ }
+} // end namespace g3
+
diff --git a/g3log/crashhandler_windows.cpp b/g3log/crashhandler_windows.cpp
new file mode 100644
index 0000000..acaebc6
--- /dev/null
+++ b/g3log/crashhandler_windows.cpp
@@ -0,0 +1,253 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+#error "crashhandler_windows.cpp used but not on a windows system"
+#endif
+
+#include <windows.h>
+#include <csignal>
+#include <sstream>
+#include <atomic>
+#include <process.h> // getpid
+#include "g3log/crashhandler.hpp"
+#include "g3log/stacktrace_windows.hpp"
+#include "g3log/logcapture.hpp"
+
+#define getpid _getpid
+
+namespace {
+ std::atomic<bool> gBlockForFatal {true};
+ LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
+
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ thread_local bool g_installed_thread_signal_handler = false;
+#endif
+
+#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
+ void *g_vector_exception_handler = nullptr;
+#endif
+
+
+
+ // Restore back to default fatal event handling
+ void ReverseToOriginalFatalHandling() {
+ SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler);
+
+#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
+ RemoveVectoredExceptionHandler (g_vector_exception_handler);
+#endif
+
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ 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");
+#endif
+ }
+
+
+
+ // called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
+ void signalHandler(int signal_number) {
+ using namespace g3::internal;
+ std::string dump = stacktrace::stackdump();
+
+ std::ostringstream fatal_stream;
+ fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
+ fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
+
+
+ LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
+ trigger.stream() << fatal_stream.str();
+
+ // Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
+ // Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio.
+ // For fatal signals only, not exceptions.
+ // This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
+ // Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
+ // This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled
+ // ref: g3log/Options.cmake
+#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
+ __debugbreak();
+#endif
+ } // scope exit - message sent to LogWorker, wait to die...
+
+
+
+ // Unhandled exception catching
+ LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info, const std::string &handler) {
+ std::string dump = stacktrace::stackdump(info);
+
+ std::ostringstream fatal_stream;
+ const g3::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
+ fatal_stream << "\n***** " << handler << ": Received fatal exception " << g3::internal::exitReasonName(g3::internal::FATAL_EXCEPTION, exception_code);
+ fatal_stream << "\tPID: " << getpid() << std::endl;
+
+ const auto fatal_id = static_cast<g3::SignalType>(exception_code);
+ LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
+ trigger.stream() << fatal_stream.str();
+ // FATAL Exception: It doesn't necessarily stop here we pass on continue search
+ // if no one else will catch that then it's goodbye anyhow.
+ // The RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER"
+ // but does not shutdown then the software will be running with g3log shutdown.
+ // .... However... this must be seen as a bug from standard handling of fatal exceptions
+ // https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+
+ // Unhandled exception catching
+ LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS *info) {
+ ReverseToOriginalFatalHandling();
+ return exceptionHandling(info, "Unexpected Exception Handler");
+ }
+
+
+ /// Setup through (Windows API) AddVectoredExceptionHandler
+ /// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
+#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
+ LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
+ const g3::SignalType exception_code = p->ExceptionRecord->ExceptionCode;
+ if (false == stacktrace::isKnownException(exception_code)) {
+ // The unknown exception is ignored. Since it is not a Windows
+ // fatal exception generated by the OS we leave the
+ // responsibility to deal with this by the client software.
+ return EXCEPTION_CONTINUE_SEARCH;
+ } else {
+ ReverseToOriginalFatalHandling();
+ return exceptionHandling(p, "Vectored Exception Handler");
+ }
+ }
+#endif
+
+
+
+
+} // end anonymous namespace
+
+
+namespace g3 {
+ namespace internal {
+ // For windows exceptions this might ONCE be set to false, in case of a
+ // windows exceptions and not a signal
+ bool shouldBlockForFatalHandling() {
+ return gBlockForFatal;
+ }
+
+
+ /// Generate stackdump. Or in case a stackdump was pre-generated and
+ /// non-empty just use that one. i.e. the latter case is only for
+ /// Windows and test purposes
+ std::string stackdump(const char *dump) {
+ if (nullptr != dump && !std::string(dump).empty()) {
+ return {dump};
+ }
+
+ return stacktrace::stackdump();
+ }
+
+
+
+ /// string representation of signal ID or Windows exception id
+ std::string exitReasonName(const LEVELS &level, g3::SignalType fatal_id) {
+ if (level == g3::internal::FATAL_EXCEPTION) {
+ return stacktrace::exceptionIdToText(fatal_id);
+ }
+
+ switch (fatal_id) {
+ 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(" << fatal_id << ")";
+ return oss.str();
+ }
+ }
+
+
+ // Triggered by g3log::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(const LEVELS &level, g3::SignalType fatal_signal_id) {
+
+ ReverseToOriginalFatalHandling();
+ // For windows exceptions we want to continue the possibility of
+ // exception handling now when the log and stacktrace are flushed
+ // to sinks. We therefore avoid to kill the preocess here. Instead
+ // it will be the exceptionHandling functions above that
+ // will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
+ if (g3::internal::FATAL_EXCEPTION == level) {
+ gBlockForFatal = false;
+ return;
+ }
+
+ // for a sigal however, we exit through that fatal signal
+ const int signal_number = static_cast<int>(fatal_signal_id);
+ raise(signal_number);
+ }
+
+
+
+ void installSignalHandler() {
+ g3::installSignalHandlerForThread();
+ }
+
+
+ } // end g3::internal
+
+
+ /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
+ /// on Windows. This is automatically done if you do at least one LOG(...) call
+ /// you can also use this function call, per thread so make sure these three
+ /// fatal signals are covered in your thread (even if you don't do a LOG(...) call
+ void installSignalHandlerForThread() {
+#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
+ if (!g_installed_thread_signal_handler) {
+ g_installed_thread_signal_handler = true;
+ if (SIG_ERR == signal(SIGTERM, signalHandler))
+ perror("signal - SIGTERM");
+ if (SIG_ERR == signal(SIGABRT, signalHandler))
+ perror("signal - SIGABRT");
+ if (SIG_ERR == signal(SIGFPE, signalHandler))
+ perror("signal - SIGFPE");
+ if (SIG_ERR == signal(SIGSEGV, signalHandler))
+ perror("signal - SIGSEGV");
+ if (SIG_ERR == signal(SIGILL, signalHandler))
+ perror("signal - SIGILL");
+ }
+#endif
+ }
+
+ void installCrashHandler() {
+ internal::installSignalHandler();
+ g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling);
+
+#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
+ // const size_t kFirstExceptionHandler = 1;
+ // kFirstExeptionsHandler is kept here for documentational purposes.
+ // The last exception seems more what we want
+ const size_t kLastExceptionHandler = 0;
+ g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
+#endif
+ }
+
+} // end namespace g3
diff --git a/g3log/filesink.cpp b/g3log/filesink.cpp
new file mode 100644
index 0000000..bfe8d5c
--- /dev/null
+++ b/g3log/filesink.cpp
@@ -0,0 +1,92 @@
+/** ==========================================================================
+ * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
+ * with no warranties. This code is yours to share, use and modify with no
+ * strings attached and no restrictions or obligations.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#include "g3log/filesink.hpp"
+#include "filesinkhelper.ipp"
+#include <cassert>
+
+namespace g3 {
+ using namespace internal;
+
+
+ FileSink::FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string& logger_id)
+ : _log_file_with_path(log_directory)
+ , _log_prefix_backup(log_prefix)
+ , _outptr(new std::ofstream)
+ {
+ _log_prefix_backup = prefixSanityFix(log_prefix);
+ if (!isValidFilename(_log_prefix_backup)) {
+ std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
+ abort();
+ }
+
+ std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
+ _log_file_with_path = pathSanityFix(_log_file_with_path, file_name);
+ _outptr = createLogFile(_log_file_with_path);
+
+ 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();
+ }
+
+
+ FileSink::~FileSink() {
+ std::string exit_msg {"g3log g3FileSink shutdown at: "};
+ exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted)).append({"\n"});
+ filestream() << exit_msg << std::flush;
+
+ exit_msg.append({"Log file at: ["}).append(_log_file_with_path).append({"]\n"});
+ std::cerr << exit_msg << std::flush;
+ }
+
+ // The actual log receiving function
+ void FileSink::fileWrite(LogMessageMover message) {
+ std::ofstream &out(filestream());
+ out << message.get().toString() << std::flush;
+ }
+
+ std::string FileSink::changeLogFile(const std::string &directory, const std::string &logger_id) {
+
+ auto now = g3::systemtime_now();
+ auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
+
+ std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
+ std::string prospect_log = directory + file_name;
+ std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
+ if (nullptr == log_stream) {
+ filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
+ 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";
+ filestream() << now_formatted << ss_change.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;
+ filestream() << now_formatted << ss_change.str();
+ return _log_file_with_path;
+ }
+ std::string FileSink::fileName() {
+ return _log_file_with_path;
+ }
+ void FileSink::addLogFileHeader() {
+ filestream() << header();
+ }
+
+} // g3
diff --git a/g3log/filesinkhelper.ipp b/g3log/filesinkhelper.ipp
new file mode 100644
index 0000000..406e993
--- /dev/null
+++ b/g3log/filesinkhelper.ipp
@@ -0,0 +1,124 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#pragma once
+
+
+#include <memory>
+#include <string>
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+
+namespace g3 {
+ 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);
+ if (pos != std::string::npos) {
+ std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
+ return false;
+ } else if (prefix_filename.empty()) {
+ std::cerr << "Empty filename prefix is not allowed" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string prefixSanityFix(std::string prefix) {
+ prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
+ prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end());
+ prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end());
+ prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end());
+ prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end());
+ if (!isValidFilename(prefix)) {
+ return
+ {
+ };
+ }
+ return prefix;
+ }
+
+ std::string pathSanityFix(std::string path, std::string file_name) {
+ // Unify the delimeters,. maybe sketchy solution but it seems to work
+ // on at least win7 + ubuntu. All bets are off for older windows
+ std::replace(path.begin(), path.end(), '\\', '/');
+
+ // clean up in case of multiples
+ auto contains_end = [&](std::string & in) -> bool {
+ size_t size = in.size();
+ if (!size) return false;
+ char end = in[size - 1];
+ return (end == '/' || end == ' ');
+ };
+
+ while (contains_end(path)) {
+ path.erase(path.size() - 1);
+ }
+
+ if (!path.empty()) {
+ path.insert(path.end(), '/');
+ }
+
+ path.insert(path.size(), file_name);
+ return path;
+ }
+
+ 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\tg3log created log at: " << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
+ ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message";
+ ss_entry << "\t\t(uuu*: microseconds fractions of the seconds value)\n\n";
+ return ss_entry.str();
+ }
+
+ std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) {
+ std::stringstream oss_name;
+ oss_name << verified_prefix << ".";
+ if( logger_id != "" ) oss_name << logger_id << ".";
+ oss_name << g3::localtime_formatted(g3::systemtime_now(), file_name_time_formatted);
+ oss_name << ".log";
+ return oss_name.str();
+ }
+
+ bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) {
+ std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
+ mode |= std::ios_base::trunc;
+ outstream.open(complete_file_with_path, mode);
+ if (!outstream.is_open()) {
+ std::ostringstream ss_error;
+ ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]";
+ ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
+ std::cerr << ss_error.str().c_str() << std::endl;
+ outstream.close();
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<std::ofstream> createLogFile(const std::string &file_with_full_path) {
+ std::unique_ptr<std::ofstream> out(new std::ofstream);
+ std::ofstream &stream(*(out.get()));
+ bool success_with_open_file = openLogFile(file_with_full_path, stream);
+ if (false == success_with_open_file) {
+ out.release();
+ }
+ return out;
+ }
+
+
+ }
+}
diff --git a/g3log/g2log.hpp b/g3log/g2log.hpp
new file mode 100644
index 0000000..0c6c932
--- /dev/null
+++ b/g3log/g2log.hpp
@@ -0,0 +1,23 @@
+/** ==========================================================================
+* 2015 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+
+// For convenience: If you don't want to do a recursive search and replace in your source code
+// for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your
+// code. It will get the necessary includes
+//
+//
+// Btw: replacing g2log for g3log include is easy on Linux
+// find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g'
+
+#include <g3log/g3log.hpp>
+#include <g3log/logworker.hpp>
+#include <g3log/loglevels.hpp>
+#include <g3log/filesink.hpp>
+
diff --git a/g3log/g3log.cpp b/g3log/g3log.cpp
new file mode 100644
index 0000000..86a437b
--- /dev/null
+++ b/g3log/g3log.cpp
@@ -0,0 +1,258 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================
+ *
+ * Filename:g3log.cpp 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 at least in "spirit" influenced
+ * 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
+ * ********************************************* */
+
+#include "g3log/g3log.hpp"
+#include "g3log/std2_make_unique.hpp"
+#include "g3log/logworker.hpp"
+#include "g3log/crashhandler.hpp"
+#include "g3log/logmessage.hpp"
+#include "g3log/loglevels.hpp"
+
+
+#include <mutex>
+#include <memory>
+#include <iostream>
+#include <thread>
+#include <atomic>
+#include <cstdlib>
+#include <sstream>
+
+namespace {
+ std::once_flag g_initialize_flag;
+ g3::LogWorker *g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
+ std::mutex g_logging_init_mutex;
+
+ std::unique_ptr<g3::LogMessage> g_first_unintialized_msg = {nullptr};
+ std::once_flag g_set_first_uninitialized_flag;
+ std::once_flag g_save_first_unintialized_flag;
+ const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */};
+ std::function<void(void)> g_fatal_pre_logging_hook;
+
+
+ std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
+}
+
+
+
+
+
+namespace g3 {
+ // 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(LogWorker *bgworker) {
+ std::call_once(g_initialize_flag, [] {
+ installCrashHandler();
+ });
+ std::lock_guard<std::mutex> lock(g_logging_init_mutex);
+ if (internal::isLoggingInitialized() || nullptr == bgworker) {
+ std::ostringstream exitMsg;
+ exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl;
+ exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n";
+ exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized();
+ exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")";
+ std::cerr << exitMsg.str() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+
+ // Save the first uninitialized message, if any
+ std::call_once(g_save_first_unintialized_flag, [&bgworker] {
+ if (g_first_unintialized_msg) {
+ bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)});
+ }
+ });
+
+ g_logger_instance = bgworker;
+ // by default the pre fatal logging hook does nothing
+ // if it WOULD do something it would happen in
+ setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
+ // recurvise crash counter re-set to zero
+ g_fatal_hook_recursive_counter.store(0);
+ }
+
+
+ /**
+ * default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing
+ * It will be called just before sending the fatal message, @ref pushFatalmessageToLogger
+ * It will be reset to do nothing in ::initializeLogging(...)
+ * so please call this function, if you ever need to, after initializeLogging(...)
+ */
+ void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
+ static std::mutex m;
+ std::lock_guard<std::mutex> lock(m);
+ g_fatal_pre_logging_hook = pre_fatal_hook;
+ }
+
+
+
+
+ // By default this function pointer goes to \ref pushFatalMessageToLogger;
+ std::function<void(FatalMessagePtr) > g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
+
+ /** REPLACE fatalCallToLogger for fatalCallForUnitTest
+ * This function switches the function pointer so that only
+ * 'unitTest' mock-fatal calls are made.
+ * */
+ void setFatalExitHandler(std::function<void(FatalMessagePtr) > fatal_call) {
+ g_fatal_to_g3logworker_function_ptr = fatal_call;
+ }
+
+
+ namespace internal {
+
+ bool isLoggingInitialized() {
+ return g_logger_instance != nullptr;
+ }
+
+ /**
+ * Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
+ * that is the responsibility of its owner. *
+ */
+ void shutDownLogging() {
+ std::lock_guard<std::mutex> lock(g_logging_init_mutex);
+ g_logger_instance = nullptr;
+
+ }
+
+ /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
+ * LOG(...) calls can happen to a non-existing LogWorker.
+ * @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
+ * and the logging continues to be active.
+ * @return true if the correct worker was given,. and shutDownLogging was called
+ */
+ bool shutDownLoggingForActiveOnly(LogWorker *active) {
+ if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
+ LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
+ << "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG"
+ << "\n\t\tEither way, this call to shutDownLogging was ignored"
+ << "\n\t\tTry g3::internal::shutDownLogging() instead";
+ return false;
+ }
+ shutDownLogging();
+ return true;
+ }
+
+
+
+
+ /** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
+ * i.e. (dlopen + dlsym) */
+ void saveMessage(const char *entry, const char *file, int line, const char *function, const LEVELS &level,
+ const char *boolean_expression, int fatal_signal, const char *stack_trace) {
+ LEVELS msgLevel {level};
+ LogMessagePtr message {std2::make_unique<LogMessage>(file, line, function, msgLevel)};
+ message.get()->write().append(entry);
+ message.get()->setExpression(boolean_expression);
+
+
+ if (internal::wasFatal(level)) {
+ auto fatalhook = g_fatal_pre_logging_hook;
+ // In case the fatal_pre logging actually will cause a crash in its turn
+ // let's not do recursive crashing!
+ setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
+ ++g_fatal_hook_recursive_counter; // thread safe counter
+ // "benign" race here. If two threads crashes, with recursive crashes
+ // then it's possible that the "other" fatal stack trace will be shown
+ // that's OK since it was anyhow the first crash detected
+ static const std::string first_stack_trace = stack_trace;
+ fatalhook();
+ message.get()->write().append(stack_trace);
+
+ if (g_fatal_hook_recursive_counter.load() > 1) {
+ message.get()->write()
+ .append("\n\n\nWARNING\n"
+ "A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
+ .append("---First crash stacktrace: ").append(first_stack_trace).append("\n---End of first stacktrace\n");
+ }
+ FatalMessagePtr fatal_message { std2::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal) };
+ // At destruction, flushes fatal message to g3LogWorker
+ // either we will stay here until the background worker has received the fatal
+ // message, flushed the crash message to the sinks and exits with the same fatal signal
+ //..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
+ fatalCall(fatal_message);
+ } else {
+ pushMessageToLogger(message);
+ }
+ }
+
+ /**
+ * save the message to the logger. In case of called before the logger is instantiated
+ * the first message will be saved. Any following subsequent unitnialized log calls
+ * will be ignored.
+ *
+ * The first initialized log entry will also save the first uninitialized log message, if any
+ * @param log_entry to save to logger
+ */
+ void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
+ // Uninitialized messages are ignored but does not CHECK/crash the logger
+ if (!internal::isLoggingInitialized()) {
+ std::call_once(g_set_first_uninitialized_flag, [&] {
+ g_first_unintialized_msg = incoming.release();
+ std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
+ err.append(g_first_unintialized_msg->message());
+ std::string &str = g_first_unintialized_msg->write();
+ str.clear();
+ str.append(err); // replace content
+ std::cerr << str << std::endl;
+ });
+ return;
+ }
+
+ // logger is initialized
+ g_logger_instance->save(incoming);
+ }
+
+ /** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
+ * to exit the program. After saving the fatal message the calling thread
+ * will sleep forever (i.e. until the background thread catches up, saves the fatal
+ * message and kills the software with the fatal signal.
+ */
+ void pushFatalMessageToLogger(FatalMessagePtr message) {
+ if (!isLoggingInitialized()) {
+ std::ostringstream error;
+ error << "FATAL CALL but logger is NOT initialized\n"
+ << "CAUSE: " << message.get()->reason()
+ << "\nMessage: \n" << message.get()->toString() << std::flush;
+ std::cerr << error.str() << std::flush;
+ internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
+ }
+ g_logger_instance->fatal(message);
+ while (shouldBlockForFatalHandling()) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ }
+ }
+
+ /** The default, initial, handling to send a 'fatal' event to g3logworker
+ * the caller will stay here, eternally, until the software is aborted
+ * ... in the case of unit testing it is the given "Mock" fatalCall that will
+ * define the behaviour.
+ */
+ void fatalCall(FatalMessagePtr message) {
+ g_fatal_to_g3logworker_function_ptr(FatalMessagePtr {std::move(message)});
+ }
+
+
+ } // internal
+} // g3
+
+
+
diff --git a/g3log/g3log/active.hpp b/g3log/g3log/active.hpp
new file mode 100644
index 0000000..b04ca3c
--- /dev/null
+++ b/g3log/g3log/active.hpp
@@ -0,0 +1,69 @@
+/** ==========================================================================
+ * 2010 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================
+ *
+ * Example of a Active Object, using C++11 std::thread mechanisms to make it
+ * safe for thread communication.
+ *
+ * This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x
+ * and inspired from Herb Sutter's C++11 Active Object
+ * http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads
+ *
+ * Last update 2013-12-19 by Kjell Hedstrom,
+ * e-mail: hedstrom at kjellkod dot cc
+ * linkedin: http://linkedin.com/se/kjellkod */
+
+#pragma once
+
+#include <thread>
+#include <functional>
+#include <memory>
+#include "g3log/shared_queue.hpp"
+
+namespace kjellkod {
+ typedef std::function<void() > Callback;
+
+ class Active {
+ private:
+ Active() : done_(false) {} // Construction ONLY through factory createActive();
+ Active(const Active &) = delete;
+ Active &operator=(const Active &) = delete;
+
+ void run() {
+ while (!done_) {
+ Callback func;
+ mq_.wait_and_pop(func);
+ func();
+ }
+ }
+
+ shared_queue<Callback> mq_;
+ std::thread thd_;
+ bool done_;
+
+
+ public:
+ virtual ~Active() {
+ send([this] { done_ = true;});
+ thd_.join();
+ }
+
+ void send(Callback msg_) {
+ mq_.push(msg_);
+ }
+
+ /// Factory: safe construction of object before thread start
+ static std::unique_ptr<Active> createActive() {
+ std::unique_ptr<Active> aPtr(new Active());
+ aPtr->thd_ = std::thread(&Active::run, aPtr.get());
+ return aPtr;
+ }
+ };
+
+
+
+} // kjellkod
diff --git a/g3log/g3log/crashhandler.hpp b/g3log/g3log/crashhandler.hpp
new file mode 100644
index 0000000..dfa4327
--- /dev/null
+++ b/g3log/g3log/crashhandler.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+#include <cstdio>
+#include <string>
+#include <map>
+#include "g3log/loglevels.hpp"
+#include "g3log/generated_definitions.hpp"
+
+// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
+// implementationsfilen kan vara den samma
+namespace g3 {
+
+ // 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
+ SIGABRT ABORT (ANSI), abnormal termination
+ SIGFPE Floating point exception (ANSI)
+ SIGILL ILlegal instruction (ANSI)
+ SIGSEGV Segmentation violation i.e. illegal memory reference
+ SIGTERM TERMINATION (ANSI) */
+ void installCrashHandler();
+
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+ typedef unsigned long SignalType;
+ /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
+ /// on Windows. This is automatically done if you do at least one LOG(...) call
+ /// you can also use this function call, per thread so make sure these three
+ /// fatal signals are covered in your thread (even if you don't do a LOG(...) call
+ void installSignalHandlerForThread();
+#else
+ typedef int SignalType;
+
+ /// Probably only needed for unit testing. Resets the signal handling back to default
+ /// which might be needed in case it was previously overridden
+ /// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
+ void restoreSignalHandlerToDefault();
+
+ /// Overrides the existing signal handling for custom signals
+ /// For example: usage of zcmq relies on its own signal handler for SIGTERM
+ /// so users of g3log with zcmq should then use the @ref overrideSetupSignals
+ /// , likely with the original set of signals but with SIGTERM removed
+ ///
+ /// call example:
+ /// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"},
+ // {SIGSEGV, "SIGSEGV"},});
+ void overrideSetupSignals(const std::map<int, std::string> overrideSignals);
+#endif
+
+
+ namespace internal {
+ /** return whether or any fatal handling is still ongoing
+ * this is used by g3log::fatalCallToLogger
+ * only in the case of Windows exceptions (not fatal signals)
+ * are we interested in changing this from false to true to
+ * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/
+ bool shouldBlockForFatalHandling();
+
+ /** \return signal_name Ref: signum.hpp and \ref installSignalHandler
+ * or for Windows exception name */
+ std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number);
+
+ /** return calling thread's stackdump*/
+ std::string stackdump(const char* dump = nullptr);
+
+ /** 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 g3log, g3LogWorker after flushing messages to file */
+ void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
+ } // end g3::internal
+} // g3
diff --git a/g3log/g3log/filesink.hpp b/g3log/g3log/filesink.hpp
new file mode 100644
index 0000000..397aa04
--- /dev/null
+++ b/g3log/g3log/filesink.hpp
@@ -0,0 +1,42 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+#pragma once
+
+#include <string>
+#include <memory>
+
+#include "g3log/logmessage.hpp"
+namespace g3 {
+
+ class FileSink {
+ public:
+ FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string &logger_id="g3log");
+ virtual ~FileSink();
+
+ void fileWrite(LogMessageMover message);
+ std::string changeLogFile(const std::string &directory, const std::string &logger_id);
+ std::string fileName();
+
+
+ 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;
+
+ void addLogFileHeader();
+ std::ofstream &filestream() {
+ return *(_outptr.get());
+ }
+
+
+ FileSink &operator=(const FileSink &) = delete;
+ FileSink(const FileSink &other) = delete;
+
+ };
+} // g3
+
diff --git a/g3log/g3log/future.hpp b/g3log/g3log/future.hpp
new file mode 100644
index 0000000..ed50ef0
--- /dev/null
+++ b/g3log/g3log/future.hpp
@@ -0,0 +1,63 @@
+#pragma once
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================
+* Filename:g3future.hpp
+* Helper functionality to put packaged_tasks in standard container. This
+* is especially helpful for background thread processing a la async but through
+* an actor pattern (active object), thread pool or similar.
+* Created: 2012 by Kjell Hedström
+*
+* COMMUNITY THANKS:
+* The code below is in large thanks to exemplifying code snippets from StackOverflow
+* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm
+* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
+*
+* Both are highly recommended reads if you are interested in c++ concurrency library
+* - Kjell, 2012
+*
+* PUBLIC DOMAIN and NOT under copywrite protection.
+* ********************************************* */
+
+
+
+#include <future>
+#include "g3log/active.hpp"
+#include "g3log/moveoncopy.hpp"
+#include "g3log/stlpatch_future.hpp"
+
+namespace g3 {
+ // Generic helper function to avoid repeating the steps for managing
+ // asynchronous task job (by active object) that returns a future results
+ // could of course be made even more generic if done more in the way of
+ // std::async, ref: http://en.cppreference.com/w/cpp/thread/async
+ //
+ // Example usage:
+ // std::unique_ptr<Active> bgWorker{Active::createActive()};
+ // ...
+ // auto msg_call=[=](){return ("Hello from the Background");};
+ // auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get());
+ template <typename Func, class BgWorker>
+ std::future<typename std::result_of<Func()>::type> spawn_task(Func func, BgWorker *worker)
+ {
+ typedef typename std::result_of<Func()>::type result_type;
+ typedef std::packaged_task<result_type()> task_type;
+
+ if (nullptr == worker) {
+ auto p = std::make_shared<std::promise<result_type>>();
+ std::future<result_type> future_result = p->get_future();
+ p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker")));
+ return future_result;
+ }
+
+ task_type task(std::move(func));
+
+ std::future<result_type> result = task.get_future();
+ worker->send(MoveOnCopy<task_type>(std::move(task)));
+ return std::move(result);
+ }
+} // end namespace g3
diff --git a/g3log/g3log/g3log.hpp b/g3log/g3log/g3log.hpp
new file mode 100644
index 0000000..6d2ec16
--- /dev/null
+++ b/g3log/g3log/g3log.hpp
@@ -0,0 +1,220 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================
+ *
+ * Filename:g3log.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=/caddpp/
+ * 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
+ * ********************************************* */
+
+
+#pragma once
+
+#include "g3log/loglevels.hpp"
+#include "g3log/logcapture.hpp"
+#include "g3log/logmessage.hpp"
+
+#include <string>
+#include <functional>
+
+
+#if !(defined(__PRETTY_FUNCTION__))
+#define __PRETTY_FUNCTION__ __FUNCTION__
+#endif
+
+// thread_local doesn't exist before VS2013
+// it exists on VS2015
+#if !(defined(thread_local)) && defined(_MSC_VER) && _MSC_VER < 1900
+#define thread_local __declspec(thread)
+#endif
+
+
+/** namespace for LOG() and CHECK() frameworks
+ * History lesson: Why the names 'g3' and 'g3log'?:
+ * The framework was made in my own free time as PUBLIC DOMAIN but the
+ * first commercial project to use it used 'g3' as an internal denominator for
+ * the current project. g3 as in 'generation 2'. I decided to keep the g3 and g3log 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 g3log became the name for this logger.
+ *
+ * --- Thanks for a great 2011 and good luck with 'g3' --- KjellKod
+ */
+namespace g3 {
+ class LogWorker;
+ struct LogMessage;
+ struct FatalMessage;
+
+ /** Should be called at very first startup of the software with \ref g3LogWorker
+ * pointer. Ownership of the \ref g3LogWorker is the responsibilkity of the caller */
+ void initializeLogging(LogWorker *logger);
+
+
+ /** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called
+ *
+ * Set a function-hook before a fatal message will be sent to the logger
+ * i.e. this is a great place to put a break point, either in your debugger
+ * or programatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal)
+ * This will be reset to default (does nothing) at initializeLogging(...);
+ *
+ * Example usage:
+ * Windows: g3::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include <intrin.h>
+ * WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely
+ * trigger a recursive crash if used here. It should only be used when debugging
+ * in your Visual Studio IDE. Recursive crashes are handled but are unnecessary.
+ *
+ * Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); });
+ */
+ void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook);
+
+ /** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then
+ * use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for
+ * example of restoring signal and exception handlers, flushing the log and shutting down.
+ */
+ void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
+
+
+
+
+ // internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely
+ // that you will use these
+ namespace internal {
+ /// @returns true if logger is initialized
+ bool isLoggingInitialized();
+
+ // Save the created LogMessage to any existing sinks
+ void saveMessage(const char *message, const char *file, int line, const char *function, const LEVELS &level,
+ const char *boolean_expression, int fatal_signal, const char *stack_trace);
+
+ // forwards the message to all sinks
+ void pushMessageToLogger(LogMessagePtr log_entry);
+
+
+ // forwards a FATAL message to all sinks,. after which the g3logworker
+ // will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler
+ //
+ // By default the "fatalCall" will forward a Fatalessageptr to this function
+ // this behaviour can be changed if you set a different fatal handler through
+ // "setFatalExitHandler"
+ void pushFatalMessageToLogger(FatalMessagePtr message);
+
+
+ // Save the created FatalMessage to any existing sinks and exit with
+ // the originating fatal signal,. or SIGABRT if it originated from a broken contract
+ // By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
+ //
+ // If you override it then you probably want to call "pushFatalMessageToLogger" after your
+ // custom fatal handler is done. This will make sure that the fatal message the pushed
+ // to sinks as well as shutting down the process
+ void fatalCall(FatalMessagePtr message);
+
+ // Shuts down logging. No object cleanup but further LOG(...) calls will be ignored.
+ void shutDownLogging();
+
+ // Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
+ bool shutDownLoggingForActiveOnly(LogWorker *active);
+
+ } // internal
+} // g3
+
+#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
+
+#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
+ LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, g3::internal::CONTRACT, boolean_expression)
+
+
+// LOG(level) is the API for the stream log
+#define LOG(level) if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
+
+
+// 'Conditional' stream log
+#define LOG_IF(level, boolean_expression) \
+ if(true == boolean_expression) \
+ if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
+
+// 'Design By Contract' stream API. For Broken Contracts:
+// unit testing: it will throw std::runtime_error when a contract breaks
+// I.R.L : it will exit the application by using fatal signal SIGABRT
+#define CHECK(boolean_expression) \
+ if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
+
+
+/** 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(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
+
+// Conditional log printf syntax
+#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
+ if(true == boolean_expression) \
+ if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
+
+// Design By Contract, printf-like API syntax with variadic input parameters.
+// Throws std::runtime_eror if contract breaks
+#define CHECKF(boolean_expression, printf_like_message, ...) \
+ if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
+
+// Backwards compatible. The same as CHECKF.
+// 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).capturef(printf_like_message, ##__VA_ARGS__)
+
+
diff --git a/g3log/g3log/logcapture.hpp b/g3log/g3log/logcapture.hpp
new file mode 100644
index 0000000..a9e7b49
--- /dev/null
+++ b/g3log/g3log/logcapture.hpp
@@ -0,0 +1,85 @@
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#pragma once
+
+#include "g3log/loglevels.hpp"
+#include "g3log/g3log.hpp"
+#include "g3log/crashhandler.hpp"
+
+#include <string>
+#include <sstream>
+#include <cstdarg>
+#include <csignal>
+#ifdef _MSC_VER
+# include <sal.h>
+#endif
+
+/**
+ * Simple struct for capturing log/fatal entries. At destruction the captured message is
+ * forwarded to background worker.
+ * As a safety precaution: No memory allocated here will be moved into the background
+ * worker in case of dynamic loaded library reasons
+*/
+struct LogCapture {
+ /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
+ LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump = nullptr);
+
+
+ /**
+ * @file, line, function are given in g3log.hpp from macros
+ * @level INFO/DEBUG/WARNING/FATAL
+ * @expression for CHECK calls
+ * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
+ */
+ LogCapture(const char *file, const int line, const char *function, const LEVELS &level, const char *expression = "", g3::SignalType fatal_signal = SIGABRT, const char *dump = nullptr);
+
+
+ // At destruction the message will be forwarded to the g3log worker.
+ // in case of dynamically (at runtime) loaded libraries the important thing to know is that
+ // all strings are copied so the original are not destroyed at the receiving end, only the copy
+ virtual ~LogCapture();
+
+
+
+
+ // Use "-Wall" to generate warnings in case of illegal printf format.
+ // Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
+#ifndef __GNUC__
+#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
+#endif
+#ifdef _MSC_VER
+# if _MSC_VER >= 1400
+# define G3LOG_FORMAT_STRING _Printf_format_string_
+# else
+# define G3LOG_FORMAT_STRING __format_string
+# endif
+#else
+# define G3LOG_FORMAT_STRING
+#endif
+ void capturef(G3LOG_FORMAT_STRING const char *printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18
+
+
+ /// prettifying API for this completely open struct
+ std::ostringstream &stream() {
+ return _stream;
+ }
+
+
+
+ std::ostringstream _stream;
+ std::string _stack_trace;
+ const char *_file;
+ const int _line;
+ const char *_function;
+ const LEVELS &_level;
+ const char *_expression;
+ const g3::SignalType _fatal_signal;
+
+};
+//} // g3
diff --git a/g3log/g3log/loglevels.hpp b/g3log/g3log/loglevels.hpp
new file mode 100644
index 0000000..f6b626a
--- /dev/null
+++ b/g3log/g3log/loglevels.hpp
@@ -0,0 +1,125 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+#include "g3log/generated_definitions.hpp"
+
+// Users of Juce or other libraries might have a define DEBUG which clashes with
+// the DEBUG logging level for G3log. In that case they can instead use the define
+// "CHANGE_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
+#if (defined(CHANGE_DEBUG_TO_DBUG))
+#if (defined(DBUG))
+#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
+#endif
+#else
+#if (defined(DEBUG))
+#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
+#endif
+#endif
+
+#include <string>
+#include <algorithm>
+
+
+// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
+struct LEVELS {
+ // force internal copy of the const char*. This is a simple safeguard for when g3log is used in a
+ // "dynamic, runtime loading of shared libraries"
+
+ LEVELS(const LEVELS& other): value(other.value), text(other.text.c_str()) {}
+ LEVELS(int id, const std::string& idtext) : value(id), text(idtext) {}
+
+ bool operator==(const LEVELS& rhs) const {
+ return (value == rhs.value && text == rhs.text);
+ }
+
+ bool operator!=(const LEVELS& rhs) const {
+ return (value != rhs.value || text != rhs.text);
+ }
+
+ friend void swap(LEVELS& first, LEVELS& second) {
+ using std::swap;
+ swap(first.value, second.value);
+ swap(first.text, second.text);
+ }
+
+
+ LEVELS& operator=(LEVELS other) {
+ swap(*this, other);
+ return *this;
+ }
+
+
+ int value;
+ std::string text;
+};
+
+
+
+namespace g3 {
+ static const int kDebugValue = 0;
+ static const int kInfoValue = 100;
+ static const int kWarningValue = 500;
+ static const int kFatalValue = 1000;
+ static const int kInternalFatalValue = 2000;
+}
+
+#if (defined(CHANGE_DEBUG_TO_DBUG))
+const LEVELS DBUG {g3::kDebugValue, {"DEBUG"}},
+#else
+const LEVELS DEBUG {g3::kDebugValue, {"DEBUG"}},
+#endif
+ INFO {g3::kInfoValue, {"INFO"}},
+ WARNING {g3::kWarningValue, {"WARNING"}},
+
+
+
+// Insert here *any* extra logging levels that is needed. You can do so in your own source file
+// If it is a FATAL you should keep it above (FATAL.value and below internal::CONTRACT.value
+// If it is a non-fatal you can keep it above (WARNING.value and below FATAL.value)
+//
+// example: MyLoggingLevel.h
+// #pragma once
+// const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"};
+// const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"};
+//
+// IMPORTANT: As of yet dynamic on/off of logging is NOT changed automatically
+// any changes of this, if you use dynamic on/off must be done in loglevels.cpp,
+// g_log_level_status and
+// void setLogLevel(LEVELS log_level, bool enabled) {...}
+// bool logLevel(LEVELS log_level){...}
+
+
+// 1) Remember to update the FATAL initialization below
+// 2) Remember to update the initialization of "g3loglevels.cpp/g_log_level_status"
+ FATAL {g3::kFatalValue, {"FATAL"}};
+
+namespace g3 {
+ namespace internal {
+ const LEVELS CONTRACT {g3::kInternalFatalValue, {"CONTRACT"}},
+ FATAL_SIGNAL {g3::kInternalFatalValue +1, {"FATAL_SIGNAL"}},
+ FATAL_EXCEPTION {kInternalFatalValue +2, {"FATAL_EXCEPTION"}};
+
+ /// helper function to tell the logger if a log message was fatal. If it is it will force
+ /// a shutdown after all log entries are saved to the sinks
+ bool wasFatal(const LEVELS& level);
+ }
+
+#ifdef G3_DYNAMIC_LOGGING
+ // Only safe if done at initialization in a single-thread context
+ namespace only_change_at_initialization {
+ // Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
+ void setLogLevel(LEVELS level, bool enabled_status);
+ std::string printLevels();
+ void reset();
+
+ } // only_change_at_initialization
+#endif
+ bool logLevel(LEVELS level);
+} // g3
+
diff --git a/g3log/g3log/logmessage.hpp b/g3log/g3log/logmessage.hpp
new file mode 100644
index 0000000..28dcf25
--- /dev/null
+++ b/g3log/g3log/logmessage.hpp
@@ -0,0 +1,136 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+
+
+
+#include "g3log/loglevels.hpp"
+#include "g3log/time.hpp"
+#include "g3log/moveoncopy.hpp"
+#include "g3log/crashhandler.hpp"
+
+#include <string>
+#include <sstream>
+#include <thread>
+#include <memory>
+
+namespace g3 {
+
+ /** LogMessage contains all the data collected from the LOG(...) call.
+ * If the sink receives a std::string it will be the std::string toString()... function
+ * that will format the data into a string
+ *
+ * For sinks that receive a LogMessage they can either use the toString() function, or use
+ * the helper functions or even the public raw data to format the saved log message any
+ * desired way.
+ */
+ struct LogMessage {
+ std::string file_path() const {
+ return _file_path;
+ }
+ std::string file() const {
+ return _file;
+ }
+ std::string line() const {
+ return std::to_string(_line);
+ }
+ std::string function() const {
+ return _function;
+ }
+ std::string level() const {
+ return _level.text;
+ }
+
+ /// use a different format string to get a different look on the time.
+ // default look is Y/M/D H:M:S
+ std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
+
+ std::string message() const {
+ return _message;
+ }
+ std::string& write() const {
+ return _message;
+ }
+
+ std::string expression() const {
+ return _expression;
+ }
+ bool wasFatal() const {
+ return internal::wasFatal(_level);
+ }
+
+ std::string threadID() const;
+
+ std::string toString() const;
+ void setExpression(const std::string expression) {
+ _expression = expression;
+ }
+
+
+ LogMessage& operator=(LogMessage other);
+
+
+ LogMessage(const std::string& file, const int line, const std::string& function, const LEVELS& level);
+
+ explicit LogMessage(const std::string& fatalOsSignalCrashMessage);
+ LogMessage(const LogMessage& other);
+ LogMessage(LogMessage&& other);
+ virtual ~LogMessage() {}
+
+ //
+ // Complete access to the raw data in case the helper functions above
+ // are not enough.
+ //
+ timespec _timestamp;
+ std::thread::id _call_thread_id;
+ std::string _file;
+ std::string _file_path;
+ int _line;
+ std::string _function;
+ LEVELS _level;
+ std::string _expression; // only with content for CHECK(...) calls
+ mutable std::string _message;
+
+
+
+ friend void swap(LogMessage& first, LogMessage& second) {
+ using std::swap;
+ swap(first._timestamp, second._timestamp);
+ swap(first._call_thread_id, second._call_thread_id);
+ swap(first._file, second._file);
+ swap(first._line, second._line);
+ swap(first._function, second._function);
+ swap(first._level, second._level);
+ swap(first._expression, second._expression);
+ swap(first._message, second._message);
+ }
+
+ };
+
+
+
+ /** 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 : public LogMessage {
+ FatalMessage(const LogMessage& details, g3::SignalType signal_id);
+ FatalMessage(const FatalMessage&);
+ virtual ~FatalMessage() {}
+
+ LogMessage copyToLogMessage() const;
+ std::string reason() const;
+
+ const SignalType _signal_id;
+ };
+
+
+ typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr;
+ typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr;
+ typedef MoveOnCopy<LogMessage> LogMessageMover;
+} // g3
diff --git a/g3log/g3log/logworker.hpp b/g3log/g3log/logworker.hpp
new file mode 100644
index 0000000..f5a7979
--- /dev/null
+++ b/g3log/g3log/logworker.hpp
@@ -0,0 +1,118 @@
+#pragma once
+/** ==========================================================================
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================
+ * Filename:g3logworker.h Framework for Logging and Design By Contract
+ * Created: 2011 by Kjell Hedström
+ *
+ * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
+ * ********************************************* */
+#include "g3log/g3log.hpp"
+#include "g3log/sinkwrapper.hpp"
+#include "g3log/sinkhandle.hpp"
+#include "g3log/filesink.hpp"
+#include "g3log/logmessage.hpp"
+#include "g3log/std2_make_unique.hpp"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+
+namespace g3 {
+ class LogWorker;
+ struct LogWorkerImpl;
+ using FileSinkHandle = g3::SinkHandle<g3::FileSink>;
+
+ /// Background side of the LogWorker. Internal use only
+ struct LogWorkerImpl final {
+ typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
+ std::vector<SinkWrapperPtr> _sinks;
+ std::unique_ptr<kjellkod::Active> _bg; // do not change declaration order. _bg must be destroyed before sinks
+
+ LogWorkerImpl();
+ ~LogWorkerImpl() = default;
+
+ void bgSave(g3::LogMessagePtr msgPtr);
+ void bgFatal(FatalMessagePtr msgPtr);
+
+ LogWorkerImpl(const LogWorkerImpl&) = delete;
+ LogWorkerImpl& operator=(const LogWorkerImpl&) = delete;
+ };
+
+
+
+ /// Front end of the LogWorker. API that is usefule is
+ /// addSink( sink, default_call ) which returns a handle to the sink. See below and REAME for usage example
+ /// save( msg ) : internal use
+ /// fatal ( fatal_msg ) : internal use
+ class LogWorker final {
+ LogWorker() = default;
+ void addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> wrapper);
+
+ LogWorkerImpl _impl;
+ LogWorker(const LogWorker&) = delete;
+ LogWorker& operator=(const LogWorker&) = delete;
+
+
+ public:
+ ~LogWorker();
+
+ /// Creates the LogWorker with no sinks. See exampel below on @ref addSink for how to use it
+ /// if you want to use the default file logger then see below for @ref addDefaultLogger
+ static std::unique_ptr<LogWorker> createLogWorker();
+
+
+ /**
+ A convenience function to add the default g3::FileSink to the log worker
+ @param log_prefix that you want
+ @param log_directory where the log is to be stored.
+ @return a handle for API access to the sink. See the README for example usage
+
+ @verbatim
+ Example:
+ using namespace g3;
+ std::unique_ptr<LogWorker> logworker {LogWorker::createLogWorker()};
+ auto handle = addDefaultLogger("my_test_log", "/tmp");
+ initializeLogging(logworker.get()); // ref. g3log.hpp
+
+ std::future<std::string> log_file_name = sinkHandle->call(&FileSink::fileName);
+ std::cout << "The filename is: " << log_file_name.get() << std::endl;
+ // something like: /tmp/my_test_log.g3log.20150819-100300.log
+ */
+ std::unique_ptr<FileSinkHandle> addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log");
+
+
+
+ /// Adds a sink and returns the handle for access to the sink
+ /// @param real_sink unique_ptr ownership is passed to the log worker
+ /// @param call the default call that should receive either a std::string or a LogMessageMover message
+ /// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger
+ template<typename T, typename DefaultLogCall>
+ std::unique_ptr<g3::SinkHandle<T>> addSink(std::unique_ptr<T> real_sink, DefaultLogCall call) {
+ using namespace g3;
+ using namespace g3::internal;
+ auto sink = std::make_shared<Sink<T>> (std::move(real_sink), call);
+ addWrappedSink(sink);
+ return std2::make_unique<SinkHandle<T>> (sink);
+ }
+
+
+
+ /// internal:
+ /// pushes in background thread (asynchronously) input messages to log file
+ void save(LogMessagePtr entry);
+
+ /// internal:
+ // pushes a fatal message on the queue, this is the last message to be processed
+ /// this way it's ensured that all existing entries were flushed before 'fatal'
+ /// Will abort the application!
+ void fatal(FatalMessagePtr fatal_message);
+
+
+ };
+} // g3
diff --git a/g3log/g3log/moveoncopy.hpp b/g3log/g3log/moveoncopy.hpp
new file mode 100644
index 0000000..4c11b5f
--- /dev/null
+++ b/g3log/g3log/moveoncopy.hpp
@@ -0,0 +1,48 @@
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+namespace g3 {
+
+ // A straightforward technique to move around packaged_tasks.
+ // Instances of std::packaged_task are MoveConstructible and MoveAssignable, but
+ // not CopyConstructible or CopyAssignable. To put them in a std container they need
+ // to be wrapped and their internals "moved" when tried to be copied.
+
+ template<typename Moveable>
+ struct MoveOnCopy {
+ mutable Moveable _move_only;
+
+ explicit MoveOnCopy(Moveable &&m) : _move_only(std::move(m)) {}
+ MoveOnCopy(MoveOnCopy const &t) : _move_only(std::move(t._move_only)) {}
+ MoveOnCopy(MoveOnCopy &&t) : _move_only(std::move(t._move_only)) {}
+
+ MoveOnCopy &operator=(MoveOnCopy const &other) {
+ _move_only = std::move(other._move_only);
+ return *this;
+ }
+
+ MoveOnCopy &operator=(MoveOnCopy && other) {
+ _move_only = std::move(other._move_only);
+ return *this;
+ }
+
+ void operator()() {
+ _move_only();
+ }
+
+ Moveable &get() {
+ return _move_only;
+ }
+
+ Moveable release() {
+ return std::move(_move_only);
+ }
+ };
+
+} // g3
diff --git a/g3log/g3log/shared_queue.hpp b/g3log/g3log/shared_queue.hpp
new file mode 100644
index 0000000..e8abdde
--- /dev/null
+++ b/g3log/g3log/shared_queue.hpp
@@ -0,0 +1,80 @@
+/** ==========================================================================
+* 2010 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================
+*
+* Example of a normal std::queue protected by a mutex for operations,
+* making it safe for thread communication, using std::mutex from C++0x with
+* the help from the std::thread library from JustSoftwareSolutions
+* ref: http://www.stdthread.co.uk/doc/headers/mutex.html
+*
+* This exampel was totally inspired by Anthony Williams lock-based data structures in
+* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
+
+#pragma once
+
+#include <queue>
+#include <mutex>
+#include <exception>
+#include <condition_variable>
+
+/** Multiple producer, multiple consumer thread safe queue
+* Since 'return by reference' is used this queue won't throw */
+template<typename T>
+class shared_queue
+{
+ std::queue<T> queue_;
+ mutable std::mutex m_;
+ std::condition_variable data_cond_;
+
+ shared_queue &operator=(const shared_queue &) = delete;
+ shared_queue(const shared_queue &other) = delete;
+
+public:
+ shared_queue() {}
+
+ void push(T item) {
+ {
+ std::lock_guard<std::mutex> lock(m_);
+ queue_.push(std::move(item));
+ }
+ data_cond_.notify_one();
+ }
+
+ /// \return immediately, with true if successful retrieval
+ bool try_and_pop(T &popped_item) {
+ std::lock_guard<std::mutex> lock(m_);
+ if (queue_.empty()) {
+ return false;
+ }
+ popped_item = std::move(queue_.front());
+ queue_.pop();
+ return true;
+ }
+
+ /// Try to retrieve, if no items, wait till an item is available and try again
+ void wait_and_pop(T &popped_item) {
+ std::unique_lock<std::mutex> lock(m_);
+ while (queue_.empty())
+ {
+ data_cond_.wait(lock);
+ // This 'while' loop is equal to
+ // data_cond_.wait(lock, [](bool result){return !queue_.empty();});
+ }
+ popped_item = std::move(queue_.front());
+ queue_.pop();
+ }
+
+ bool empty() const {
+ std::lock_guard<std::mutex> lock(m_);
+ return queue_.empty();
+ }
+
+ unsigned size() const {
+ std::lock_guard<std::mutex> lock(m_);
+ return queue_.size();
+ }
+};
diff --git a/g3log/g3log/sink.hpp b/g3log/g3log/sink.hpp
new file mode 100644
index 0000000..c3896ff
--- /dev/null
+++ b/g3log/g3log/sink.hpp
@@ -0,0 +1,79 @@
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+
+
+#include "g3log/sinkwrapper.hpp"
+#include "g3log/active.hpp"
+#include "g3log/future.hpp"
+#include "g3log/logmessage.hpp"
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+
+namespace g3 {
+ namespace internal {
+ typedef std::function<void(LogMessageMover) > AsyncMessageCall;
+
+ /// The asynchronous Sink has an active object, incoming requests for actions
+ // will be processed in the background by the specific object the Sink represents.
+ //
+ // The Sink will wrap either
+ // a Sink with Message object receiving call
+ // or a Sink with a LogEntry (string) receving call
+ //
+ // The Sink can also be used through the SinkHandler to call Sink specific function calls
+ // Ref: send(Message) deals with incoming log entries (converted if necessary to string)
+ // Ref: send(Call call, Args... args) deals with calls
+ // to the real sink's API
+
+ template<class T>
+ struct Sink : public SinkWrapper {
+ std::unique_ptr<T> _real_sink;
+ std::unique_ptr<kjellkod::Active> _bg;
+ AsyncMessageCall _default_log_call;
+
+ template<typename DefaultLogCall >
+ Sink(std::unique_ptr<T> sink, DefaultLogCall call)
+ : SinkWrapper(),
+ _real_sink {std::move(sink)},
+ _bg(kjellkod::Active::createActive()),
+ _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
+ }
+
+
+ Sink(std::unique_ptr<T> sink, void(T::*Call)(std::string) )
+ : SinkWrapper(),
+ _real_sink {std::move(sink)},
+ _bg(kjellkod::Active::createActive()) {
+ std::function<void(std::string)> adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1);
+ _default_log_call = [ = ](LogMessageMover m) {
+ adapter(m.get().toString());
+ };
+ }
+
+ virtual ~Sink() {
+ _bg.reset(); // TODO: to remove
+ }
+
+ void send(LogMessageMover msg) override {
+ _bg->send([this, msg] {
+ _default_log_call(msg);
+ });
+ }
+
+ template<typename Call, typename... Args>
+ auto async(Call call, Args &&... args)-> std::future< typename std::result_of<decltype(call)(T, Args...)>::type> {
+ return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
+ }
+ };
+ } // internal
+} // g3
+
diff --git a/g3log/g3log/sinkhandle.hpp b/g3log/g3log/sinkhandle.hpp
new file mode 100644
index 0000000..308aa6e
--- /dev/null
+++ b/g3log/g3log/sinkhandle.hpp
@@ -0,0 +1,54 @@
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+
+#include "g3log/sink.hpp"
+
+#include <memory>
+#include <type_traits>
+
+namespace g3 {
+
+ // The Sinkhandle is the client's access point to the specific sink instance.
+ // Only through the Sinkhandle can, and should, the real sink's specific API
+ // be called.
+ //
+ // The real sink will be owned by the g3logger. If the real sink is deleted
+ // calls to sink's API through the SinkHandle will return an exception embedded
+ // in the resulting future. Ref: SinkHandle::call
+ template<class T>
+ class SinkHandle {
+ std::weak_ptr<internal::Sink<T>> _sink;
+
+ public:
+ SinkHandle(std::shared_ptr<internal::Sink<T>> sink)
+ : _sink(sink) {}
+
+ ~SinkHandle() {}
+
+
+ // Asynchronous call to the real sink. If the real sink is already deleted
+ // the returned future will contain a bad_weak_ptr exception instead of the
+ // call result.
+ template<typename AsyncCall, typename... Args>
+ auto call(AsyncCall func , Args &&... args) -> std::future<typename std::result_of<decltype(func)(T, Args...)>::type> {
+ try {
+ std::shared_ptr<internal::Sink<T>> sink(_sink);
+ return sink->async(func, std::forward<Args>(args)...);
+ } catch (const std::bad_weak_ptr &e) {
+ typedef typename std::result_of<decltype(func)(T, Args...)>::type PromiseType;
+ std::promise<PromiseType> promise;
+ promise.set_exception(std::make_exception_ptr(e));
+ return std::move(promise.get_future());
+ }
+ }
+ };
+}
+
+
diff --git a/g3log/g3log/sinkwrapper.hpp b/g3log/g3log/sinkwrapper.hpp
new file mode 100644
index 0000000..0cc1c1b
--- /dev/null
+++ b/g3log/g3log/sinkwrapper.hpp
@@ -0,0 +1,22 @@
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#pragma once
+
+#include "g3log/logmessage.hpp"
+
+namespace g3 {
+ namespace internal {
+
+ struct SinkWrapper {
+ virtual ~SinkWrapper() { }
+ virtual void send(LogMessageMover msg) = 0;
+ };
+ }
+}
+
diff --git a/g3log/g3log/stacktrace_windows.hpp b/g3log/g3log/stacktrace_windows.hpp
new file mode 100644
index 0000000..257abde
--- /dev/null
+++ b/g3log/g3log/stacktrace_windows.hpp
@@ -0,0 +1,42 @@
+/** ==========================================================================
+ * 2014 by KjellKod.cc AND Robert Engeln.
+ * The stacktrace code was given as a public domain dedication by Robert Engeln
+ * It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
+ * It was (here) modified for g3log purposes.
+ *
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+
+#pragma once
+#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+#error "stacktrace_win.cpp used but not on a windows system"
+#endif
+
+#include "g3log/crashhandler.hpp"
+
+#include <string>
+#include <windows.h>
+
+namespace stacktrace {
+ /// return the text description of a Windows exception code
+ std::string exceptionIdToText(g3::SignalType id);
+
+ /// return whether or not the exception is a known exception, i.e.
+ /// an exception that we should treat as a fatal event
+ bool isKnownException(g3::SignalType id);
+
+ /// helper function: retrieve stackdump from no excisting exception pointer
+ std::string stackdump();
+
+ /// helper function: retrieve stackdump, starting from an exception pointer
+ std::string stackdump(EXCEPTION_POINTERS *info);
+
+ /// main stackdump function. retrieve stackdump, from the given context
+ std::string stackdump(CONTEXT *context);
+
+} // stacktrace
diff --git a/g3log/g3log/std2_make_unique.hpp b/g3log/g3log/std2_make_unique.hpp
new file mode 100644
index 0000000..325ed63
--- /dev/null
+++ b/g3log/g3log/std2_make_unique.hpp
@@ -0,0 +1,47 @@
+/** ==========================================================================
+ * 2013 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ *
+ * make_unique will be in C++14, this implementation is copied as I understood
+ * Stephan T. Lavavej's description of it.
+ *
+ * PUBLIC DOMAIN and NOT under copywrite protection.
+ *
+ *
+ * Example: usage
+ * auto an_int = make_unique<int>(123);
+ * auto a_string = make_unique<string>(5, 'x');
+ * auto an_int_array = make_unique<int[]>(11, 22, 33);
+ * ********************************************* */
+
+#pragma once
+
+#include <memory>
+#include <type_traits>
+
+namespace std2 {
+ namespace impl_fut_stl {
+
+ template<typename T, typename ... Args>
+ std::unique_ptr<T> make_unique_helper(std::false_type, Args &&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ template<typename T, typename ... Args>
+ std::unique_ptr<T> make_unique_helper(std::true_type, Args &&... args) {
+ static_assert(std::extent<T>::value == 0, "make_unique<T[N]>() is forbidden, please use make_unique<T[]>(),");
+ typedef typename std::remove_extent<T>::type U;
+ return std::unique_ptr<T>(new U[sizeof...(Args)] {std::forward<Args>(args)...});
+ }
+ }
+
+ template<typename T, typename ... Args>
+ std::unique_ptr<T> make_unique(Args &&... args) {
+ return impl_fut_stl::make_unique_helper<T>(
+ std::is_array<T>(), std::forward<Args>(args)...);
+ }
+}
+
diff --git a/g3log/g3log/stlpatch_future.hpp b/g3log/g3log/stlpatch_future.hpp
new file mode 100644
index 0000000..80cf867
--- /dev/null
+++ b/g3log/g3log/stlpatch_future.hpp
@@ -0,0 +1,81 @@
+/** ==========================================================================
+* 2013 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+*
+*
+* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well
+* std::packaged_task<void()>. Thanks to Michael Rasmussen (lap777)
+* Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable
+* ============================================================================*/
+
+
+
+#pragma once
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
+namespace std {
+
+ template<class... _ArgTypes>
+ class packaged_task<void(_ArgTypes...)>
+ {
+ promise<void> _my_promise;
+ function<void(_ArgTypes...)> _my_func;
+
+ public:
+ packaged_task() {
+ }
+
+ template<class _Fty2>
+ explicit packaged_task(_Fty2 &&_Fnarg)
+ : _my_func(_Fnarg) {
+ }
+
+ packaged_task(packaged_task &&_Other)
+ : _my_promise(move(_Other._my_promise)),
+ _my_func(move(_Other._my_func)) {
+ }
+
+ packaged_task &operator=(packaged_task && _Other) {
+ _my_promise = move(_Other._my_promise);
+ _my_func = move(_Other._my_func);
+ return (*this);
+ }
+
+ packaged_task(const packaged_task &) = delete;
+ packaged_task &operator=(const packaged_task &) = delete;
+
+ ~packaged_task() {
+ }
+
+ void swap(packaged_task &_Other) {
+ swap(_my_promise, _Other._my_promise);
+ swap(_my_func, _Other._my_func);
+ }
+
+ explicit operator bool() const {
+ return _my_func != false;
+ }
+
+ bool valid() const {
+ return _my_func != false;
+ }
+
+ future<void> get_future() {
+ return _my_promise.get_future();
+ }
+
+ void operator()(_ArgTypes... _Args) {
+ _my_func(forward<_ArgTypes>(_Args)...);
+ _my_promise.set_value();
+ }
+
+ void reset() {
+ _my_promise.swap(promise<void>());
+ _my_func.swap(function<void(_ArgTypes...)>());
+ }
+ };
+
+}; // namespace std
+#endif // defined(WIN32) ...
diff --git a/g3log/g3log/time.hpp b/g3log/g3log/time.hpp
new file mode 100644
index 0000000..6227972
--- /dev/null
+++ b/g3log/g3log/time.hpp
@@ -0,0 +1,70 @@
+#pragma once
+/** ==========================================================================
+* 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================
+* Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe
+* localtime (and similar)
+* Created: 2012 by Kjell Hedström
+*
+* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc
+* ********************************************* */
+
+#include <ctime>
+#include <string>
+#include <chrono>
+
+// FYI:
+// namespace g3::internal ONLY in g3time.cpp
+// std::string put_time(const struct tm* tmb, const char* c_time_format)
+
+namespace g3 {
+ namespace internal {
+ enum class Fractional {Millisecond, Microsecond, Nanosecond, NanosecondDefault};
+ Fractional getFractional(const std::string& format_buffer, size_t pos);
+ std::string to_string(const timespec& time_snapshot, Fractional fractional);
+ static const std::string date_formatted = "%Y/%m/%d";
+ // %f: fractions of seconds (%f is nanoseconds)
+ // %f3: milliseconds, 3 digits: 001
+ // %6: microseconds: 6 digits: 000001 --- default for the time_format
+ // %f9, %f: nanoseconds, 9 digits: 000000001
+ static const std::string time_formatted = "%H:%M:%S %f6";
+ } // internal
+
+ typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
+ typedef std::chrono::milliseconds milliseconds;
+ typedef std::chrono::microseconds microseconds;
+
+
+
+ // custom wrap for std::chrono::system_clock::now()but this one
+ // returns timespec struct instead which is what we use in g3log
+ struct timespec systemtime_now();
+
+ // OSX, Windows needed wrapper for std::timespec_get(struct timespec *ts, int base)
+ // OSX and Windows also lacks the POSIX clock_gettime(int base, struct timespec *ts)
+ // so for that reason we go with the std::timespec_get(...) but wrap it
+ int timespec_get(struct timespec* ts/*, int base*/);
+
+ // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
+ // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
+ // return value is SIMPLIFIED to only return a std::string
+ std::string put_time(const struct tm* tmb, const char* c_time_format);
+
+ /** return time representing POD struct (ref ctime + wchar) that is normally
+ * retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not.
+ * g3::localtime is probably used together with @ref g3::systemtime_now */
+ tm localtime(const std::time_t& time);
+
+ /** format string must conform to std::put_time's demands.
+ * WARNING: At time of writing there is only so-so compiler support for
+ * std::put_time. A possible fix if your c++11 library is not updated is to
+ * modify this to use std::strftime instead */
+ std::string localtime_formatted(const timespec& time_snapshot, const std::string& time_format) ;
+}
+
+
+
diff --git a/g3log/logcapture.cpp b/g3log/logcapture.cpp
new file mode 100644
index 0000000..c5dc433
--- /dev/null
+++ b/g3log/logcapture.cpp
@@ -0,0 +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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#include "g3log/logcapture.hpp"
+#include "g3log/crashhandler.hpp"
+
+// For Windows we need force a thread_local install per thread of three
+// signals that must have a signal handler instealled per thread-basis
+// It is really a royal pain. Seriously Microsoft? Seriously?
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
+#else
+// Does nothing --- enforces that semicolon must be written
+#define SIGNAL_HANDLER_VERIFY() do {} while(0)
+#endif
+
+
+
+/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
+* captured message is forwarded to background worker.
+* As a safety precaution: No memory allocated here will be moved into the background
+* worker in case of dynamic loaded library reasons instead the arguments are copied
+* inside of g3log.cpp::saveMessage*/
+LogCapture::~LogCapture() {
+ using namespace g3::internal;
+ SIGNAL_HANDLER_VERIFY();
+ saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
+}
+
+
+/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
+LogCapture::LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump) : LogCapture("", 0, "", level, "", fatal_signal, dump) {
+}
+
+/**
+ * @file, line, function are given in g3log.hpp from macros
+ * @level INFO/DEBUG/WARNING/FATAL
+ * @expression for CHECK calls
+ * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
+ */
+LogCapture::LogCapture(const char *file, const int line, const char *function, const LEVELS &level,
+ const char *expression, g3::SignalType fatal_signal, const char *dump)
+ : _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {
+
+ if (g3::internal::wasFatal(level)) {
+ _stack_trace = {"\n*******\tSTACKDUMP *******\n"};
+ _stack_trace.append(g3::internal::stackdump(dump));
+ }
+}
+
+
+
+/**
+* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
+* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
+*/
+void LogCapture::capturef(const char *printf_like_message, ...) {
+ static const int kMaxMessageSize = 2048;
+ static const std::string kTruncatedWarningText = "[...truncated...]";
+ char finished_message[kMaxMessageSize];
+ va_list arglist;
+ va_start(arglist, printf_like_message);
+
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
+ const int nbrcharacters = vsnprintf_s(finished_message, _countof(finished_message), _TRUNCATE, printf_like_message, arglist);
+#else
+ const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
+#endif
+ va_end(arglist);
+
+ if (nbrcharacters <= 0) {
+ stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
+ stream() << '"' << printf_like_message << '"' << std::endl;
+ } else if (nbrcharacters > kMaxMessageSize) {
+ stream() << finished_message << kTruncatedWarningText;
+ } else {
+ stream() << finished_message;
+ }
+}
+
+
diff --git a/g3log/loglevels.cpp b/g3log/loglevels.cpp
new file mode 100644
index 0000000..01a5713
--- /dev/null
+++ b/g3log/loglevels.cpp
@@ -0,0 +1,77 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#include "g3log/loglevels.hpp"
+#include <atomic>
+#include <cassert>
+#include <map>
+
+namespace {
+ namespace {
+ /// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
+ struct atomicbool {
+ private:
+ std::atomic<bool> value_;
+ public:
+ atomicbool(): value_ {false} {}
+ atomicbool(const bool &value): value_ {value} {}
+ atomicbool(const std::atomic<bool> &value) : value_ {value.load(std::memory_order_acquire)} {}
+ atomicbool(const atomicbool &other): value_ {other.value_.load(std::memory_order_acquire)} {}
+ atomicbool &operator=(const atomicbool &other) {
+ value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
+ return *this;
+ }
+ bool value() {return value_.load(std::memory_order_acquire);}
+ std::atomic<bool>& get() {return value_;}
+ };
+
+ } // anonymous
+
+}
+namespace g3 {
+ namespace internal {
+ bool wasFatal(const LEVELS &level) {
+ return level.value >= FATAL.value;
+ }
+
+#ifdef G3_DYNAMIC_LOGGING
+ std::map<int, atomicbool> g_log_level_status = {{g3::kDebugValue, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
+#endif
+ } // internal
+
+#ifdef G3_DYNAMIC_LOGGING
+ namespace only_change_at_initialization {
+ void setLogLevel(LEVELS log_level, bool enabled) {
+ int level = log_level.value;
+ internal::g_log_level_status[level].get().store(enabled, std::memory_order_release);
+ }
+
+ std::string printLevels() {
+ std::string levels;
+ for (auto& v : internal::g_log_level_status) {
+ levels += "value: " + std::to_string(v.first) + " status: " + std::to_string(v.second.value()) + "\n";
+ }
+ return levels;
+ }
+
+ void reset() {
+ internal::g_log_level_status.clear();
+ internal::g_log_level_status = std::map<int, atomicbool>{{g3::kDebugValue, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
+ }
+ } // only_change_at_initialization
+#endif
+
+ bool logLevel(LEVELS log_level) {
+#ifdef G3_DYNAMIC_LOGGING
+ int level = log_level.value;
+ bool status = internal::g_log_level_status[level].value();
+ return status;
+#endif
+ return true;
+ }
+} // g3
diff --git a/g3log/logmessage.cpp b/g3log/logmessage.cpp
new file mode 100644
index 0000000..038b5f7
--- /dev/null
+++ b/g3log/logmessage.cpp
@@ -0,0 +1,199 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#include "g3log/logmessage.hpp"
+#include "g3log/crashhandler.hpp"
+#include "g3log/time.hpp"
+#include <mutex>
+
+namespace {
+ std::string splitFileName(const std::string& str) {
+ size_t found;
+ found = str.find_last_of("(/\\");
+ return str.substr(found + 1);
+ }
+} // anonymous
+
+
+
+namespace g3 {
+
+
+ // helper for setting the normal log details in an entry
+ std::string LogDetailsToString(const LogMessage& msg) {
+ std::string out;
+ out.append(msg.timestamp() + "\t"
+ + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
+ return out;
+ }
+
+
+ // helper for normal
+ std::string normalToString(const LogMessage& msg) {
+ auto out = LogDetailsToString(msg);
+ out.append(msg.message() + '\n');
+ return out;
+ }
+
+ // helper for fatal signal
+ std::string fatalSignalToString(const LogMessage& msg) {
+ std::string out; // clear any previous text and formatting
+ out.append(msg.timestamp()
+ + "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
+ + msg.message() + '\n');
+ return out;
+ }
+
+
+ // helper for fatal exception (windows only)
+ std::string fatalExceptionToString(const LogMessage& msg) {
+ std::string out; // clear any previous text and formatting
+ out.append(msg.timestamp()
+ + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n"
+ + msg.message() + '\n');
+ return out;
+ }
+
+
+ // helper for fatal LOG
+ std::string fatalLogToString(const LogMessage& msg) {
+ auto out = LogDetailsToString(msg);
+ static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
+ out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
+ return out;
+ }
+
+ // helper for fatal CHECK
+ std::string fatalCheckToString(const LogMessage& msg) {
+ auto out = LogDetailsToString(msg);
+ static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
+ out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t"
+ + '"' + msg. message() + '"');
+ return out;
+ }
+
+
+ // Format the log message according to it's type
+ std::string LogMessage::toString() const {
+ if (false == wasFatal()) {
+ return normalToString(*this);
+ }
+
+ const auto level_value = _level.value;
+ if (internal::FATAL_SIGNAL.value == _level.value) {
+ return fatalSignalToString(*this);
+ }
+
+ if (internal::FATAL_EXCEPTION.value == _level.value) {
+ return fatalExceptionToString(*this);
+ }
+
+ if (FATAL.value == _level.value) {
+ return fatalLogToString(*this);
+ }
+
+ if (internal::CONTRACT.value == level_value) {
+ return fatalCheckToString(*this);
+ }
+
+ // What? Did we hit a custom made level?
+ auto out = LogDetailsToString(*this);
+ static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"};
+ out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n');
+ return out;
+ }
+
+
+
+ std::string LogMessage::timestamp(const std::string& time_look) const {
+ return g3::localtime_formatted(_timestamp, time_look);
+ }
+
+
+// By copy, not by reference. See this explanation for details:
+// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
+ LogMessage& LogMessage::operator=(LogMessage other) {
+ swap(*this, other);
+ return *this;
+ }
+
+
+ LogMessage::LogMessage(const std::string& file, const int line,
+ const std::string& function, const LEVELS& level)
+ : _call_thread_id(std::this_thread::get_id())
+ , _file(splitFileName(file))
+ , _file_path(file)
+ , _line(line)
+ , _function(function)
+ , _level(level)
+ {
+ g3::timespec_get(&_timestamp/*, TIME_UTC*/);
+ // Another possibility could be to Falling back to clock_gettime as TIME_UTC
+ // is not recognized by travis CI.
+ // i.e. clock_gettime(CLOCK_REALTIME, &_timestamp);
+ }
+
+
+ LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage)
+ : LogMessage( {""}, 0, {""}, internal::FATAL_SIGNAL) {
+ _message.append(fatalOsSignalCrashMessage);
+ }
+
+ LogMessage::LogMessage(const LogMessage& other)
+ : _timestamp(other._timestamp)
+ , _call_thread_id(other._call_thread_id)
+ , _file(other._file)
+ , _file_path(other._file_path)
+ , _line(other._line)
+ , _function(other._function)
+ , _level(other._level)
+ , _expression(other._expression)
+ , _message(other._message) {
+ }
+
+ LogMessage::LogMessage(LogMessage &&other)
+ : _timestamp(other._timestamp)
+ , _call_thread_id(other._call_thread_id)
+ , _file(std::move(other._file))
+ , _file_path(std::move(other._file_path))
+ , _line(other._line)
+ , _function(std::move(other._function))
+ , _level(other._level)
+ , _expression(std::move(other._expression))
+ , _message(std::move(other._message)) {
+ }
+
+
+
+ std::string LogMessage::threadID() const {
+ std::ostringstream oss;
+ oss << _call_thread_id;
+ return oss.str();
+ }
+
+
+
+ FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id)
+ : LogMessage(details), _signal_id(signal_id) { }
+
+
+
+ FatalMessage::FatalMessage(const FatalMessage& other)
+ : LogMessage(other), _signal_id(other._signal_id) {}
+
+
+ LogMessage FatalMessage::copyToLogMessage() const {
+ return LogMessage(*this);
+ }
+
+ std::string FatalMessage::reason() const {
+ return internal::exitReasonName(_level, _signal_id);
+ }
+
+
+} // g3
diff --git a/g3log/logworker.cpp b/g3log/logworker.cpp
new file mode 100644
index 0000000..3f469d4
--- /dev/null
+++ b/g3log/logworker.cpp
@@ -0,0 +1,130 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#include "g3log/logworker.hpp"
+#include "g3log/logmessage.hpp"
+#include "g3log/active.hpp"
+#include "g3log/g3log.hpp"
+#include "g3log/future.hpp"
+#include "g3log/crashhandler.hpp"
+
+#include <iostream>
+
+namespace g3 {
+
+ LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }
+
+ void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
+ std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
+
+ for (auto& sink : _sinks) {
+ LogMessage msg(*(uniqueMsg));
+ sink->send(LogMessageMover(std::move(msg)));
+ }
+
+ if (_sinks.empty()) {
+ std::string err_msg {"g3logworker has no sinks. Message: ["};
+ err_msg.append(uniqueMsg.get()->toString()).append({"]\n"});
+ std::cerr << err_msg;
+ }
+ }
+
+ void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) {
+ // this will be the last message. Only the active logworker can receive a FATAL call so it's
+ // safe to shutdown logging now
+ g3::internal::shutDownLogging();
+
+ std::string reason = msgPtr.get()->reason();
+ const auto level = msgPtr.get()->_level;
+ const auto fatal_id = msgPtr.get()->_signal_id;
+
+
+ std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
+ uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
+
+
+ // Change output in case of a fatal signal (or windows exception)
+ std::string exiting = {"Fatal type: "};
+
+ uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason)
+ .append("\nLog content flushed sucessfully to sink\n\n");
+
+ std::cerr << uniqueMsg->toString() << std::flush;
+ for (auto& sink : _sinks) {
+ LogMessage msg(*(uniqueMsg));
+ sink->send(LogMessageMover(std::move(msg)));
+ }
+
+
+ // This clear is absolutely necessary
+ // All sinks are forced to receive the fatal message above before we continue
+ _sinks.clear(); // flush all queues
+ internal::exitWithDefaultSignalHandler(level, fatal_id);
+
+ // should never reach this point
+ perror("g3log exited after receiving FATAL trigger. Flush message status: ");
+ }
+
+ LogWorker::~LogWorker() {
+ g3::internal::shutDownLoggingForActiveOnly(this);
+
+ // The sinks WILL automatically be cleared at exit of this destructor
+ // However, the waiting below ensures that all messages until this point are taken care of
+ // before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
+ // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling
+ // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl"
+ //
+ // Any messages put into the queue will be OK due to:
+ // *) If it is before the wait below then they will be executed
+ // *) If it is AFTER the wait below then they will be ignored and NEVER executed
+ auto bg_clear_sink_call = [this] { _impl._sinks.clear(); };
+ auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
+ token_cleared.wait();
+
+ // The background worker WILL be automatically cleared at the exit of the destructor
+ // However, the explicitly clearing of the background worker (below) makes sure that there can
+ // be no thread that manages to add another sink after the call to clear the sinks above.
+ // i.e. this manages the extremely unlikely case of another thread calling
+ // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
+ // and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to
+ // other threads that do not know the state of LogWorker is considered a bug but it is dealt with
+ // nonetheless below.
+ //
+ // If sinks would already have been added after the sink clear above then this reset will deal with it
+ // without risking lambda execution with a partially deconstructed LogWorkerImpl
+ // Calling g3::spawn_task on a nullptr Active object will not crash but return
+ // a future containing an appropriate exception.
+ _impl._bg.reset(nullptr);
+ }
+
+ void LogWorker::save(LogMessagePtr msg) {
+ _impl._bg->send([this, msg] {_impl.bgSave(msg); });
+ }
+
+ void LogWorker::fatal(FatalMessagePtr fatal_message) {
+ _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); });
+ }
+
+ void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
+ auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);};
+ auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
+ token_done.wait();
+ }
+
+ std::unique_ptr<LogWorker> LogWorker::createLogWorker() {
+ return std::unique_ptr<LogWorker>(new LogWorker);
+ }
+
+ std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
+ return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
+ }
+
+
+
+
+} // g3
diff --git a/g3log/stacktrace_windows.cpp b/g3log/stacktrace_windows.cpp
new file mode 100644
index 0000000..cfdd69f
--- /dev/null
+++ b/g3log/stacktrace_windows.cpp
@@ -0,0 +1,222 @@
+/** ==========================================================================
+ * Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for
+ * the benefit of g3log. It was originally published at:
+ * http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
+
+ * 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod).
+ *
+ * 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.
+ *
+ * For more information see g3log/LICENSE or refer refer to http://unlicense.org
+ * ============================================================================*/
+
+#include "g3log/stacktrace_windows.hpp"
+
+#include <windows.h>
+#include <dbghelp.h>
+#include <map>
+#include <memory>
+#include <cassert>
+#include <vector>
+#include <mutex>
+#include <g3log/g3log.hpp>
+
+#pragma comment(lib, "dbghelp.lib")
+
+
+#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+#error "stacktrace_win.cpp used but not on a windows system"
+#endif
+
+
+
+#define g3_MAP_PAIR_STRINGIFY(x) {x, #x}
+
+namespace {
+ thread_local size_t g_thread_local_recursive_crash_check = 0;
+
+ const std::map<g3::SignalType, std::string> kExceptionsAsText = {
+ g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT)
+ , g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
+
+ };
+
+
+ // Using the given context, fill in all the stack frames.
+ // Which then later can be interpreted to human readable text
+ void captureStackTrace(CONTEXT *context, std::vector<uint64_t> &frame_pointers) {
+ DWORD machine_type = 0;
+ STACKFRAME64 frame = {}; // force zeroeing
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+#ifdef _M_X64
+ frame.AddrPC.Offset = context->Rip;
+ frame.AddrFrame.Offset = context->Rbp;
+ frame.AddrStack.Offset = context->Rsp;
+ machine_type = IMAGE_FILE_MACHINE_AMD64;
+#else
+ frame.AddrPC.Offset = context->Eip;
+ frame.AddrPC.Offset = context->Ebp;
+ frame.AddrPC.Offset = context->Esp;
+ machine_type = IMAGE_FILE_MACHINE_I386;
+#endif
+ for (size_t index = 0; index < frame_pointers.size(); ++index)
+ {
+ if (StackWalk64(machine_type,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &frame,
+ context,
+ NULL,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ NULL)) {
+ frame_pointers[index] = frame.AddrPC.Offset;
+ } else {
+ break;
+ }
+ }
+ }
+
+
+
+ // extract readable text from a given stack frame. All thanks to
+ // using SymFromAddr and SymGetLineFromAddr64 with the stack pointer
+ std::string getSymbolInformation(const size_t index, const std::vector<uint64_t> &frame_pointers) {
+ auto addr = frame_pointers[index];
+ std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
+
+ DWORD64 displacement64;
+ DWORD displacement;
+ char symbol_buffer[sizeof(SYMBOL_INFO) + 256];
+ SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(symbol_buffer);
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+
+ IMAGEHLP_LINE64 line;
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ std::string lineInformation;
+ std::string callInformation;
+ if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) {
+ callInformation.append(" ").append({symbol->Name, symbol->NameLen});
+ if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) {
+ lineInformation.append("\t").append(line.FileName).append(" L: ");
+ lineInformation.append(std::to_string(line.LineNumber));
+ }
+ }
+ frame_dump.append(lineInformation).append(callInformation);
+ return frame_dump;
+ }
+
+
+ // Retrieves all the symbols for the stack frames, fills them witin a text representation and returns it
+ std::string convertFramesToText(std::vector<uint64_t> &frame_pointers) {
+ std::string dump; // slightly more efficient than ostringstream
+ const size_t kSize = frame_pointers.size();
+ for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
+ dump += getSymbolInformation(index, frame_pointers);
+ dump += "\n";
+ }
+ return dump;
+ }
+} // anonymous
+
+
+
+
+namespace stacktrace {
+ const std::string kUnknown = {"UNKNOWN EXCEPTION"};
+ /// return the text description of a Windows exception code
+ /// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
+ std::string exceptionIdToText(g3::SignalType id) {
+ const auto iter = kExceptionsAsText.find(id);
+ if ( iter == kExceptionsAsText.end()) {
+ std::string unknown = {kUnknown + ":" + std::to_string(id)};
+ return unknown;
+ }
+ return iter->second;
+ }
+
+ /// Yes a double lookup: first for isKnownException and then exceptionIdToText
+ /// for vectored exceptions we only deal with known exceptions so this tiny
+ /// overhead we can live with
+ bool isKnownException(g3::SignalType id) {
+ return (kExceptionsAsText.end() != kExceptionsAsText.find(id));
+ }
+
+ /// helper function: retrieve stackdump from no excisting exception pointer
+ std::string stackdump() {
+ CONTEXT current_context;
+ memset(¤t_context, 0, sizeof(CONTEXT));
+ RtlCaptureContext(¤t_context);
+ return stackdump(¤t_context);
+ }
+
+ /// helper function: retrieve stackdump, starting from an exception pointer
+ std::string stackdump(EXCEPTION_POINTERS *info) {
+ auto context = info->ContextRecord;
+ return stackdump(context);
+
+ }
+
+
+ /// main stackdump function. retrieve stackdump, from the given context
+ std::string stackdump(CONTEXT *context) {
+
+ if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass
+ std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
+ recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
+ return recursive_crash;
+ }
+ ++g_thread_local_recursive_crash_check;
+
+ static std::mutex m;
+ std::lock_guard<std::mutex> lock(m);
+ {
+ const BOOL kLoadSymModules = TRUE;
+ const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
+ if (TRUE != initialized) {
+ return { "Error: Cannot call SymInitialize(...) for retrieving symbols in stack" };
+ }
+
+ std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void *) {
+ SymCleanup(GetCurrentProcess());
+ }); // Raii sym cleanup
+
+
+ const size_t kmax_frame_dump_size = 64;
+ std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
+ // C++11: size set and values are zeroed
+
+ assert(frame_pointers.size() == kmax_frame_dump_size);
+ captureStackTrace(context, frame_pointers);
+ return convertFramesToText(frame_pointers);
+ }
+ }
+
+} // stacktrace
+
+
+
diff --git a/g3log/time.cpp b/g3log/time.cpp
new file mode 100644
index 0000000..937e5d0
--- /dev/null
+++ b/g3log/time.cpp
@@ -0,0 +1,191 @@
+/** ==========================================================================
+* 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.
+*
+* For more information see g3log/LICENSE or refer refer to http://unlicense.org
+* ============================================================================*/
+
+#include "g3log/time.hpp"
+
+#include <sstream>
+#include <string>
+#include <cstring>
+#include <cmath>
+#include <chrono>
+#include <cassert>
+#include <iomanip>
+#ifdef __MACH__
+#include <sys/time.h>
+#endif
+
+namespace g3 {
+ namespace internal {
+ const std::string kFractionalIdentier = "%f";
+ const size_t kFractionalIdentierSize = 2;
+
+
+
+ Fractional getFractional(const std::string& format_buffer, size_t pos) {
+ char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
+ Fractional type = Fractional::NanosecondDefault;
+ switch (ch) {
+ case '3': type = Fractional::Millisecond; break;
+ case '6': type = Fractional::Microsecond; break;
+ case '9': type = Fractional::Nanosecond; break;
+ default: type = Fractional::NanosecondDefault; break;
+ }
+ return type;
+ }
+
+
+ // Returns the fractional as a string with padded zeroes
+ // 1 ms --> 001
+ // 1 us --> 000001
+ // 1 ns --> 000000001
+ std::string to_string(const timespec& time_snapshot, Fractional fractional) {
+ auto ns = time_snapshot.tv_nsec;
+ auto zeroes = 9; // default ns
+ auto digitsToCut = 1; // default ns, divide by 1 makes no change
+ switch (fractional) {
+ case Fractional::Millisecond : {
+ zeroes = 3;
+ digitsToCut = 1000000;
+ break;
+ }
+ case Fractional::Microsecond : {
+ zeroes = 6;
+ digitsToCut = 1000;
+ break;
+ }
+ case Fractional::Nanosecond :
+ case Fractional::NanosecondDefault:
+ default:
+ zeroes = 9;
+ digitsToCut = 1;
+
+ }
+
+ ns /= digitsToCut;
+ // auto value = std::to_string(typeAdjustedValue);
+ // return value; // std::string(fractional_digit, '0') + value;
+ auto value = std::string(std::to_string(ns));
+ return std::string(zeroes - value.size(), '0') + value;
+ }
+ } // internal
+} // g3
+
+
+
+namespace g3 {
+ struct timespec systemtime_now() {
+ struct timespec ts;
+ timespec_get(&ts);
+ return ts;
+ }
+
+
+ // std::timespec_get or posix clock_gettime)(...) are not
+ // implemented on OSX and ubuntu gcc5 has no support for std::timespec_get(...) as of yet
+ // so instead we roll our own.
+ int timespec_get(struct timespec* ts/*, int base*/) {
+ using namespace std::chrono;
+
+ // thanks @AndreasSchoenle for the implementation and the explanation:
+ // The time since epoch for the steady_clock is not necessarily really the time since 1970.
+ // It usually is the time since program start. Thus, here is calculated the offset between
+ // the starting point and the real start of the epoch as reported by the system clock
+ // with the precision of the system clock.
+ //
+ // Time stamps will later have system clock accuracy but relative times will have the precision
+ // of the high resolution clock.
+ thread_local const auto os_system =
+ time_point_cast<nanoseconds>(system_clock::now()).time_since_epoch();
+ thread_local const auto os_high_resolution =
+ time_point_cast<nanoseconds>(high_resolution_clock::now()).time_since_epoch();
+ thread_local auto os = os_system - os_high_resolution;
+
+ // 32-bit system work-around, where apparenetly the os correction above could sometimes
+ // become negative. This correction will only be done once per thread
+ if (os.count() < 0 ) {
+ os = os_system;
+ }
+
+ auto now_ns = (time_point_cast<nanoseconds>(high_resolution_clock::now()).time_since_epoch() + os).count();
+ const auto kNanos = 1000000000;
+ ts ->tv_sec = now_ns / kNanos;
+ ts ->tv_nsec = now_ns % kNanos;
+ #ifdef TIME_UTC
+ return TIME_UTC;
+ #endif
+ return 1;
+ }
+
+
+
+ // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
+ // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
+ // return value is SIMPLIFIED to only return a std::string
+ std::string put_time(const struct tm* tmb, const char* c_time_format) {
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
+ std::ostringstream oss;
+ oss.fill('0');
+ // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
+ oss << std::put_time(const_cast<struct tm*> (tmb), c_time_format);
+ return oss.str();
+#else // LINUX
+ const size_t size = 1024;
+ char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
+ // ... also ... This is way more buffer space then we need
+
+ auto success = std::strftime(buffer, size, c_time_format, tmb);
+ // In DEBUG the assert will trigger a process exit. Once inside the if-statement
+ // the 'always true' expression will be displayed as reason for the exit
+ //
+ // In Production mode
+ // the assert will do nothing but the format string will instead be returned
+ if (0 == success) {
+ assert((0 != success) && "strftime fails with illegal formatting");
+ return c_time_format;
+ }
+
+ return buffer;
+#endif
+ }
+
+
+
+ tm localtime(const std::time_t& time) {
+ struct tm tm_snapshot;
+#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
+ localtime_s(&tm_snapshot, &time); // windsows
+#else
+ localtime_r(&time, &tm_snapshot); // POSIX
+#endif
+ return tm_snapshot;
+ }
+
+
+
+
+ std::string localtime_formatted(const timespec& time_snapshot, const std::string& time_format) {
+ auto format_buffer = time_format; // copying format string to a separate buffer
+
+ // iterating through every "%f" instance in the format string
+ auto identifierExtraSize = 0;
+ for (size_t pos = 0; (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
+ // figuring out whether this is nano, micro or milli identifier
+ auto type = g3::internal::getFractional(format_buffer, pos);
+ auto value = g3::internal::to_string(time_snapshot, type);
+ auto padding = 0;
+ if (type != g3::internal::Fractional::NanosecondDefault) {
+ padding = 1;
+ }
+
+ // replacing "%f[3|6|9]" with sec fractional part value
+ format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value);
+ }
+ std::tm t = localtime(time_snapshot.tv_sec);
+ return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
+ }
+} // g3