Improved copy constructors and assignment operator which is needed now when LOGLEVELS can be made on the fly

This commit is contained in:
Kjell Hedstrom 2015-08-12 06:43:24 -06:00
parent 49aee72faf
commit 8dfe9e0716
5 changed files with 206 additions and 146 deletions

View File

@ -13,16 +13,17 @@
// the DEBUG logging level for G3log. In that case they can instead use the define // the DEBUG logging level for G3log. In that case they can instead use the define
// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG // "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG)) #if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
#if (defined(DBUG)) #if (defined(DBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG" #error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif #endif
#else #else
#if (defined(DEBUG)) #if (defined(DEBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG" #error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif #endif
#endif #endif
#include <string> #include <string>
#include <algorithm>
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod // Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
@ -30,19 +31,32 @@ struct LEVELS {
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a // 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" // "dynamic, runtime loading of shared libraries"
LEVELS(const LEVELS &other): value(other.value), text(other.text.c_str()) {} LEVELS(const LEVELS& other): value(other.value), text(other.text.c_str()) {}
LEVELS(int id, const char *idtext) : value(id), text(idtext) {} LEVELS(int id, const char* idtext) : value(id), text(idtext) {}
bool operator==(const LEVELS &rhs) const { bool operator==(const LEVELS& rhs) const {
return (value == rhs.value && text == rhs.text); return (value == rhs.value && text == rhs.text);
} }
bool operator!=(const LEVELS &rhs) const { bool operator!=(const LEVELS& rhs) const {
return (value != rhs.value || text != rhs.text); return (value != rhs.value || text != rhs.text);
} }
const int value; friend void swap(LEVELS& first, LEVELS& second) {
const std::string text; 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;
}; };
@ -89,7 +103,7 @@ namespace g3 {
/// helper function to tell the logger if a log message was fatal. If it is it will force /// 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 /// a shutdown after all log entries are saved to the sinks
bool wasFatal(const LEVELS &level); bool wasFatal(const LEVELS& level);
} }
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
@ -99,9 +113,9 @@ namespace g3 {
void setLogLevel(LEVELS level, bool enabled_status); void setLogLevel(LEVELS level, bool enabled_status);
std::string printLevels(); std::string printLevels();
void reset(); void reset();
} // only_change_at_initialization } // only_change_at_initialization
#endif #endif
bool logLevel(LEVELS level); bool logLevel(LEVELS level);
} // g3 } // g3

View File

