1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #13909 from kinchungwong:logging_20190220

OE-11 Logging revamp (#13909)

* Initial commit for log tag support.

Part of #11003, incomplete. Should pass build.

Moved LogLevel enum to logger.defines.hpp

LogTag struct used to convey both name and log level threshold as
one argument to the new logging macro. See logtag.hpp file, and
CV_LOG_WITH_TAG macro.

Global log level is now associated with a global log tag, when a
logging statement doesn't specify any log tag. See getLogLevel and
getGlobalLogTag functions.

A macro CV_LOGTAG_FALLBACK is allowed to be re-defined by other modules
or compilation units, internally, so that logging statements inside
that unit that specify NULL as tag will fall back to the re-defined tag.

Line-of-code information (file name, line number, function name),
together with tag name, are passed into the new log message sink.
See writeLogMessageEx function.

Fixed old incorrect CV_LOG_VERBOSE usage in ocl4dnn_conv_spatial.cpp.

* Implemented tag-based log filtering

Added LogTagManager. This is an initial version, using standard C++
approach as much as possible, to allow easier code review. Will
optimize later.

A workaround for all static dynamic initialization issues is
implemented. Refer to code comments.

* Added LogTagConfigParser.

Note: new code does not fully handle old log config parsing behavior.

* Fix log tag config vs registering ordering issue.

* Started testing LogTagConfigParser, incomplete.

The intention of this commit is to illustrate the capabilities of
the current design of LogTagConfigParser.

The test contained in this commit is not complete. Also, design changes
may require throwing away this commit and rewriting test code from
scratch.

Does not test whitespace segmentation (multiple tags on the config);
will do in next commit.

* Added CV_LOGTAG_EXPAND_NAME macro

This macro allows to be re-defined locally in other compilation units
to apply a prefix to whatever argument is passed as the "tag" argument
into CV_LOG_WITH_TAG. The default definition in logger.hpp does not
modify the argument. It is recommended to include the address-of
operator (ampersand) when re-defined locally.

* Added a few tests for LogTagManager, some fail.

See test_logtagmanager.cpp
Failed tests are: non-global ("something"), setting level by name-part
(first part or any part) has no effect at all.

* LogTagManagerTests substring non-confusion tests

* Fix major bugs in LogTagManager

The code change is intended to approximate the spec documented in
https://gist.github.com/kinchungwong/ec25bc1eba99142e0be4509b0f67d0c6

Refer to test suite in test_logtagmanager.cpp

Filter test result in "opencv_test_core" ...
with gtest_filter "LogTagManager*"

To see the test code that finds the bugs, refer to original commits
(before rebase; might be gone)

.. f3451208 (2019-03-03T19:45:17Z)
.... LogTagManagerTests substring non-confusion tests

.. 1b848f5f (2019-03-03T01:55:18Z)
.... Added a few tests for LogTagManager, some fail.

* Added LogTagManagerNamePartNonConfusionTest.

See test_logtagmanager.cpp in modules/core/test.

* Added LogTagAuto for auto registration in ctor

* Rewritten LogTagManager to resolve issues.

* Resolves code review issues around 2019-04-10

LogTagConfigParser::parseLogLevel - as part of resolving code review
issues, this function is rewritten to simplify control flow and to
improve conformance with legacy usage (for string values "OFF",
"DISABLED", and "WARNINGS").
This commit is contained in:
Ryan Wong
2019-04-21 14:01:10 -07:00
committed by Alexander Alekhin
parent f439fc4306
commit 8af96248bf
11 changed files with 2351 additions and 60 deletions
+51
View File
@@ -0,0 +1,51 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_LOGTAGCONFIG_HPP
#define OPENCV_CORE_LOGTAGCONFIG_HPP
#if 1 // if not already in precompiled headers
#include <opencv2/core/utils/logger.defines.hpp>
#include <string>
#endif
namespace cv {
namespace utils {
namespace logging {
struct LogTagConfig
{
std::string namePart;
LogLevel level;
bool isGlobal;
bool hasPrefixWildcard;
bool hasSuffixWildcard;
LogTagConfig()
: namePart()
, level()
, isGlobal()
, hasPrefixWildcard()
, hasSuffixWildcard()
{
}
LogTagConfig(const std::string& _namePart, LogLevel _level, bool _isGlobal = false,
bool _hasPrefixWildcard = false, bool _hasSuffixWildcard = false)
: namePart(_namePart)
, level(_level)
, isGlobal(_isGlobal)
, hasPrefixWildcard(_hasPrefixWildcard)
, hasSuffixWildcard(_hasSuffixWildcard)
{
}
LogTagConfig(const LogTagConfig&) = default;
LogTagConfig(LogTagConfig&&) = default;
~LogTagConfig() = default;
};
}}}
#endif
@@ -0,0 +1,305 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "logtagconfigparser.hpp"
namespace cv {
namespace utils {
namespace logging {
LogTagConfigParser::LogTagConfigParser()
{
m_parsedGlobal.namePart = "global";
m_parsedGlobal.isGlobal = true;
m_parsedGlobal.hasPrefixWildcard = false;
m_parsedGlobal.hasSuffixWildcard = false;
m_parsedGlobal.level = LOG_LEVEL_VERBOSE;
}
LogTagConfigParser::LogTagConfigParser(const std::string& input)
{
parse(input);
}
LogTagConfigParser::~LogTagConfigParser()
{
}
bool LogTagConfigParser::parse(const std::string& input)
{
m_input = input;
segmentTokens();
return (m_malformed.empty());
}
bool LogTagConfigParser::hasMalformed() const
{
return !m_malformed.empty();
}
const LogTagConfig& LogTagConfigParser::getGlobalConfig() const
{
return m_parsedGlobal;
}
const std::vector<LogTagConfig>& LogTagConfigParser::getFullNameConfigs() const
{
return m_parsedFullName;
}
const std::vector<LogTagConfig>& LogTagConfigParser::getFirstPartConfigs() const
{
return m_parsedFirstPart;
}
const std::vector<LogTagConfig>& LogTagConfigParser::getAnyPartConfigs() const
{
return m_parsedAnyPart;
}
const std::vector<std::string>& LogTagConfigParser::getMalformed() const
{
return m_malformed;
}
void LogTagConfigParser::segmentTokens()
{
const size_t len = m_input.length();
std::vector<std::pair<size_t, size_t>> startStops;
bool wasSeparator = true;
for (size_t pos = 0u; pos < len; ++pos)
{
char c = m_input[pos];
bool isSeparator = (c == ' ' || c == '\t' || c == ';');
if (!isSeparator)
{
if (wasSeparator)
{
startStops.emplace_back(pos, pos + 1u);
}
else
{
startStops.back().second = pos + 1u;
}
}
wasSeparator = isSeparator;
}
for (const auto& startStop : startStops)
{
const auto s = m_input.substr(startStop.first, startStop.second - startStop.first);
parseNameAndLevel(s);
}
}
void LogTagConfigParser::parseNameAndLevel(const std::string& s)
{
const size_t npos = std::string::npos;
const size_t len = s.length();
size_t colonIdx = s.find_first_of(":=");
if (colonIdx == npos)
{
// See if the whole string is a log level
auto parsedLevel = parseLogLevel(s);
if (parsedLevel.second)
{
// If it is, assume the log level is for global
parseWildcard("", parsedLevel.first);
return;
}
else
{
// not sure what to do.
m_malformed.push_back(s);
return;
}
}
if (colonIdx == 0u || colonIdx + 1u == len)
{
// malformed (colon or equal sign at beginning or end), cannot do anything
m_malformed.push_back(s);
return;
}
size_t colonIdx2 = s.find_first_of(":=", colonIdx + 1u);
if (colonIdx2 != npos)
{
// malformed (more than one colon or equal sign), cannot do anything
m_malformed.push_back(s);
return;
}
auto parsedLevel = parseLogLevel(s.substr(colonIdx + 1u));
if (parsedLevel.second)
{
parseWildcard(s.substr(0u, colonIdx), parsedLevel.first);
return;
}
else
{
// Cannot recognize the right side of the colon or equal sign.
// Not sure what to do.
m_malformed.push_back(s);
return;
}
}
void LogTagConfigParser::parseWildcard(const std::string& name, LogLevel level)
{
constexpr size_t npos = std::string::npos;
const size_t len = name.length();
if (len == 0u)
{
m_parsedGlobal.level = level;
return;
}
const bool hasPrefixWildcard = (name[0u] == '*');
if (hasPrefixWildcard && len == 1u)
{
m_parsedGlobal.level = level;
return;
}
const size_t firstNonWildcard = name.find_first_not_of("*.");
if (hasPrefixWildcard && firstNonWildcard == npos)
{
m_parsedGlobal.level = level;
return;
}
const bool hasSuffixWildcard = (name[len - 1u] == '*');
const size_t lastNonWildcard = name.find_last_not_of("*.");
std::string trimmedNamePart = name.substr(firstNonWildcard, lastNonWildcard - firstNonWildcard + 1u);
// The case of a single asterisk has been handled above;
// here we only handle the explicit use of "global" in the log config string.
const bool isGlobal = (trimmedNamePart == "global");
if (isGlobal)
{
m_parsedGlobal.level = level;
return;
}
LogTagConfig result(trimmedNamePart, level, false, hasPrefixWildcard, hasSuffixWildcard);
if (hasPrefixWildcard)
{
m_parsedAnyPart.emplace_back(std::move(result));
}
else if (hasSuffixWildcard)
{
m_parsedFirstPart.emplace_back(std::move(result));
}
else
{
m_parsedFullName.emplace_back(std::move(result));
}
}
std::pair<LogLevel, bool> LogTagConfigParser::parseLogLevel(const std::string& s)
{
const auto falseDontCare = std::make_pair(LOG_LEVEL_VERBOSE, false);
const auto make_parsed_result = [](LogLevel lev) -> std::pair<LogLevel, bool>
{
return std::make_pair(lev, true);
};
const size_t len = s.length();
if (len >= 1u)
{
const char c = (char)std::toupper(s[0]);
switch (c)
{
case '0':
if (len == 1u)
{
return make_parsed_result(LOG_LEVEL_SILENT);
}
break;
case 'D':
if (len == 1u ||
(len == 5u && cv::toUpperCase(s) == "DEBUG"))
{
return make_parsed_result(LOG_LEVEL_DEBUG);
}
if ((len == 7u && cv::toUpperCase(s) == "DISABLE") ||
(len == 8u && cv::toUpperCase(s) == "DISABLED"))
{
return make_parsed_result(LOG_LEVEL_SILENT);
}
break;
case 'E':
if (len == 1u ||
(len == 5u && cv::toUpperCase(s) == "ERROR"))
{
return make_parsed_result(LOG_LEVEL_ERROR);
}
break;
case 'F':
if (len == 1u ||
(len == 5u && cv::toUpperCase(s) == "FATAL"))
{
return make_parsed_result(LOG_LEVEL_FATAL);
}
break;
case 'I':
if (len == 1u ||
(len == 4u && cv::toUpperCase(s) == "INFO"))
{
return make_parsed_result(LOG_LEVEL_INFO);
}
break;
case 'O':
if (len == 3u && cv::toUpperCase(s) == "OFF")
{
return make_parsed_result(LOG_LEVEL_SILENT);
}
break;
case 'S':
if (len == 1u ||
(len == 6u && cv::toUpperCase(s) == "SILENT"))
{
return make_parsed_result(LOG_LEVEL_SILENT);
}
break;
case 'V':
if (len == 1u ||
(len == 7u && cv::toUpperCase(s) == "VERBOSE"))
{
return make_parsed_result(LOG_LEVEL_VERBOSE);
}
break;
case 'W':
if (len == 1u ||
(len == 4u && cv::toUpperCase(s) == "WARN") ||
(len == 7u && cv::toUpperCase(s) == "WARNING") ||
(len == 8u && cv::toUpperCase(s) == "WARNINGS"))
{
return make_parsed_result(LOG_LEVEL_WARNING);
}
break;
default:
break;
}
// fall through
}
return falseDontCare;
}
std::string LogTagConfigParser::toString(LogLevel level)
{
switch (level)
{
case LOG_LEVEL_SILENT:
return "SILENT";
case LOG_LEVEL_FATAL:
return "FATAL";
case LOG_LEVEL_ERROR:
return "ERROR";
case LOG_LEVEL_WARNING:
return "WARNING";
case LOG_LEVEL_INFO:
return "INFO";
case LOG_LEVEL_DEBUG:
return "DEBUG";
case LOG_LEVEL_VERBOSE:
return "VERBOSE";
default:
return std::to_string((int)level);
}
}
}}} //namespace
@@ -0,0 +1,55 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_LOGTAGCONFIGPARSER_HPP
#define OPENCV_CORE_LOGTAGCONFIGPARSER_HPP
#if 1 // if not already in precompiled headers
#include <string>
#include <vector>
#include <functional>
#endif
#include <opencv2/core/utils/logtag.hpp>
#include "logtagconfig.hpp"
namespace cv {
namespace utils {
namespace logging {
class LogTagConfigParser
{
public:
LogTagConfigParser();
explicit LogTagConfigParser(const std::string& input);
~LogTagConfigParser();
public:
bool parse(const std::string& input);
bool hasMalformed() const;
const LogTagConfig& getGlobalConfig() const;
const std::vector<LogTagConfig>& getFullNameConfigs() const;
const std::vector<LogTagConfig>& getFirstPartConfigs() const;
const std::vector<LogTagConfig>& getAnyPartConfigs() const;
const std::vector<std::string>& getMalformed() const;
private:
void segmentTokens();
void parseNameAndLevel(const std::string& s);
void parseWildcard(const std::string& name, LogLevel level);
static std::pair<LogLevel, bool> parseLogLevel(const std::string& s);
static std::string toString(LogLevel level);
private:
std::string m_input;
LogTagConfig m_parsedGlobal;
std::vector<LogTagConfig> m_parsedFullName;
std::vector<LogTagConfig> m_parsedFirstPart;
std::vector<LogTagConfig> m_parsedAnyPart;
std::vector<std::string> m_malformed;
};
}}} //namespace
#endif
+425
View File
@@ -0,0 +1,425 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "logtagmanager.hpp"
#include "logtagconfigparser.hpp"
namespace cv {
namespace utils {
namespace logging {
LogTagManager::LogTagManager(LogLevel defaultUnconfiguredGlobalLevel)
: m_mutex()
, m_globalLogTag(new LogTag(m_globalName, defaultUnconfiguredGlobalLevel))
, m_config(std::make_shared<LogTagConfigParser>())
{
assign(m_globalName, m_globalLogTag.get());
}
LogTagManager::~LogTagManager()
{
}
void LogTagManager::setConfigString(const std::string& configString, bool apply /*true*/)
{
m_config->parse(configString);
if (m_config->hasMalformed())
{
return;
}
if (!apply)
{
return;
}
// The following code is arranged with "priority by overwriting",
// where when the same log tag has multiple matches, the last code
// block has highest priority by literally overwriting the effects
// from the earlier code blocks.
//
// Matching by full name has highest priority.
// Matching by any name part has moderate priority.
// Matching by first name part (prefix) has lowest priority.
//
const auto& globalConfig = m_config->getGlobalConfig();
m_globalLogTag->level = globalConfig.level;
for (const auto& config : m_config->getFirstPartConfigs())
{
setLevelByFirstPart(config.namePart, config.level);
}
for (const auto& config : m_config->getAnyPartConfigs())
{
setLevelByAnyPart(config.namePart, config.level);
}
for (const auto& config : m_config->getFullNameConfigs())
{
setLevelByFullName(config.namePart, config.level);
}
}
LogTagConfigParser& LogTagManager::getConfigParser() const
{
return *m_config;
}
void LogTagManager::assign(const std::string& fullName, LogTag* ptr)
{
CV_TRACE_FUNCTION();
LockType lock(m_mutex);
FullNameLookupResult result(fullName);
result.m_findCrossReferences = true;
m_nameTable.addOrLookupFullName(result);
FullNameInfo& fullNameInfo = *result.m_fullNameInfoPtr;
const bool isPtrChanged = (fullNameInfo.logTagPtr != ptr);
if (!isPtrChanged)
{
return;
}
fullNameInfo.logTagPtr = ptr;
if (!ptr)
{
return;
}
const bool hasAppliedFullNameConfig = internal_applyFullNameConfigToTag(fullNameInfo);
if (hasAppliedFullNameConfig)
{
return;
}
internal_applyNamePartConfigToSpecificTag(result);
}
void LogTagManager::unassign(const std::string& fullName)
{
// Lock is inside assign() method.
assign(fullName, nullptr);
}
LogTag* LogTagManager::get(const std::string& fullName)
{
CV_TRACE_FUNCTION();
LockType lock(m_mutex);
FullNameInfo* fullNameInfoPtr = m_nameTable.getFullNameInfo(fullName);
if (fullNameInfoPtr && fullNameInfoPtr->logTagPtr)
{
return fullNameInfoPtr->logTagPtr;
}
return nullptr;
}
void LogTagManager::setLevelByFullName(const std::string& fullName, LogLevel level)
{
CV_TRACE_FUNCTION();
LockType lock(m_mutex);
FullNameLookupResult result(fullName);
result.m_findCrossReferences = false;
m_nameTable.addOrLookupFullName(result);
FullNameInfo& fullNameInfo = *result.m_fullNameInfoPtr;
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full &&
fullNameInfo.parsedLevel.level == level)
{
// skip additional processing if nothing changes.
return;
}
// update the cached configured value.
fullNameInfo.parsedLevel.scope = MatchingScope::Full;
fullNameInfo.parsedLevel.level = level;
// update the actual tag, if already registered.
LogTag* logTagPtr = fullNameInfo.logTagPtr;
if (logTagPtr)
{
logTagPtr->level = level;
}
}
void LogTagManager::setLevelByFirstPart(const std::string& firstPart, LogLevel level)
{
// Lock is inside setLevelByNamePart() method.
setLevelByNamePart(firstPart, level, MatchingScope::FirstNamePart);
}
void LogTagManager::setLevelByAnyPart(const std::string& anyPart, LogLevel level)
{
// Lock is inside setLevelByNamePart() method.
setLevelByNamePart(anyPart, level, MatchingScope::AnyNamePart);
}
void LogTagManager::setLevelByNamePart(const std::string& namePart, LogLevel level, MatchingScope scope)
{
CV_TRACE_FUNCTION();
LockType lock(m_mutex);
NamePartLookupResult result(namePart);
result.m_findCrossReferences = true;
m_nameTable.addOrLookupNamePart(result);
NamePartInfo& namePartInfo = *result.m_namePartInfoPtr;
if (namePartInfo.parsedLevel.scope == scope &&
namePartInfo.parsedLevel.level == level)
{
// skip additional processing if nothing changes.
return;
}
namePartInfo.parsedLevel.scope = scope;
namePartInfo.parsedLevel.level = level;
internal_applyNamePartConfigToMatchingTags(result);
}
std::vector<std::string> LogTagManager::splitNameParts(const std::string& fullName)
{
const size_t npos = std::string::npos;
const size_t len = fullName.length();
std::vector<std::string> nameParts;
size_t start = 0u;
while (start < len)
{
size_t nextPeriod = fullName.find('.', start);
if (nextPeriod == npos)
{
nextPeriod = len;
}
if (nextPeriod >= start + 1u)
{
nameParts.emplace_back(fullName.substr(start, nextPeriod - start));
}
start = nextPeriod + 1u;
}
return nameParts;
}
bool LogTagManager::internal_isNamePartMatch(MatchingScope scope, size_t matchingPos)
{
switch (scope)
{
case MatchingScope::FirstNamePart:
return (matchingPos == 0u);
case MatchingScope::AnyNamePart:
return true;
case MatchingScope::None:
case MatchingScope::Full:
default:
return false;
}
}
bool LogTagManager::internal_applyFullNameConfigToTag(FullNameInfo& fullNameInfo)
{
if (!fullNameInfo.logTagPtr)
{
return false;
}
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full)
{
fullNameInfo.logTagPtr->level = fullNameInfo.parsedLevel.level;
return true;
}
return false;
}
bool LogTagManager::internal_applyNamePartConfigToSpecificTag(FullNameLookupResult& fullNameResult)
{
const FullNameInfo& fullNameInfo = *fullNameResult.m_fullNameInfoPtr;
LogTag* const logTag = fullNameInfo.logTagPtr;
if (!logTag)
{
return false;
}
CV_Assert(fullNameResult.m_findCrossReferences);
const auto& crossReferences = fullNameResult.m_crossReferences;
const size_t matchingNamePartCount = crossReferences.size();
for (size_t k = 0u; k < matchingNamePartCount; ++k)
{
const auto& match = crossReferences.at(k);
const auto& namePartInfo = *match.m_namePartInfo;
const auto& parsedLevel = namePartInfo.parsedLevel;
const auto scope = parsedLevel.scope;
const LogLevel level = parsedLevel.level;
const size_t matchingPos = match.m_matchingPos;
const bool isMatch = internal_isNamePartMatch(scope, matchingPos);
if (isMatch)
{
logTag->level = level;
return true;
}
}
return false;
}
void LogTagManager::internal_applyNamePartConfigToMatchingTags(NamePartLookupResult& namePartResult)
{
CV_Assert(namePartResult.m_findCrossReferences);
const auto& crossReferences = namePartResult.m_crossReferences;
const size_t matchingFullNameCount = crossReferences.size();
NamePartInfo& namePartInfo = *namePartResult.m_namePartInfoPtr;
const MatchingScope scope = namePartInfo.parsedLevel.scope;
CV_Assert(scope != MatchingScope::Full);
if (scope == MatchingScope::None)
{
return;
}
const LogLevel level = namePartInfo.parsedLevel.level;
for (size_t k = 0u; k < matchingFullNameCount; ++k)
{
const auto& match = crossReferences.at(k);
FullNameInfo& fullNameInfo = *match.m_fullNameInfo;
LogTag* logTagPtr = fullNameInfo.logTagPtr;
if (!logTagPtr)
{
continue;
}
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full)
{
// If the full name already has valid config, that full name config
// has precedence over name part config.
continue;
}
const size_t matchingPos = match.m_matchingPos;
const bool isMatch = internal_isNamePartMatch(scope, matchingPos);
if (!isMatch)
{
continue;
}
logTagPtr->level = level;
}
}
void LogTagManager::NameTable::addOrLookupFullName(FullNameLookupResult& result)
{
const auto fullNameIdAndFlag = internal_addOrLookupFullName(result.m_fullName);
result.m_fullNameId = fullNameIdAndFlag.first;
result.m_nameParts = LogTagManager::splitNameParts(result.m_fullName);
internal_addOrLookupNameParts(result.m_nameParts, result.m_namePartIds);
const bool isNew = fullNameIdAndFlag.second;
if (isNew)
{
internal_addCrossReference(result.m_fullNameId, result.m_namePartIds);
}
// ====== IMPORTANT ====== Critical order-of-operation ======
// The gathering of the pointers of FullNameInfo and NamePartInfo are performed
// as the last step of the operation, so that these pointer are not invalidated
// by the vector append operations earlier in this function.
// ======
result.m_fullNameInfoPtr = internal_getFullNameInfo(result.m_fullNameId);
if (result.m_findCrossReferences)
{
internal_findMatchingNamePartsForFullName(result);
}
}
void LogTagManager::NameTable::addOrLookupNamePart(NamePartLookupResult& result)
{
result.m_namePartId = internal_addOrLookupNamePart(result.m_namePart);
result.m_namePartInfoPtr = internal_getNamePartInfo(result.m_namePartId);
if (result.m_findCrossReferences)
{
internal_findMatchingFullNamesForNamePart(result);
}
}
std::pair<size_t, bool> LogTagManager::NameTable::internal_addOrLookupFullName(const std::string& fullName)
{
const auto fullNameIdIter = m_fullNameIds.find(fullName);
if (fullNameIdIter != m_fullNameIds.end())
{
return std::make_pair(fullNameIdIter->second, false);
}
const size_t fullNameId = m_fullNameInfos.size();
m_fullNameInfos.emplace_back(FullNameInfo{});
m_fullNameIds.emplace(fullName, fullNameId);
return std::make_pair(fullNameId, true);
}
void LogTagManager::NameTable::internal_addOrLookupNameParts(const std::vector<std::string>& nameParts,
std::vector<size_t>& namePartIds)
{
const size_t namePartCount = nameParts.size();
namePartIds.resize(namePartCount, ~(size_t)0u);
for (size_t namePartIndex = 0u; namePartIndex < namePartCount; ++namePartIndex)
{
const std::string& namePart = nameParts.at(namePartIndex);
const size_t namePartId = internal_addOrLookupNamePart(namePart);
namePartIds.at(namePartIndex) = namePartId;
}
}
size_t LogTagManager::NameTable::internal_addOrLookupNamePart(const std::string& namePart)
{
const auto namePartIter = m_namePartIds.find(namePart);
if (namePartIter != m_namePartIds.end())
{
return namePartIter->second;
}
const size_t namePartId = m_namePartInfos.size();
m_namePartInfos.emplace_back(NamePartInfo{});
m_namePartIds.emplace(namePart, namePartId);
return namePartId;
}
void LogTagManager::NameTable::internal_addCrossReference(size_t fullNameId, const std::vector<size_t>& namePartIds)
{
const size_t namePartCount = namePartIds.size();
for (size_t namePartPos = 0u; namePartPos < namePartCount; ++namePartPos)
{
const size_t namePartId = namePartIds.at(namePartPos);
m_fullNameToNamePartIds.emplace(fullNameId, std::make_pair(namePartId, namePartPos));
m_namePartToFullNameIds.emplace(namePartId, std::make_pair(fullNameId, namePartPos));
}
}
LogTagManager::FullNameInfo* LogTagManager::NameTable::getFullNameInfo(const std::string& fullName)
{
const auto fullNameIdIter = m_fullNameIds.find(fullName);
if (fullNameIdIter == m_fullNameIds.end())
{
return nullptr;
}
const size_t fullNameId = fullNameIdIter->second;
return internal_getFullNameInfo(fullNameId);
}
LogTagManager::FullNameInfo* LogTagManager::NameTable::internal_getFullNameInfo(size_t fullNameId)
{
return std::addressof(m_fullNameInfos.at(fullNameId));
}
LogTagManager::NamePartInfo* LogTagManager::NameTable::internal_getNamePartInfo(size_t namePartId)
{
return std::addressof(m_namePartInfos.at(namePartId));
}
void LogTagManager::NameTable::internal_findMatchingNamePartsForFullName(FullNameLookupResult& fullNameResult)
{
const size_t fullNameId = fullNameResult.m_fullNameId;
FullNameInfo* fullNameInfo = fullNameResult.m_fullNameInfoPtr;
const auto& namePartIds = fullNameResult.m_namePartIds;
const size_t namePartCount = namePartIds.size();
auto& crossReferences = fullNameResult.m_crossReferences;
crossReferences.clear();
crossReferences.reserve(namePartCount);
for (size_t matchingPos = 0u; matchingPos < namePartCount; ++matchingPos)
{
const size_t namePartId = namePartIds.at(matchingPos);
NamePartInfo* namePartInfo = internal_getNamePartInfo(namePartId);
crossReferences.emplace_back(CrossReference(fullNameId, namePartId, matchingPos, fullNameInfo, namePartInfo));
}
}
void LogTagManager::NameTable::internal_findMatchingFullNamesForNamePart(NamePartLookupResult& result)
{
const size_t namePartId = result.m_namePartId;
NamePartInfo* namePartInfo = result.m_namePartInfoPtr;
const size_t matchingFullNameCount = m_namePartToFullNameIds.count(namePartId);
std::vector<CrossReference>& crossReferences = result.m_crossReferences;
crossReferences.clear();
crossReferences.reserve(matchingFullNameCount);
const auto namePartToFullNameIterPair = m_namePartToFullNameIds.equal_range(result.m_namePartId);
const auto iterBegin = namePartToFullNameIterPair.first;
const auto iterEnd = namePartToFullNameIterPair.second;
for (auto iter = iterBegin; iter != iterEnd; ++iter)
{
const size_t fullNameId = iter->second.first;
const size_t matchingPos = iter->second.second;
FullNameInfo* fullNameInfo = internal_getFullNameInfo(fullNameId);
crossReferences.emplace_back(CrossReference(fullNameId, namePartId, matchingPos, fullNameInfo, namePartInfo));
}
}
}}} //namespace
+284
View File
@@ -0,0 +1,284 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_CORE_LOGTAGMANAGER_HPP
#define OPENCV_CORE_LOGTAGMANAGER_HPP
#if 1 // if not already in precompiled headers
#include <string>
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <mutex>
#include <memory>
#endif
#include <opencv2/core/utils/logtag.hpp>
#include "logtagconfig.hpp"
namespace cv {
namespace utils {
namespace logging {
// forward declaration
class LogTagConfigParser;
// A lookup table of LogTags using full name, first part of name, and any part of name.
// The name parts of a LogTag is delimited by period.
//
// This class does not handle wildcard characters. The name-matching method can only be
// selected by calling the appropriate function.
//
class LogTagManager
{
private:
// Current implementation does not seem to require recursive mutex;
// also, extensible functions (accepting user-provided callback) are not allowed
// to call LogTagManger (to prevent iterator invalidation), which needs enforced
// with a non-recursive mutex.
using MutexType = std::mutex;
using LockType = std::lock_guard<MutexType>;
enum class MatchingScope
{
None,
Full,
FirstNamePart,
AnyNamePart
};
struct ParsedLevel
{
LogLevel level;
MatchingScope scope;
ParsedLevel()
: level()
, scope(MatchingScope::None)
{
}
};
struct FullNameInfo
{
LogTag* logTagPtr;
ParsedLevel parsedLevel;
};
struct NamePartInfo
{
ParsedLevel parsedLevel;
};
struct CrossReference
{
size_t m_fullNameId;
size_t m_namePartId;
size_t m_matchingPos;
FullNameInfo* m_fullNameInfo;
NamePartInfo* m_namePartInfo;
explicit CrossReference(size_t fullNameId, size_t namePartId, size_t matchingPos,
FullNameInfo* fullNameInfo, NamePartInfo* namePartInfo)
: m_fullNameId(fullNameId)
, m_namePartId(namePartId)
, m_matchingPos(matchingPos)
, m_fullNameInfo(fullNameInfo)
, m_namePartInfo(namePartInfo)
{
}
};
struct FullNameLookupResult
{
// The full name being looked up
std::string m_fullName;
// The full name being broken down into name parts
std::vector<std::string> m_nameParts;
// The full name ID that is added or looked up from the table
size_t m_fullNameId;
// The name part IDs that are added to or looked up from the table
// listed in the same order as m_nameParts
std::vector<size_t> m_namePartIds;
// The information struct for the full name
FullNameInfo* m_fullNameInfoPtr;
// Specifies whether cross references (full names that match the name part)
// should be computed.
bool m_findCrossReferences;
// List of all full names that match the given name part.
// This field is computed only if m_findCrossReferences is true.
std::vector<CrossReference> m_crossReferences;
explicit FullNameLookupResult(const std::string& fullName)
: m_fullName(fullName)
, m_nameParts()
, m_fullNameId()
, m_namePartIds()
, m_fullNameInfoPtr()
, m_findCrossReferences()
, m_crossReferences()
{
}
};
struct NamePartLookupResult
{
// The name part being looked up
std::string m_namePart;
// The name part ID that is added or looked up from the table
size_t m_namePartId;
// Information struct ptr for the name part
NamePartInfo* m_namePartInfoPtr;
// Specifies whether cross references (full names that match the name part) should be computed.
bool m_findCrossReferences;
// List of all full names that match the given name part.
// This field is computed only if m_findCrossReferences is true.
std::vector<CrossReference> m_crossReferences;
explicit NamePartLookupResult(const std::string& namePart)
: m_namePart(namePart)
, m_namePartId()
, m_namePartInfoPtr()
, m_findCrossReferences()
, m_crossReferences()
{
}
};
struct NameTable
{
// All data structures in this class are append-only. The item count
// is being used as an incrementing integer key.
public:
// Full name information struct.
std::vector<FullNameInfo> m_fullNameInfos;
// Name part information struct.
std::vector<NamePartInfo> m_namePartInfos;
// key: full name (string)
// value: full name ID
// .... (index into the vector of m_fullNameInfos)
// .... (key into m_fullNameToNamePartIds)
// .... (value.second in m_namePartToFullNameIds)
std::unordered_map<std::string, size_t> m_fullNameIds;
// key: name part (string)
// value: name part ID
// .... (index into the vector of m_namePartInfos)
// .... (key into m_namePartToFullNameIds)
// .... (value.second in m_fullNameToNamePartIds)
std::unordered_map<std::string, size_t> m_namePartIds;
// key: full name ID
// value.first: name part ID
// value.second: occurrence position of name part in the full name
std::unordered_multimap<size_t, std::pair<size_t, size_t>> m_fullNameToNamePartIds;
// key: name part ID
// value.first: full name ID
// value.second: occurrence position of name part in the full name
std::unordered_multimap<size_t, std::pair<size_t, size_t>> m_namePartToFullNameIds;
public:
void addOrLookupFullName(FullNameLookupResult& result);
void addOrLookupNamePart(NamePartLookupResult& result);
FullNameInfo* getFullNameInfo(const std::string& fullName);
private:
// Add or get full name. Does not compute name parts or access them in the table.
// Returns the full name ID, and a bool indicating if the full name is new.
std::pair<size_t, bool> internal_addOrLookupFullName(const std::string& fullName);
// Add or get multiple name parts. Saves name part IDs into a vector.
void internal_addOrLookupNameParts(const std::vector<std::string>& nameParts, std::vector<size_t>& namePartIds);
// Add or get name part. Returns namePartId.
size_t internal_addOrLookupNamePart(const std::string& namePart);
// For each name part ID, insert the tuples (full name, name part ID, name part position)
// into the cross reference table
void internal_addCrossReference(size_t fullNameId, const std::vector<size_t>& namePartIds);
// Gather pointer for full name info struct.
// Note: The pointer is interior to the table vector. The pointers are invalidated
// if the table is modified.
FullNameInfo* internal_getFullNameInfo(size_t fullNameId);
// Gather pointers for name part info struct.
// Note: The pointers are interior to the table vector. The pointers are invalidated
// if the table is modified.
NamePartInfo* internal_getNamePartInfo(size_t namePartId);
void internal_findMatchingNamePartsForFullName(FullNameLookupResult& fullNameResult);
void internal_findMatchingFullNamesForNamePart(NamePartLookupResult& result);
};
public:
LogTagManager(LogLevel defaultUnconfiguredGlobalLevel);
~LogTagManager();
public:
// Parse and apply the config string.
void setConfigString(const std::string& configString, bool apply = true);
// Gets the config parser. This is necessary to retrieve the list of malformed strings.
LogTagConfigParser& getConfigParser() const;
// Add (register) the log tag.
// Note, passing in nullptr as value is equivalent to unassigning.
void assign(const std::string& fullName, LogTag* ptr);
// Unassign the log tag. This is equivalent to calling assign with nullptr value.
void unassign(const std::string& fullName);
// Retrieve the log tag by exact name.
LogTag* get(const std::string& fullName);
// Changes the log level of the tag having the exact full name.
void setLevelByFullName(const std::string& fullName, LogLevel level);
// Changes the log level of the tags matching the first part of the name.
void setLevelByFirstPart(const std::string& firstPart, LogLevel level);
// Changes the log level of the tags matching any part of the name.
void setLevelByAnyPart(const std::string& anyPart, LogLevel level);
// Changes the log level of the tags with matching name part according
// to the specified scope.
void setLevelByNamePart(const std::string& namePart, LogLevel level, MatchingScope scope);
private:
bool internal_applyFullNameConfigToTag(FullNameInfo& fullNameInfo);
bool internal_applyNamePartConfigToSpecificTag(FullNameLookupResult& fullNameResult);
void internal_applyNamePartConfigToMatchingTags(NamePartLookupResult& namePartResult);
private:
static std::vector<std::string> splitNameParts(const std::string& fullName);
static bool internal_isNamePartMatch(MatchingScope scope, size_t matchingPos);
private:
static constexpr const char* m_globalName = "global";
private:
mutable MutexType m_mutex;
std::unique_ptr<LogTag> m_globalLogTag;
NameTable m_nameTable;
std::shared_ptr<LogTagConfigParser> m_config;
};
}}} //namespace
#endif //OPENCV_CORE_LOGTAGMANAGER_HPP