incremental
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
+
+
+