@ -2,7 +2,7 @@
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes * 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 * with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations. * strings attached and no restrictions or obligations.
* *
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
@ -47,7 +47,7 @@ namespace g3 {
/// use a different format string to get a different look on the time. /// use a different format string to get a different look on the time.
// default look is Y/M/D H:M:S // 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 timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
std::string microseconds() const { std::string microseconds() const {
return std::to_string(_microseconds); return std::to_string(_microseconds);
} }
@ -55,7 +55,7 @@ namespace g3 {
std::string message() const { std::string message() const {
return _message; return _message;
} }
std::string &write() const { std::string& write() const {
return _message; return _message;
} }
@ -74,11 +74,14 @@ namespace g3 {
} }
LogMessage(const std::string &file, const int line, const std::string &function, const LEVELS &level); LogMessage& operator=(LogMessage other);
explicit LogMessage(const std::string &fatalOsSignalCrashMessage);
LogMessage(const LogMessage &);
LogMessage(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() {} virtual ~LogMessage() {}
// //
@ -94,6 +97,26 @@ namespace g3 {
LEVELS _level; LEVELS _level;
std::string _expression; // only with content for CHECK(...) calls std::string _expression; // only with content for CHECK(...) calls
mutable std::string _message; mutable std::string _message;
friend void swap(LogMessage& first, LogMessage& second) {
// enable ADL (not necessary in our case, but good practice)
using std::swap;
swap(first._timestamp, second._timestamp);
swap(first._call_thread_id, second._call_thread_id);
swap(first._microseconds, second._microseconds);
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);
}
private:
LogMessage() = default; // only used for internal swap
}; };
@ -103,8 +126,8 @@ namespace g3 {
* A thread that causes a FatalMessage will sleep forever until the * A thread that causes a FatalMessage will sleep forever until the
* application has exited (after message flush) */ * application has exited (after message flush) */
struct FatalMessage : public LogMessage { struct FatalMessage : public LogMessage {
FatalMessage(const LogMessage &details, g3::SignalType signal_id); FatalMessage(const LogMessage& details, g3::SignalType signal_id);
FatalMessage(const FatalMessage &); FatalMessage(const FatalMessage&);
virtual ~FatalMessage() {} virtual ~FatalMessage() {}
LogMessage copyToLogMessage() const; LogMessage copyToLogMessage() const;

View File

@ -2,7 +2,7 @@
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes * 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 * with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations. * strings attached and no restrictions or obligations.
* *
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
@ -10,8 +10,9 @@
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/time.hpp" #include "g3log/time.hpp"
#include "g3log/std2_make_unique.hpp" #include "g3log/std2_make_unique.hpp"
#include <algorithm>
#include <mutex> #include <mutex>
#include <iostream>
namespace { namespace {
std::once_flag g_start_time_flag; std::once_flag g_start_time_flag;
@ -25,7 +26,7 @@ namespace {
return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count(); return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
} }
std::string splitFileName(const std::string &str) { std::string splitFileName(const std::string& str) {
size_t found; size_t found;
found = str.find_last_of("(/\\"); found = str.find_last_of("(/\\");
return str.substr(found + 1); return str.substr(found + 1);
@ -38,7 +39,7 @@ namespace g3 {
// helper for setting the normal log details in an entry // helper for setting the normal log details in an entry
std::string LogDetailsToString(const LogMessage &msg) { std::string LogDetailsToString(const LogMessage& msg) {
std::string out; std::string out;
out.append("\n" + msg.timestamp() + " " + msg.microseconds() + "\t" out.append("\n" + msg.timestamp() + " " + msg.microseconds() + "\t"
+ msg.level() + " [" + msg.file() + " L: " + msg.line() + "]\t"); + msg.level() + " [" + msg.file() + " L: " + msg.line() + "]\t");
@ -47,14 +48,14 @@ namespace g3 {
// helper for normal // helper for normal
std::string normalToString(const LogMessage &msg) { std::string normalToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg); auto out = LogDetailsToString(msg);
out.append('"' + msg.message() + '"'); out.append('"' + msg.message() + '"');
return out; return out;
} }
// helper for fatal signal // helper for fatal signal
std::string fatalSignalToString(const LogMessage &msg) { std::string fatalSignalToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds() out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
@ -64,7 +65,7 @@ namespace g3 {
// helper for fatal exception (windows only) // helper for fatal exception (windows only)
std::string fatalExceptionToString(const LogMessage &msg) { std::string fatalExceptionToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds() out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n"
@ -74,7 +75,7 @@ namespace g3 {
// helper for fatal LOG // helper for fatal LOG
std::string fatalLogToString(const LogMessage &msg) { std::string fatalLogToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg); auto out = LogDetailsToString(msg);
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "}; static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"'); out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
@ -82,7 +83,7 @@ namespace g3 {
} }
// helper for fatal CHECK // helper for fatal CHECK
std::string fatalCheckToString(const LogMessage &msg) { std::string fatalCheckToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg); auto out = LogDetailsToString(msg);
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t"
@ -123,15 +124,21 @@ namespace g3 {
std::string LogMessage::timestamp(const std::string &time_look) const { std::string LogMessage::timestamp(const std::string& time_look) const {
return localtime_formatted(_timestamp, time_look); return 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, LogMessage::LogMessage(const std::string& file, const int line,
const std::string &function, const LEVELS &level) const std::string& function, const LEVELS& level)
: _timestamp(g3::systemtime_now()) : _timestamp(g3::systemtime_now())
, _call_thread_id(std::this_thread::get_id()) , _call_thread_id(std::this_thread::get_id())
, _microseconds(microsecondsCounter()) , _microseconds(microsecondsCounter())
@ -142,12 +149,12 @@ namespace g3 {
{} {}
LogMessage::LogMessage(const std::string &fatalOsSignalCrashMessage) LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage)
: LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) { : LogMessage( {""}, 0, {""}, internal::FATAL_SIGNAL) {
_message.append(fatalOsSignalCrashMessage); _message.append(fatalOsSignalCrashMessage);
} }
LogMessage::LogMessage(const LogMessage &other) LogMessage::LogMessage(const LogMessage& other)
: _timestamp(other._timestamp) : _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id) , _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds) , _microseconds(other._microseconds)
@ -156,21 +163,21 @@ namespace g3 {
, _function(other._function) , _function(other._function)
, _level(other._level) , _level(other._level)
, _expression(other._expression) , _expression(other._expression)
, _message(other._message) , _message(other._message) {
{
} }
LogMessage::LogMessage(LogMessage&& other)
: _timestamp(0)
, _call_thread_id(0)
, _microseconds(0)
, _file("---")
, _line(0)
, _function("---")
, _level(DEBUG)
, _expression("---")
, _message("---") {
LogMessage::LogMessage(LogMessage &&other) swap(*this, other);
: _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds)
, _file(std::move(other._file))
, _line(other._line)
, _function(std::move(other._function))
, _level(other._level)
, _expression(std::move(other._expression))
, _message(std::move(other._message)) {
} }
@ -180,12 +187,12 @@ namespace g3 {
return oss.str(); return oss.str();
} }
FatalMessage::FatalMessage(const LogMessage &details, g3::SignalType signal_id) FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id)
: LogMessage(details), _signal_id(signal_id) { } : LogMessage(details), _signal_id(signal_id) { }
FatalMessage::FatalMessage(const FatalMessage &other) FatalMessage::FatalMessage(const FatalMessage& other)
: LogMessage(other), _signal_id(other._signal_id) {} : LogMessage(other), _signal_id(other._signal_id) {}

View File

@ -13,32 +13,32 @@
#include "g3log/stacktrace_windows.hpp" #include "g3log/stacktrace_windows.hpp"
#include <windows.h> #include <windows.h>
TEST(CrashHandler_Windows, ExceptionType) { TEST(CrashHandler_Windows, ExceptionType) {
EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123"); EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123");
EXPECT_EQ(stacktrace::exceptionIdToText(1), "UNKNOWN EXCEPTION:1"); EXPECT_EQ(stacktrace::exceptionIdToText(1), "UNKNOWN EXCEPTION:1");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ACCESS_VIOLATION), "EXCEPTION_ACCESS_VIOLATION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ACCESS_VIOLATION), "EXCEPTION_ACCESS_VIOLATION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), "EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_BREAKPOINT),"EXCEPTION_BREAKPOINT"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_BREAKPOINT), "EXCEPTION_BREAKPOINT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_DATATYPE_MISALIGNMENT),"EXCEPTION_DATATYPE_MISALIGNMENT"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_DATATYPE_MISALIGNMENT), "EXCEPTION_DATATYPE_MISALIGNMENT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DENORMAL_OPERAND),"EXCEPTION_FLT_DENORMAL_OPERAND"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DENORMAL_OPERAND), "EXCEPTION_FLT_DENORMAL_OPERAND");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DIVIDE_BY_ZERO),"EXCEPTION_FLT_DIVIDE_BY_ZERO"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DIVIDE_BY_ZERO), "EXCEPTION_FLT_DIVIDE_BY_ZERO");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT),"EXCEPTION_FLT_INEXACT_RESULT"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT),"EXCEPTION_FLT_INEXACT_RESULT"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INVALID_OPERATION),"EXCEPTION_FLT_INVALID_OPERATION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INVALID_OPERATION), "EXCEPTION_FLT_INVALID_OPERATION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_OVERFLOW),"EXCEPTION_FLT_OVERFLOW"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_OVERFLOW), "EXCEPTION_FLT_OVERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_STACK_CHECK),"EXCEPTION_FLT_STACK_CHECK"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_STACK_CHECK), "EXCEPTION_FLT_STACK_CHECK");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_UNDERFLOW),"EXCEPTION_FLT_UNDERFLOW"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_UNDERFLOW), "EXCEPTION_FLT_UNDERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ILLEGAL_INSTRUCTION),"EXCEPTION_ILLEGAL_INSTRUCTION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ILLEGAL_INSTRUCTION), "EXCEPTION_ILLEGAL_INSTRUCTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_IN_PAGE_ERROR),"EXCEPTION_IN_PAGE_ERROR"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_IN_PAGE_ERROR), "EXCEPTION_IN_PAGE_ERROR");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_DIVIDE_BY_ZERO),"EXCEPTION_INT_DIVIDE_BY_ZERO"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_DIVIDE_BY_ZERO), "EXCEPTION_INT_DIVIDE_BY_ZERO");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_OVERFLOW),"EXCEPTION_INT_OVERFLOW"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_OVERFLOW), "EXCEPTION_INT_OVERFLOW");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INVALID_DISPOSITION),"EXCEPTION_INVALID_DISPOSITION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INVALID_DISPOSITION), "EXCEPTION_INVALID_DISPOSITION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_NONCONTINUABLE_EXCEPTION),"EXCEPTION_NONCONTINUABLE_EXCEPTION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_NONCONTINUABLE_EXCEPTION), "EXCEPTION_NONCONTINUABLE_EXCEPTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_PRIV_INSTRUCTION),"EXCEPTION_PRIV_INSTRUCTION"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_PRIV_INSTRUCTION), "EXCEPTION_PRIV_INSTRUCTION");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_SINGLE_STEP),"EXCEPTION_SINGLE_STEP"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_SINGLE_STEP), "EXCEPTION_SINGLE_STEP");
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW),"EXCEPTION_STACK_OVERFLOW"); EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW), "EXCEPTION_STACK_OVERFLOW");
} }
#endif // defined WIN32 #endif // defined WIN32

