refs #601, #600, #485 Status message handling.

* follow up of debug session, added failure/success to status message
* return status messages instead of directly logging in functions returning CStatusMessage.
* Ignore empty preformatted messages.
* new log category
This commit is contained in:
Klaus Basan
2016-02-25 22:26:28 +00:00
committed by Mathew Sutcliffe
parent f490504f52
commit 144ba62572
9 changed files with 70 additions and 33 deletions

View File

@@ -50,6 +50,13 @@ namespace BlackMisc
return cat; return cat;
} }
//! Core/base services such as caching etc.
static const CLogCategory &services()
{
static const CLogCategory cat { "swift.services" };
return cat;
}
//! Contexts //! Contexts
static const CLogCategory &context() static const CLogCategory &context()
{ {
@@ -106,6 +113,7 @@ namespace BlackMisc
static const QList<CLogCategory> cats static const QList<CLogCategory> cats
{ {
uncategorized(), uncategorized(),
services(),
validation(), validation(),
context(), context(),
contextSlot(), contextSlot(),

View File

@@ -179,6 +179,7 @@ namespace BlackMisc
void CLogMessage::preformatted(const CStatusMessage &statusMessage) void CLogMessage::preformatted(const CStatusMessage &statusMessage)
{ {
if (statusMessage.isEmpty()) { return; } // just skip empty messages
CLogMessage msg(statusMessage.getCategories()); CLogMessage msg(statusMessage.getCategories());
msg.m_severity = statusMessage.getSeverity(); msg.m_severity = statusMessage.getSeverity();
msg.m_message = statusMessage.getMessage(); msg.m_message = statusMessage.getMessage();

View File

@@ -19,6 +19,7 @@ namespace BlackMisc
{ "uncategorized (swift)", exactMatch(CLogCategory::uncategorized()) }, { "uncategorized (swift)", exactMatch(CLogCategory::uncategorized()) },
{ "validation", exactMatch(CLogCategory::validation()) }, { "validation", exactMatch(CLogCategory::validation()) },
{ "verification", exactMatch(CLogCategory::verification()) }, { "verification", exactMatch(CLogCategory::verification()) },
{ "services", exactMatch(CLogCategory::services()) },
{ "model mapping", exactMatch(CLogCategory::mapping()) }, { "model mapping", exactMatch(CLogCategory::mapping()) },
{ "swift contexts", exactMatch(CLogCategory::context()) }, { "swift contexts", exactMatch(CLogCategory::context()) },
{ "swift context slots", exactMatch(CLogCategory::contextSlot()) }, { "swift context slots", exactMatch(CLogCategory::contextSlot()) },

View File

@@ -142,6 +142,16 @@ namespace BlackMisc
return c.isEmpty() ? this->getCategoriesAsString() : c; return c.isEmpty() ? this->getCategoriesAsString() : c;
} }
bool CStatusMessage::isSuccess() const
{
return !isFailure();
}
bool CStatusMessage::isFailure() const
{
return getSeverity() == SeverityError;
}
void CStatusMessage::prependMessage(const QString &msg) void CStatusMessage::prependMessage(const QString &msg)
{ {
if (msg.isEmpty()) { return; } if (msg.isEmpty()) { return; }

View File

@@ -107,6 +107,12 @@ namespace BlackMisc
//! Warning or above //! Warning or above
bool isWarningOrAbove() const { return this->m_severity == SeverityWarning || this->m_severity == SeverityError; } bool isWarningOrAbove() const { return this->m_severity == SeverityWarning || this->m_severity == SeverityError; }
//! Operation considered successful
bool isSuccess() const;
//! Operation considered unsuccessful
bool isFailure() const;
//! Message //! Message
QString getMessage() const { return this->m_message; } QString getMessage() const { return this->m_message; }

View File

@@ -85,6 +85,12 @@ namespace BlackMisc
// CValueCache // CValueCache
//////////////////////////////// ////////////////////////////////
const CLogCategoryList &CValueCache::getLogCategories()
{
static const CLogCategoryList cats({ CLogCategory("swift.valuecache") , CLogCategory::services()} );
return cats;
}
CValueCache::CValueCache(CValueCache::DistributionMode mode, QObject *parent) : CValueCache::CValueCache(CValueCache::DistributionMode mode, QObject *parent) :
QObject(parent) QObject(parent)
{ {
@@ -227,7 +233,7 @@ namespace BlackMisc
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
auto values = getAllValues(keyPrefix); auto values = getAllValues(keyPrefix);
auto status = saveToFiles(dir, values); auto status = saveToFiles(dir, values);
if (! status.isEmpty()) { markAllAsSaved(keyPrefix); } if (status.isSuccess()) { markAllAsSaved(keyPrefix); }
return status; return status;
} }
@@ -240,19 +246,19 @@ namespace BlackMisc
} }
if (! QDir::root().mkpath(dir)) if (! QDir::root().mkpath(dir))
{ {
return CLogMessage(this).error("Failed to create directory %1") << dir; return CStatusMessage(this, CStatusMessage::SeverityError, "Failed to create directory " + dir);
} }
for (auto it = namespaces.cbegin(); it != namespaces.cend(); ++it) for (auto it = namespaces.cbegin(); it != namespaces.cend(); ++it)
{ {
CAtomicFile file(dir + "/" + it.key() + ".json"); CAtomicFile file(dir + "/" + it.key() + ".json");
if (! file.open(QFile::ReadWrite | QFile::Text)) if (! file.open(QFile::ReadWrite | QFile::Text))
{ {
return CLogMessage(this).error("Failed to open %1: %2") << file.fileName() << file.errorString(); return CStatusMessage(this, CStatusMessage::SeverityError, QString("Failed to open %1: %2").arg(file.fileName()).arg(file.errorString()));
} }
auto json = QJsonDocument::fromJson(file.readAll()); auto json = QJsonDocument::fromJson(file.readAll());
if (json.isArray() || (json.isNull() && ! json.isEmpty())) if (json.isArray() || (json.isNull() && ! json.isEmpty()))
{ {
return CLogMessage(this).error("Invalid JSON format in %1") << file.fileName(); return CStatusMessage(this, CStatusMessage::SeverityError, "Invalid JSON format in " + file.fileName());
} }
CVariantMap storedValues; CVariantMap storedValues;
storedValues.convertFromJson(json.object()); storedValues.convertFromJson(json.object());
@@ -261,10 +267,12 @@ namespace BlackMisc
if (!(file.seek(0) && file.resize(0) && file.write(json.toJson()) > 0 && file.checkedClose())) if (!(file.seek(0) && file.resize(0) && file.write(json.toJson()) > 0 && file.checkedClose()))
{ {
return CLogMessage(this).error("Failed to write to %1: %2") << file.fileName() << file.errorString(); return CStatusMessage(this, CStatusMessage::SeverityError,
QString("Failed to write to %1: %2").arg(file.fileName()).arg(file.errorString()));
} }
} }
return {}; return CStatusMessage(this, CStatusMessage::SeverityInfo,
QString("Written %1 files for value cache in %2").arg(namespaces.size()).arg(dir));
} }
CStatusMessage CValueCache::loadFromFiles(const QString &dir) CStatusMessage CValueCache::loadFromFiles(const QString &dir)
@@ -281,19 +289,21 @@ namespace BlackMisc
{ {
if (! QDir(dir).isReadable()) if (! QDir(dir).isReadable())
{ {
return CLogMessage(this).error("Failed to read directory %1") << dir; return CStatusMessage(this, CStatusMessage::SeverityError, "Failed to create directory " + dir);
} }
for (const auto &filename : QDir(dir).entryList({ "*.json" }, QDir::Files))
const QStringList entries(QDir(dir).entryList({ "*.json" }, QDir::Files));
for (const auto &filename : entries)
{ {
QFile file(dir + "/" + filename); QFile file(dir + "/" + filename);
if (! file.open(QFile::ReadOnly | QFile::Text)) if (! file.open(QFile::ReadOnly | QFile::Text))
{ {
return CLogMessage(this).error("Failed to open %1 : %2") << file.fileName() << file.errorString(); return CStatusMessage(this, CStatusMessage::SeverityError, QString("Failed to open %1: %2").arg(file.fileName()).arg(file.errorString()));
} }
auto json = QJsonDocument::fromJson(file.readAll()); auto json = QJsonDocument::fromJson(file.readAll());
if (json.isArray() || (json.isNull() && ! json.isEmpty())) if (json.isArray() || (json.isNull() && ! json.isEmpty()))
{ {
return CLogMessage(this).error("Invalid JSON format in %1") << file.fileName(); return CStatusMessage(this, CStatusMessage::SeverityError, "Invalid JSON format in " + file.fileName());
} }
CVariantMap temp; CVariantMap temp;
temp.convertFromJson(json.object()); temp.convertFromJson(json.object());
@@ -304,7 +314,8 @@ namespace BlackMisc
temp.removeDuplicates(currentValues); temp.removeDuplicates(currentValues);
o_values.insert(temp, QFileInfo(file).lastModified().toMSecsSinceEpoch()); o_values.insert(temp, QFileInfo(file).lastModified().toMSecsSinceEpoch());
} }
return {}; return CStatusMessage(this, CStatusMessage::SeverityInfo,
QString("Loaded value cache from %1 files in %2").arg(entries.size()).arg(dir));
} }
void CValueCache::markAllAsSaved(const QString &keyPrefix) void CValueCache::markAllAsSaved(const QString &keyPrefix)
@@ -401,12 +412,13 @@ namespace BlackMisc
auto &element = *(m_elements[key] = ElementPtr(new Element(key, metaType, validator, defaultValue, slot))); auto &element = *(m_elements[key] = ElementPtr(new Element(key, metaType, validator, defaultValue, slot)));
std::forward_as_tuple(element.m_value.uniqueWrite(), element.m_timestamp, element.m_saved) = m_cache->getValue(key); std::forward_as_tuple(element.m_value.uniqueWrite(), element.m_timestamp, element.m_saved) = m_cache->getValue(key);
auto error = validate(element, element.m_value.read()); auto status = validate(element, element.m_value.read(), CStatusMessage::SeverityDebug);
if (! error.isEmpty()) if (!status.isEmpty()) // intentionally kept !empty here, debug message supposed to write default value
{ {
CLogMessage::preformatted(error);
element.m_value.uniqueWrite() = defaultValue; element.m_value.uniqueWrite() = defaultValue;
CLogMessage::preformatted(status);
} }
return element; return element;
} }
@@ -422,8 +434,8 @@ namespace BlackMisc
if (timestamp == 0) { timestamp = QDateTime::currentMSecsSinceEpoch(); } if (timestamp == 0) { timestamp = QDateTime::currentMSecsSinceEpoch(); }
if (element.m_value.read() == value && element.m_timestamp == timestamp) { return {}; } if (element.m_value.read() == value && element.m_timestamp == timestamp) { return {}; }
auto error = validate(element, value); auto status = validate(element, value, CStatusMessage::SeverityError);
if (error.isEmpty()) if (status.isSuccess())
{ {
if (m_batchMode > 0) if (m_batchMode > 0)
{ {
@@ -439,11 +451,7 @@ namespace BlackMisc
emit valuesWantToCache({ { { element.m_key, value } }, timestamp, save }); emit valuesWantToCache({ { { element.m_key, value } }, timestamp, save });
} }
} }
else return status;
{
CLogMessage::preformatted(error);
}
return error;
} }
const QString &CValuePage::getKey(const Element &element) const const QString &CValuePage::getKey(const Element &element) const
@@ -481,8 +489,8 @@ namespace BlackMisc
} }
else if (element->m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency else if (element->m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency
{ {
auto error = validate(*element, it.value()); auto error = validate(*element, it.value(), CStatusMessage::SeverityError);
if (error.isEmpty()) if (error.isSuccess())
{ {
element->m_value.uniqueWrite() = it.value(); element->m_value.uniqueWrite() = it.value();
element->m_timestamp = it.timestamp(); element->m_timestamp = it.timestamp();
@@ -541,11 +549,11 @@ namespace BlackMisc
} }
} }
CStatusMessage CValuePage::validate(const Element &element, const CVariant &value) const CStatusMessage CValuePage::validate(const Element &element, const CVariant &value, CStatusMessage::StatusSeverity invalidSeverity) const
{ {
if (! value.isValid()) if (! value.isValid())
{ {
return CStatusMessage(this, CStatusMessage::SeverityDebug, "Uninitialized value for " + element.m_key); return CStatusMessage(this, invalidSeverity, "Uninitialized value for " + element.m_key);
} }
else if (value.userType() != element.m_metaType) else if (value.userType() != element.m_metaType)
{ {

View File

@@ -113,6 +113,9 @@ namespace BlackMisc
Distributed //!< Distributed among multiple processes. Distributed //!< Distributed among multiple processes.
}; };
//! Log categories
static const CLogCategoryList &getLogCategories();
//! Constructor. //! Constructor.
//! \param mode Whether or not the cache can be distributed among multiple processes. //! \param mode Whether or not the cache can be distributed among multiple processes.
//! \param parent The parent of the QObject. //! \param parent The parent of the QObject.

View File

@@ -105,7 +105,7 @@ namespace BlackMisc
CVariantMap m_batchedValues; CVariantMap m_batchedValues;
CValuePage(QObject *parent, CValueCache *cache); CValuePage(QObject *parent, CValueCache *cache);
CStatusMessage validate(const Element &element, const CVariant &value) const; CStatusMessage validate(const Element &element, const CVariant &value, CStatusMessage::StatusSeverity invalidSeverity) const;
}; };
} // namespace } // namespace

View File

@@ -227,7 +227,7 @@ namespace BlackMiscTest
if (dir.exists()) { dir.removeRecursively(); } if (dir.exists()) { dir.removeRecursively(); }
auto status = cache.saveToFiles(dir.absolutePath()); auto status = cache.saveToFiles(dir.absolutePath());
QVERIFY(status.isEmpty()); QVERIFY(status.isSuccess());
auto files = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name); auto files = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name);
QCOMPARE(files.size(), 2); QCOMPARE(files.size(), 2);
@@ -236,7 +236,7 @@ namespace BlackMiscTest
CValueCache cache2(CValueCache::LocalOnly); CValueCache cache2(CValueCache::LocalOnly);
status = cache2.loadFromFiles(dir.absolutePath()); status = cache2.loadFromFiles(dir.absolutePath());
QVERIFY(status.isEmpty()); QVERIFY(status.isSuccess());
QCOMPARE(cache2.getAllValues(), testData); QCOMPARE(cache2.getAllValues(), testData);
} }