Another, tiny, breaking change.

Due to popular request I have added the possibility to put in your own custom log levels. Due to performance regards changes to the log levels should ONLY be done
in a single thread context (initialiation) for that reason the namespace
used to "setLogLevel" is named appropriately
i.e. use: g3::only_change_at_initialization::setLogLevel(...)
This commit is contained in:
Kjell Hedstrom 2015-07-20 08:00:33 -06:00
parent c4f9463b6b
commit 786b92a6d1
6 changed files with 170 additions and 47 deletions

View File

@ -23,6 +23,8 @@
#include "g3log/logworker.hpp" #include "g3log/logworker.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/logmessage.hpp" #include "g3log/logmessage.hpp"
#include "g3log/loglevels.hpp"
#include <cstdio> // vsnprintf #include <cstdio> // vsnprintf
#include <mutex> #include <mutex>
@ -124,7 +126,12 @@ namespace g3 {
*/ */
void shutDownLogging() { void shutDownLogging() {
std::lock_guard<std::mutex> lock(g_logging_init_mutex); std::lock_guard<std::mutex> lock(g_logging_init_mutex);
#ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::reset();
#endif
g_logger_instance = nullptr; g_logger_instance = nullptr;
} }
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further

View File

@ -8,18 +8,17 @@
#pragma once #pragma once
// Users of Juce or other libraries might have a define DEBUG which clashes with // 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 // 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>
@ -52,8 +51,8 @@ const LEVELS DBUG {g3::kDebugVaulue, {"DEBUG"}},
#else #else
const LEVELS DEBUG {g3::kDebugVaulue, {"DEBUG"}}, const LEVELS DEBUG {g3::kDebugVaulue, {"DEBUG"}},
#endif #endif
INFO {g3::kDebugVaulue + 1, {"INFO"}}, INFO {g3::kDebugVaulue + 1, {"INFO"}},
WARNING {INFO.value + 1, {"WARNING"}}, WARNING {INFO.value + 1, {"WARNING"}},
@ -67,31 +66,37 @@ WARNING {INFO.value + 1, {"WARNING"}},
// const LEVELS MYFATAL {FATAL.value +1, {"MyFatalLevel"}}; // const LEVELS MYFATAL {FATAL.value +1, {"MyFatalLevel"}};
// //
// IMPORTANT: As of yet dynamic on/off of logging is NOT changed automatically // 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, // any changes of this, if you use dynamic on/off must be done in loglevels.cpp,
// g_log_level_status and // g_log_level_status and
// void setLogLevel(LEVELS log_level, bool enabled) {...} // void setLogLevel(LEVELS log_level, bool enabled) {...}
// bool logLevel(LEVELS log_level){...} // bool logLevel(LEVELS log_level){...}
// 1) Remember to update the FATAL initialization below // 1) Remember to update the FATAL initialization below
// 2) Remember to update the initialization of "g3loglevels.cpp/g_log_level_status" // 2) Remember to update the initialization of "g3loglevels.cpp/g_log_level_status"
FATAL {WARNING.value + 500, {"FATAL"}}; FATAL {500, {"FATAL"}};
namespace g3 { namespace g3 {
namespace internal { namespace internal {
const LEVELS CONTRACT {1000, {"CONTRACT"}}, const LEVELS CONTRACT {1000, {"CONTRACT"}},
FATAL_SIGNAL {1001, {"FATAL_SIGNAL"}}, FATAL_SIGNAL {1001, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {1002, {"FATAL_EXCEPTION"}}; FATAL_EXCEPTION {1002, {"FATAL_EXCEPTION"}};
/// 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
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL} // Only safe if done at initialization in a single-thread context
void setLogLevel(LEVELS level, bool enabled_status); 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 #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
* ============================================================================*/ * ============================================================================*/
@ -10,7 +10,30 @@
#include "g3log/g3log.hpp" #include "g3log/g3log.hpp"
#include <atomic> #include <atomic>
#include <cassert> #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 g3 {
namespace internal { namespace internal {
bool wasFatal(const LEVELS &level) { bool wasFatal(const LEVELS &level) {
@ -22,24 +45,37 @@ namespace g3 {
const int g_level_size { const int g_level_size {
FATAL.value + 1 FATAL.value + 1
}; };
std::atomic<bool> g_log_level_status[4] {{true}, {true}, {true}, {true}}; //std::atomic<bool> g_log_level_status[4] {{true}, {true}, {true}, {true}};
std::map<int, atomicbool> g_log_level_status = {{DEBUG.value, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
#endif #endif
} // internal } // internal
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
void setLogLevel(LEVELS log_level, bool enabled) { namespace only_change_at_initialization {
assert(internal::g_level_size == 4 && "Mismatch between number of logging levels and their use"); void setLogLevel(LEVELS log_level, bool enabled) {
int level = log_level.value; int level = log_level.value;
CHECK((level >= g3::kDebugVaulue) && (level <= FATAL.value)); internal::g_log_level_status[level].get().store(enabled, std::memory_order_release);
internal::g_log_level_status[level].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 = {{DEBUG.value, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
}
} // only_change_at_initialization
#endif #endif
bool logLevel(LEVELS log_level) { bool logLevel(LEVELS log_level) {
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
int level = log_level.value; int level = log_level.value;
CHECK((level >= g3::kDebugVaulue) && (level <= FATAL.value)); bool status = internal::g_log_level_status[level].value();
bool status = (internal::g_log_level_status[level].load(std::memory_order_acquire));
return status; return status;
#endif #endif
return true; return true;

View File

@ -54,7 +54,8 @@ TEST(Initialization, No_Logger_Initialized___LevelsAreONByDefault) {
EXPECT_EQ(DEBUG.value, 0); EXPECT_EQ(DEBUG.value, 0);
EXPECT_EQ(INFO.value, 1); EXPECT_EQ(INFO.value, 1);
EXPECT_EQ(WARNING.value, 2); EXPECT_EQ(WARNING.value, 2);
EXPECT_EQ(FATAL.value, 3); EXPECT_EQ(FATAL.value, 500);
EXPECT_EQ(g3::internal::CONTRACT.value, 1000);
} }
TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKish) { TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKish) {
@ -421,6 +422,9 @@ 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
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
#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());
@ -433,9 +437,13 @@ TEST(CustomLogLevels, AddANonFatal){
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));
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
g3::only_change_at_initialization::setLogLevel(DEADLY, true);
#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();
@ -459,13 +467,80 @@ namespace {
RestoreDynamicLoggingLevels() { RestoreDynamicLoggingLevels() {
}; };
~RestoreDynamicLoggingLevels() { ~RestoreDynamicLoggingLevels() {
g3::setLogLevel(DEBUG, false); g3::only_change_at_initialization::reset();
g3::setLogLevel(INFO, false); g3::only_change_at_initialization::setLogLevel(DEBUG, false);
g3::setLogLevel(WARNING, false); g3::only_change_at_initialization::setLogLevel(INFO, false);
g3::setLogLevel(FATAL, false); g3::only_change_at_initialization::setLogLevel(WARNING, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
} }
}; };
} // anonymous } // anonymous
TEST(CustomLogLevels, AddANonFatal__ThenReset){
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
EXPECT_TRUE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::reset();
EXPECT_FALSE(g3::logLevel(MYINFO));
}
TEST(CustomLogLevels, AddANonFatal__ThenResetByLoggingShutdown) {
const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
{
RestoreFileLogger logger(log_directory);
EXPECT_FALSE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
EXPECT_TRUE(g3::logLevel(MYINFO));
}
EXPECT_FALSE(g3::logLevel(MYINFO)) << g3::only_change_at_initialization::printLevels();
}
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1){
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}};
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
}
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2){
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO));
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
}
TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue){
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value +3, {"MY_INFO_LEVEL"}};
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp L: " + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected;
}
TEST(DynamicLogging, DynamicLogging_IS_ENABLED) { TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
RestoreDynamicLoggingLevels raiiLevelRestore; RestoreDynamicLoggingLevels raiiLevelRestore;
@ -473,25 +548,25 @@ TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
ASSERT_TRUE(g3::logLevel(INFO)); ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::setLogLevel(DEBUG, false); g3::only_change_at_initialization::setLogLevel(DEBUG, false);
ASSERT_FALSE(g3::logLevel(DEBUG)); ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO)); ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::setLogLevel(INFO, false); g3::only_change_at_initialization::setLogLevel(INFO, false);
ASSERT_FALSE(g3::logLevel(DEBUG)); ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::setLogLevel(WARNING, false); g3::only_change_at_initialization::setLogLevel(WARNING, false);
ASSERT_FALSE(g3::logLevel(DEBUG)); ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING)); ASSERT_FALSE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::setLogLevel(FATAL, false); g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(DEBUG)); ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING)); ASSERT_FALSE(g3::logLevel(WARNING));
@ -521,7 +596,7 @@ TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
g3::setLogLevel(DEBUG, false); g3::only_change_at_initialization::setLogLevel(DEBUG, false);
EXPECT_FALSE(g3::logLevel(DEBUG)); EXPECT_FALSE(g3::logLevel(DEBUG));
LOG(DEBUG) << msg_debugOff; LOG(DEBUG) << msg_debugOff;
auto content = logger.resetAndRetrieveContent(); auto content = logger.resetAndRetrieveContent();
@ -552,7 +627,7 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
g3::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;
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
@ -575,7 +650,7 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
// Disable also CHECK calls // Disable also CHECK calls
g3::setLogLevel(FATAL, false); g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(FATAL)); ASSERT_FALSE(g3::logLevel(FATAL));
LOG(FATAL) << msg3; LOG(FATAL) << msg3;
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());

