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

View File

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

View File

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

View File

@@ -142,6 +142,16 @@ namespace BlackMisc
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)
{
if (msg.isEmpty()) { return; }

View File

@@ -107,6 +107,12 @@ namespace BlackMisc
//! Warning or above
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
QString getMessage() const { return this->m_message; }

View File

@@ -85,13 +85,19 @@ namespace BlackMisc
// CValueCache
////////////////////////////////
const CLogCategoryList &CValueCache::getLogCategories()
{
static const CLogCategoryList cats({ CLogCategory("swift.valuecache") , CLogCategory::services()} );
return cats;
}
CValueCache::CValueCache(CValueCache::DistributionMode mode, QObject *parent) :
QObject(parent)
{
if (mode == LocalOnly)
{
// loopback signal to own slot for local operation
connect(this, &CValueCache::valuesChangedByLocal, this, [ = ](const CValueCachePacket &values)
connect(this, &CValueCache::valuesChangedByLocal, this, [ = ](const CValueCachePacket & values)
{
changeValuesFromRemote(values, CIdentifier());
});
@@ -227,7 +233,7 @@ namespace BlackMisc
QMutexLocker lock(&m_mutex);
auto values = getAllValues(keyPrefix);
auto status = saveToFiles(dir, values);
if (! status.isEmpty()) { markAllAsSaved(keyPrefix); }
if (status.isSuccess()) { markAllAsSaved(keyPrefix); }
return status;
}
@@ -240,31 +246,33 @@ namespace BlackMisc
}
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)
{
CAtomicFile file(dir + "/" + it.key() + ".json");
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());
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;
storedValues.convertFromJson(json.object());
storedValues.insert(*it);
json.setObject(storedValues.toJson());
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)
@@ -281,19 +289,21 @@ namespace BlackMisc
{
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);
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());
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;
temp.convertFromJson(json.object());
@@ -304,7 +314,8 @@ namespace BlackMisc
temp.removeDuplicates(currentValues);
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)
@@ -371,7 +382,7 @@ namespace BlackMisc
CValuePage &CValuePage::getPageFor(QObject *parent, CValueCache *cache)
{
auto pages = parent->findChildren<CValuePage *>();
auto it = std::find_if(pages.cbegin(), pages.cend(), [cache](CValuePage *page) { return page->m_cache == cache; });
auto it = std::find_if(pages.cbegin(), pages.cend(), [cache](CValuePage * page) { return page->m_cache == cache; });
if (it == pages.cend()) { return *new CValuePage(parent, cache); }
else { return **it; }
}
@@ -401,12 +412,13 @@ namespace BlackMisc
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);
auto error = validate(element, element.m_value.read());
if (! error.isEmpty())
auto status = validate(element, element.m_value.read(), CStatusMessage::SeverityDebug);
if (!status.isEmpty()) // intentionally kept !empty here, debug message supposed to write default value
{
CLogMessage::preformatted(error);
element.m_value.uniqueWrite() = defaultValue;
CLogMessage::preformatted(status);
}
return element;
}
@@ -422,8 +434,8 @@ namespace BlackMisc
if (timestamp == 0) { timestamp = QDateTime::currentMSecsSinceEpoch(); }
if (element.m_value.read() == value && element.m_timestamp == timestamp) { return {}; }
auto error = validate(element, value);
if (error.isEmpty())
auto status = validate(element, value, CStatusMessage::SeverityError);
if (status.isSuccess())
{
if (m_batchMode > 0)
{
@@ -439,11 +451,7 @@ namespace BlackMisc
emit valuesWantToCache({ { { element.m_key, value } }, timestamp, save });
}
}
else
{
CLogMessage::preformatted(error);
}
return error;
return status;
}
const QString &CValuePage::getKey(const Element &element) const
@@ -472,7 +480,7 @@ namespace BlackMisc
QList<NotifySlot> notifySlots;
forEachIntersection(m_elements, values, [changedBy, this, &notifySlots, &values](const QString &key, const ElementPtr &element, CValueCachePacket::const_iterator it)
forEachIntersection(m_elements, values, [changedBy, this, &notifySlots, &values](const QString & key, const ElementPtr & element, CValueCachePacket::const_iterator it)
{
if (changedBy == this) // round trip
{
@@ -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
{
auto error = validate(*element, it.value());
if (error.isEmpty())
auto error = validate(*element, it.value(), CStatusMessage::SeverityError);
if (error.isSuccess())
{
element->m_value.uniqueWrite() = it.value();
element->m_timestamp = it.timestamp();
@@ -529,7 +537,7 @@ namespace BlackMisc
if (m_batchMode <= 0 && ! m_batchedValues.empty())
{
qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
forEachIntersection(m_elements, m_batchedValues, [timestamp](const QString &, const ElementPtr &element, CVariantMap::const_iterator it)
forEachIntersection(m_elements, m_batchedValues, [timestamp](const QString &, const ElementPtr & element, CVariantMap::const_iterator it)
{
Q_ASSERT(isSafeToIncrement(element->m_pendingChanges));
element->m_pendingChanges++;
@@ -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())
{
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)
{

View File

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

View File

@@ -105,7 +105,7 @@ namespace BlackMisc
CVariantMap m_batchedValues;
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

View File

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