View File

@ -2,7 +2,7 @@
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes * 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 * with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations. * strings attached and no restrictions or obligations.
* *
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
@ -21,18 +21,18 @@
#include <algorithm> #include <algorithm>
namespace { namespace {
const std::string log_directory = "./"; const std::string log_directory = "./";
const std::string t_info = "test INFO "; const std::string t_info = "test INFO ";
const std::string t_info2 = "test INFO 123"; const std::string t_info2 = "test INFO 123";
const std::string t_debug = "test DEBUG "; const std::string t_debug = "test DEBUG ";
const std::string t_debug3 = "test DEBUG 1.123456"; const std::string t_debug3 = "test DEBUG 1.123456";
const std::string t_warning = "test WARNING "; const std::string t_warning = "test WARNING ";
const std::string t_warning3 = "test WARNING yello"; const std::string t_warning3 = "test WARNING yello";
std::atomic<size_t> g_fatal_counter = {0}; std::atomic<size_t> g_fatal_counter = {0};
void fatalCounter() { void fatalCounter() {
++g_fatal_counter; ++g_fatal_counter;
} }
} // end anonymous namespace } // end anonymous namespace
@ -40,12 +40,12 @@ void fatalCounter() {
using namespace testing_helpers; using namespace testing_helpers;
/// THIS MUST BE THE FIRST UNIT TEST TO RUN! If any unit test run before this /// THIS MUST BE THE FIRST UNIT TEST TO RUN! If any unit test run before this
/// one then it could fail. For dynamic levels all levels are turned on only AT /// one then it could fail. For dynamic levels all levels are turned on only AT
/// instantiation so we do different test for dynamic logging levels /// instantiation so we do different test for dynamic logging levels
/// ///
/// TODO : (gtest issue) /// TODO : (gtest issue)
///Move out to separate unit test binary to ensure reordering of tests does not happen ///Move out to separate unit test binary to ensure reordering of tests does not happen
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
TEST(Initialization, No_Logger_Initialized___LevelsAreONByDefault) { TEST(Initialization, No_Logger_Initialized___LevelsAreONByDefault) {
EXPECT_FALSE(g3::internal::isLoggingInitialized()); EXPECT_FALSE(g3::internal::isLoggingInitialized());
@ -83,7 +83,7 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call if debug level would be ON. auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call if debug level would be ON.
ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]"; ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
} }
#else #else
TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKish) { TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKish) {
@ -111,23 +111,38 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]"; ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
} }
#endif // #ifdef G3_DYNAMIC_LOGGING #endif // #ifdef G3_DYNAMIC_LOGGING
TEST(Basics, Levels) { TEST(Basics, Levels_StdFind) {
std::vector<LEVELS> levels = {INFO, WARNING, FATAL}; std::vector<LEVELS> levels = {INFO, WARNING, FATAL};
auto info = INFO; auto info = INFO;
auto warning = WARNING; auto warning = WARNING;
auto debug = DEBUG;
auto found_info = std::find(levels.begin(), levels.end(), info); auto found_info = std::find(levels.begin(), levels.end(), info);
EXPECT_TRUE(found_info != levels.end()); EXPECT_TRUE(found_info != levels.end());
auto found_warning = std::find(levels.begin(), levels.end(), WARNING); bool wasFound = (levels.end() != std::find(levels.begin(), levels.end(), info));
EXPECT_TRUE(found_warning != levels.end()); EXPECT_TRUE(wasFound);
auto not_found_debug = std::find(levels.begin(), levels.end(), DEBUG); auto wasNotFound = (levels.end() == std::find(levels.begin(), levels.end(), debug));
EXPECT_FALSE(not_found_debug!= levels.end()); EXPECT_TRUE(wasNotFound);
EXPECT_NE(INFO, WARNING);
auto foundWarningIterator = std::find(levels.begin(), levels.end(), WARNING);
EXPECT_TRUE(foundWarningIterator != levels.end());
foundWarningIterator = std::find(levels.begin(), levels.end(), warning);
EXPECT_TRUE(foundWarningIterator != levels.end());
auto wasNotFoundIterator = std::find(levels.begin(), levels.end(), DEBUG);
EXPECT_FALSE(wasNotFoundIterator != levels.end());
}
TEST(Basics, Levels_Operator) {
auto info = INFO;
auto warning = WARNING;
EXPECT_NE(INFO, WARNING);
EXPECT_EQ(info, INFO); EXPECT_EQ(info, INFO);
EXPECT_TRUE(INFO == INFO); EXPECT_TRUE(INFO == INFO);
EXPECT_FALSE(info == WARNING); EXPECT_FALSE(info == WARNING);
@ -302,12 +317,13 @@ TEST(LogTest, LOG_preFatalLogging_hook) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0); g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter); g3::setFatalPreLoggingHook(fatalCounter);
LOG(FATAL) << "This message is fatal"; LOG(FATAL) << "This message is fatal";
logger.reset(); logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{1}); EXPECT_EQ(g_fatal_counter.load(), size_t{1});
} }
{ // Now with no fatal pre-logging-hook {
// Now with no fatal pre-logging-hook
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0); g_fatal_counter.store(0);
@ -321,14 +337,14 @@ TEST(LogTest, LOG_preFatalLogging_hook) {
TEST(LogTest, LOG_FATAL) { TEST(LogTest, LOG_FATAL) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
LOG(FATAL) << "This message is fatal"; LOG(FATAL) << "This message is fatal";
EXPECT_TRUE(mockFatalWasCalled()); EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal")) EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
<< "\ncontent: [[" << mockFatalMessage() << "]]"; << "\ncontent: [[" << mockFatalMessage() << "]]";
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
logger.reset(); logger.reset();
@ -441,31 +457,31 @@ TEST(CHECK, CHECK_ThatWontThrow) {
TEST(CustomLogLevels, AddANonFatal){ TEST(CustomLogLevels, AddANonFatal) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +1, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO {WARNING.value + 1, {"MY_INFO_LEVEL"}};
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::setLogLevel(MYINFO, true); g3::only_change_at_initialization::setLogLevel(MYINFO, true);
#endif #endif
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n" << expected;
} }
TEST(CustomLogLevels, AddFatal){ TEST(CustomLogLevels, AddFatal) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS DEADLY {FATAL.value +1, {"DEADLY"}}; const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}};
EXPECT_TRUE(g3::internal::wasFatal(DEADLY)); EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
g_fatal_counter.store(0); g_fatal_counter.store(0);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
g3::setFatalPreLoggingHook(fatalCounter); g3::setFatalPreLoggingHook(fatalCounter);
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::setLogLevel(DEADLY, true); g3::only_change_at_initialization::setLogLevel(DEADLY, true);
#endif #endif
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__; LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
logger.reset(); logger.reset();
@ -475,13 +491,13 @@ TEST(CustomLogLevels, AddFatal){
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "DEADLY [test_io.cpp L: " + std::to_string(line); expected += "DEADLY [test_io.cpp L: " + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n" << expected;
g_fatal_counter.store(0); // restore g_fatal_counter.store(0); // restore
} }
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
namespace { namespace {
// Restore dynamic levels if turned off // Restore dynamic levels if turned off
@ -499,9 +515,9 @@ namespace {
} // anonymous } // anonymous
TEST(CustomLogLevels, AddANonFatal__ThenReset){ TEST(CustomLogLevels, AddANonFatal__ThenReset) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO)); EXPECT_FALSE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::setLogLevel(MYINFO, true); g3::only_change_at_initialization::setLogLevel(MYINFO, true);
EXPECT_TRUE(g3::logLevel(MYINFO)); EXPECT_TRUE(g3::logLevel(MYINFO));
@ -510,22 +526,22 @@ TEST(CustomLogLevels, AddANonFatal__ThenReset){
} }
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1){ TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels(); << "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
} }
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2){ TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO)); EXPECT_FALSE(g3::logLevel(MYINFO));
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset(); logger.reset();
@ -533,21 +549,21 @@ TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2){
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels(); << "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
} }
TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue){ TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +3, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO {WARNING.value + 3, {"MY_INFO_LEVEL"}};
g3::only_change_at_initialization::setLogLevel(MYINFO, true); g3::only_change_at_initialization::setLogLevel(MYINFO, true);
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n" << expected;
} }
@ -584,7 +600,7 @@ TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
} }
TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) { TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
ASSERT_TRUE(g3::logLevel(DEBUG)); ASSERT_TRUE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO)); ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
@ -592,16 +608,16 @@ TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
} }
RestoreDynamicLoggingLevels raiiLevelRestore; RestoreDynamicLoggingLevels raiiLevelRestore;
std::string msg_debugOn = "This %s SHOULD appear in the %s"; std::string msg_debugOn = "This %s SHOULD appear in the %s";
std::string msg_debugOff = "This message should never appear in the log"; std::string msg_debugOff = "This message should never appear in the log";
std::string msg_info1 = "This info msg log"; std::string msg_info1 = "This info msg log";
try { try {
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
LOGF(DEBUG, msg_debugOn.c_str(), "msg", "log"); LOGF(DEBUG, msg_debugOn.c_str(), "msg", "log");
auto content = logger.resetAndRetrieveContent(); auto content = logger.resetAndRetrieveContent();
ASSERT_TRUE(verifyContent(content, "This msg SHOULD appear in the log")) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, "This msg SHOULD appear in the log")) << "Content: [" << content << "]";
} }
{ {
@ -612,8 +628,8 @@ TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
auto content = logger.resetAndRetrieveContent(); auto content = logger.resetAndRetrieveContent();
ASSERT_FALSE(verifyContent(content, "This message should never appear in the log")) << "Content: [" << content << "]"; ASSERT_FALSE(verifyContent(content, "This message should never appear in the log")) << "Content: [" << content << "]";
} }
} catch (std::exception const &e) { } catch (std::exception const& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
ADD_FAILURE() << "Should never have thrown"; ADD_FAILURE() << "Should never have thrown";
} }
@ -632,11 +648,11 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
EXPECT_TRUE(mockFatalWasCalled()); EXPECT_TRUE(mockFatalWasCalled());
EXPECT_FALSE(mockFatalMessage().empty()); EXPECT_FALSE(mockFatalMessage().empty());
EXPECT_TRUE(verifyContent(mockFatalMessage(), msg1)); EXPECT_TRUE(verifyContent(mockFatalMessage(), msg1));
clearMockFatal(); clearMockFatal();
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
g3::only_change_at_initialization::setLogLevel(FATAL, false); g3::only_change_at_initialization::setLogLevel(FATAL, false);
std::string msg3 = "This is NOT fatal (not crash, since it is unit test. FATAL is disabled"; std::string msg3 = "This is NOT fatal (not crash, since it is unit test. FATAL is disabled";
LOG(FATAL) << msg3; LOG(FATAL) << msg3;
@ -655,10 +671,10 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
LOG(FATAL) << msg1; LOG(FATAL) << msg1;
EXPECT_TRUE(mockFatalWasCalled()); EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), msg1)); EXPECT_TRUE(verifyContent(mockFatalMessage(), msg1));
clearMockFatal(); clearMockFatal();
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
// Disable also CHECK calls // Disable also CHECK calls
g3::only_change_at_initialization::setLogLevel(FATAL, false); g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(FATAL)); ASSERT_FALSE(g3::logLevel(FATAL));
@ -669,7 +685,7 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
#else #else
TEST(DynamicLogging, DynamicLogging_IS_NOT_ENABLED) { TEST(DynamicLogging, DynamicLogging_IS_NOT_ENABLED) {
ASSERT_TRUE(g3::logLevel(DEBUG)); ASSERT_TRUE(g3::logLevel(DEBUG));
//g3::setLogLevel(DEBUG, false); this line will not compile since G3_DYNAMIC_LOGGING is not enabled. Kept for show. //g3::setLogLevel(DEBUG, false); this line will not compile since G3_DYNAMIC_LOGGING is not enabled. Kept for show.