View File

@ -46,13 +46,13 @@ TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
EXPECT_FALSE(nullptr == factory); EXPECT_FALSE(nullptr == factory);
SomeLibrary* loadedLibrary = factory->CreateLibrary(); SomeLibrary* loadedLibrary = factory->CreateLibrary();
for (size_t i = 0; i < 300; ++i) { for (auto i = 0; i < 300; ++i) {
loadedLibrary->action(); loadedLibrary->action();
} }
delete loadedLibrary; delete loadedLibrary;
dlclose(libHandle); dlclose(libHandle);
} // scope exit. All log entries must be flushed now } // scope exit. All log entries must be flushed now
const int numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library
EXPECT_EQ(receiver.size(), numberOfMessages); EXPECT_EQ(receiver.size(), numberOfMessages);
} }

View File

@ -121,10 +121,10 @@ namespace testing_helpers {
_log_file = filename.get(); _log_file = filename.get();
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
g3::setLogLevel(INFO, true); g3::only_change_at_initialization::setLogLevel(INFO, true);
g3::setLogLevel(DEBUG, true); g3::only_change_at_initialization::setLogLevel(DEBUG, true);
g3::setLogLevel(WARNING, true); g3::only_change_at_initialization::setLogLevel(WARNING, true);
g3::setLogLevel(FATAL, true); g3::only_change_at_initialization::setLogLevel(FATAL, true);
#endif #endif
} }