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/crashhandler.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/loglevels.hpp"
#include <cstdio> // vsnprintf
#include <mutex>
@ -124,7 +126,12 @@ namespace g3 {
*/
void shutDownLogging() {
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;
}
/** 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
// 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_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
#if (defined(DBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif
#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
#if (defined(DEBUG))
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif
#endif
#include <string>
@ -52,8 +51,8 @@ const LEVELS DBUG {g3::kDebugVaulue, {"DEBUG"}},
#else
const LEVELS DEBUG {g3::kDebugVaulue, {"DEBUG"}},
#endif
INFO {g3::kDebugVaulue + 1, {"INFO"}},
WARNING {INFO.value + 1, {"WARNING"}},
INFO {g3::kDebugVaulue + 1, {"INFO"}},
WARNING {INFO.value + 1, {"WARNING"}},
@ -75,13 +74,13 @@ WARNING {INFO.value + 1, {"WARNING"}},
// 1) Remember to update the FATAL initialization below
// 2) Remember to update the initialization of "g3loglevels.cpp/g_log_level_status"
FATAL {WARNING.value + 500, {"FATAL"}};
FATAL {500, {"FATAL"}};
namespace g3 {
namespace internal {
const LEVELS CONTRACT {1000, {"CONTRACT"}},
FATAL_SIGNAL {1001, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {1002, {"FATAL_EXCEPTION"}};
FATAL_SIGNAL {1001, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {1002, {"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
@ -89,9 +88,15 @@ namespace g3 {
}
#ifdef G3_DYNAMIC_LOGGING
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
void setLogLevel(LEVELS level, bool enabled_status);
// 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);
bool logLevel(LEVELS level);
} // g3

View File

@ -10,7 +10,30 @@
#include "g3log/g3log.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) {
@ -22,24 +45,37 @@ namespace g3 {
const int g_level_size {
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
} // internal
#ifdef G3_DYNAMIC_LOGGING
void setLogLevel(LEVELS log_level, bool enabled) {
assert(internal::g_level_size == 4 && "Mismatch between number of logging levels and their use");
int level = log_level.value;
CHECK((level >= g3::kDebugVaulue) && (level <= FATAL.value));
internal::g_log_level_status[level].store(enabled, std::memory_order_release);
}
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 = {{DEBUG.value, 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;
CHECK((level >= g3::kDebugVaulue) && (level <= FATAL.value));
bool status = (internal::g_log_level_status[level].load(std::memory_order_acquire));
bool status = internal::g_log_level_status[level].value();
return status;
#endif
return true;

View File

@ -54,7 +54,8 @@ TEST(Initialization, No_Logger_Initialized___LevelsAreONByDefault) {
EXPECT_EQ(DEBUG.value, 0);
EXPECT_EQ(INFO.value, 1);
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) {
@ -421,6 +422,9 @@ TEST(CHECK, CHECK_ThatWontThrow) {
TEST(CustomLogLevels, AddANonFatal){
RestoreFileLogger logger(log_directory);
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__;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
@ -433,9 +437,13 @@ TEST(CustomLogLevels, AddANonFatal){
TEST(CustomLogLevels, AddFatal){
RestoreFileLogger logger(log_directory);
const LEVELS DEADLY {FATAL.value +1, {"DEADLY"}};
EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
g_fatal_counter.store(0);
ASSERT_FALSE(mockFatalWasCalled());
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__;
logger.reset();
@ -459,13 +467,80 @@ namespace {
RestoreDynamicLoggingLevels() {
};
~RestoreDynamicLoggingLevels() {
g3::setLogLevel(DEBUG, false);
g3::setLogLevel(INFO, false);
g3::setLogLevel(WARNING, false);
g3::setLogLevel(FATAL, false);
g3::only_change_at_initialization::reset();
g3::only_change_at_initialization::setLogLevel(DEBUG, false);
g3::only_change_at_initialization::setLogLevel(INFO, false);
g3::only_change_at_initialization::setLogLevel(WARNING, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
}
};
} // 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) {
RestoreDynamicLoggingLevels raiiLevelRestore;
@ -473,25 +548,25 @@ TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
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_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
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(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
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(INFO));
ASSERT_FALSE(g3::logLevel(WARNING));
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(INFO));
ASSERT_FALSE(g3::logLevel(WARNING));
@ -521,7 +596,7 @@ TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
{
RestoreFileLogger logger(log_directory);
g3::setLogLevel(DEBUG, false);
g3::only_change_at_initialization::setLogLevel(DEBUG, false);
EXPECT_FALSE(g3::logLevel(DEBUG));
LOG(DEBUG) << msg_debugOff;
auto content = logger.resetAndRetrieveContent();
@ -552,7 +627,7 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
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";
LOG(FATAL) << msg3;
EXPECT_FALSE(mockFatalWasCalled());
@ -575,7 +650,7 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
EXPECT_FALSE(mockFatalWasCalled());
// Disable also CHECK calls
g3::setLogLevel(FATAL, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(FATAL));
LOG(FATAL) << msg3;
EXPECT_FALSE(mockFatalWasCalled());

View File

@ -46,13 +46,13 @@ TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
EXPECT_FALSE(nullptr == factory);
SomeLibrary* loadedLibrary = factory->CreateLibrary();
for (size_t i = 0; i < 300; ++i) {
for (auto i = 0; i < 300; ++i) {
loadedLibrary->action();
}
delete loadedLibrary;
dlclose(libHandle);
} // 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);
}

View File

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