|
#include "pch.h" |
|
#include "LogLevelManager_PrefixSupport.h" |
|
|
|
namespace logging_20190125 |
|
{ |
|
LogLevelManager_2::LogLevelManager_2() |
|
: m_mutex() |
|
, m_configuredLogLevels() |
|
, m_registeredLogLevels() |
|
, m_prefixLogLevels() |
|
{ |
|
} |
|
|
|
LogLevelManager_2::~LogLevelManager_2() |
|
{ |
|
} |
|
|
|
LogLevelManager_2& LogLevelManager_2::getInstance() |
|
{ |
|
static LogLevelManager_2 instance{}; |
|
return instance; |
|
} |
|
|
|
void LogLevelManager_2::registerLogLevel(const std::string& name, int& refLogLevel) |
|
{ |
|
LockType lock(m_mutex); |
|
auto regIter = m_registeredLogLevels.find(name); |
|
if (regIter == m_registeredLogLevels.end()) |
|
{ |
|
regIter = m_registeredLogLevels.emplace(name, std::addressof(refLogLevel)).first; |
|
} |
|
internal_applyPrefixLogLevel_onRegistering(lock, *regIter); |
|
internal_applyConfiguredLogLevel_onRegistering(lock, *regIter); |
|
} |
|
|
|
void LogLevelManager_2::setLogLevel(const std::string& name, int logLevel) |
|
{ |
|
LockType lock(m_mutex); |
|
auto cfgIter = m_configuredLogLevels.find(name); |
|
if (cfgIter == m_configuredLogLevels.end()) |
|
{ |
|
cfgIter = m_configuredLogLevels.emplace(name, logLevel).first; |
|
} |
|
else |
|
{ |
|
cfgIter->second = logLevel; |
|
} |
|
auto regIter = m_registeredLogLevels.find(name); |
|
if (regIter != m_registeredLogLevels.end()) |
|
{ |
|
*(regIter->second) = (cfgIter->second); |
|
} |
|
} |
|
|
|
int LogLevelManager_2::getLogLevel(const std::string& name) const |
|
{ |
|
LockType lock(m_mutex); |
|
const auto regIter = m_registeredLogLevels.find(name); |
|
if (regIter != m_registeredLogLevels.end()) |
|
{ |
|
return *(regIter->second); |
|
} |
|
const auto cfgIter = m_configuredLogLevels.find(name); |
|
if (cfgIter != m_configuredLogLevels.end()) |
|
{ |
|
return (cfgIter->second); |
|
} |
|
// ====== TODO ====== |
|
return -1; // or something |
|
} |
|
|
|
void LogLevelManager_2::setPrefixLogLevel(const std::string& prefix, int logLevel) |
|
{ |
|
LockType lock(m_mutex); |
|
auto prefixIter = m_prefixLogLevels.find(prefix); |
|
if (prefixIter == m_prefixLogLevels.end()) |
|
{ |
|
prefixIter = m_prefixLogLevels.emplace(prefix, logLevel).first; |
|
} |
|
else |
|
{ |
|
prefixIter->second = logLevel; |
|
} |
|
internal_applyPrefixLogLevel_onPrefixAdded(lock, *prefixIter); |
|
} |
|
|
|
void LogLevelManager_2::internal_applyConfiguredLogLevel_onRegistering(const LockType& lock, |
|
const std::pair<const std::string, int*>& registeredPair) |
|
{ |
|
const std::string& name = registeredPair.first; |
|
auto cfgIter = m_configuredLogLevels.find(name); |
|
if (cfgIter != m_configuredLogLevels.end()) |
|
{ |
|
*(registeredPair.second) = (cfgIter->second); |
|
} |
|
} |
|
|
|
void LogLevelManager_2::internal_applyPrefixLogLevel_onPrefixAdded(const LockType& lock, |
|
const std::pair<const std::string, int>& prefixPair) |
|
{ |
|
const std::string& prefix = prefixPair.first; |
|
const size_t prefixLen = prefix.length(); |
|
for (auto& registeredPair : m_registeredLogLevels) |
|
{ |
|
const std::string& name = registeredPair.first; |
|
const size_t nameLen = name.length(); |
|
if (nameLen < prefixLen) |
|
{ |
|
continue; |
|
} |
|
// having checked string length, memcmp can be used |
|
// since we don't anticipate null terminating chars inside strings. |
|
if (0 != memcmp(prefix.c_str(), name.c_str(), prefixLen)) |
|
{ |
|
continue; |
|
} |
|
*(registeredPair.second) = prefixPair.second; |
|
} |
|
} |
|
|
|
void LogLevelManager_2::internal_applyPrefixLogLevel_onRegistering(const LockType& lock, |
|
const std::pair<const std::string, int*>& registeredPair) |
|
{ |
|
using PrefixIterType = decltype(m_prefixLogLevels)::const_iterator; |
|
constexpr size_t prefixSearchStrategyThreshold = 10u; |
|
const size_t prefixListCount = m_prefixLogLevels.size(); |
|
const std::string& name = registeredPair.first; |
|
const size_t nameLen = name.length(); |
|
PrefixIterType prefixIterBegin; |
|
PrefixIterType prefixIterEnd; |
|
if (prefixListCount < prefixSearchStrategyThreshold) |
|
{ |
|
// If list is short, iterate the whole list |
|
prefixIterBegin = m_prefixLogLevels.cbegin(); |
|
prefixIterEnd = m_prefixLogLevels.cend(); |
|
} |
|
else |
|
{ |
|
// If list is long, use bounded search |
|
// |
|
// REMARK |
|
// When co-opting sorted list for string prefix search, |
|
// the lower-bound and upper-bound logic is not intuitive. |
|
// There may be errors, but there are also non-errors - |
|
// code that "looks like wrong" but is actually correct. |
|
// |
|
// TODO |
|
// Need formally prove correctness, or else replace with a |
|
// provably correct implementation. |
|
// |
|
// REMARK (performance) |
|
// Using a sorted list for string prefix search is actually |
|
// a poor choice. A binary search tree is not a prefix tree. |
|
// |
|
const char nameFirstChar = name.front(); |
|
const std::string nameLowerBound{ 1u, nameFirstChar }; |
|
const std::string nameUpperBound{ 1u, std::min<char>(nameFirstChar, (char)127) }; |
|
prefixIterBegin = m_prefixLogLevels.lower_bound(nameLowerBound); |
|
prefixIterEnd = m_prefixLogLevels.lower_bound(nameUpperBound); |
|
} |
|
for (PrefixIterType prefixIter = prefixIterBegin; |
|
prefixIter != prefixIterEnd; |
|
++prefixIter) |
|
{ |
|
const auto& prefixPair = *prefixIter; |
|
const std::string& prefix = prefixPair.first; |
|
const size_t prefixLen = prefix.length(); |
|
if (nameLen < prefixLen) |
|
{ |
|
continue; |
|
} |
|
// having checked string length, memcmp can be used |
|
// since we don't anticipate null terminating chars inside strings. |
|
if (0 != memcmp(prefix.c_str(), name.c_str(), prefixLen)) |
|
{ |
|
continue; |
|
} |
|
*(registeredPair.second) = prefixPair.second; |
|
} |
|
} |
|
} |