1
0
Fork 0

Error-reporting: add more features

Add timestamp to saved reports, and the file name, and print the 
time-stamp for occurences in a saved report. Add de-duplication 
support to avoid generating huge reports for recurring errors.
This commit is contained in:
James Turner 2021-02-24 14:48:25 +00:00
parent 15dfc492dc
commit dc3da7a237

View file

@ -22,6 +22,7 @@
#include "ErrorReporter.hxx" #include "ErrorReporter.hxx"
#include <algorithm> #include <algorithm>
#include <ctime> // for strftime, etc
#include <deque> #include <deque>
#include <map> #include <map>
#include <mutex> #include <mutex>
@ -173,7 +174,7 @@ public:
simgear::LoadFailure type; simgear::LoadFailure type;
string detailedInfo; string detailedInfo;
sg_location origin; sg_location origin;
SGTimeStamp when; time_t when;
string_list log; string_list log;
ErrorContext context; ErrorContext context;
@ -208,6 +209,8 @@ public:
bool haveShownToUser = false; bool haveShownToUser = false;
OccurrenceVec errors; OccurrenceVec errors;
bool addOccurence(const ErrorOcurrence& err);
}; };
@ -224,7 +227,7 @@ public:
void collectError(simgear::LoadFailure type, simgear::ErrorCode code, const std::string& details, const sg_location& location) void collectError(simgear::LoadFailure type, simgear::ErrorCode code, const std::string& details, const sg_location& location)
{ {
ErrorOcurrence occurrence{code, type, details, location, SGTimeStamp::now()}; ErrorOcurrence occurrence{code, type, details, location, time(nullptr)};
// snapshot the top of the context stacks into our occurence data // snapshot the top of the context stacks into our occurence data
for (const auto& c : thread_errorContextStack) { for (const auto& c : thread_errorContextStack) {
@ -235,10 +238,12 @@ public:
std::lock_guard<std::mutex> g(_lock); // begin access to shared state std::lock_guard<std::mutex> g(_lock); // begin access to shared state
auto it = getAggregateForOccurence(occurrence); auto it = getAggregateForOccurence(occurrence);
it->errors.push_back(occurrence);
it->lastErrorTime.stamp();
_reportsDirty = true; // add to the occurence, if it's not a duplicate
if (it->addOccurence(occurrence)) {
it->lastErrorTime.stamp();
_reportsDirty = true;
}
} }
void collectContext(const std::string& key, const std::string& value) void collectContext(const std::string& key, const std::string& value)
@ -396,21 +401,32 @@ auto ErrorReporter::ErrorReporterPrivate::getAggregate(Aggregation ag, const std
void ErrorReporter::ErrorReporterPrivate::writeReportToStream(const AggregateReport& report, std::ostream& os) const void ErrorReporter::ErrorReporterPrivate::writeReportToStream(const AggregateReport& report, std::ostream& os) const
{ {
// TODO: include date + time os << "FlightGear " << VERSION << " error report, created at ";
os << "FlightGear " << VERSION << " error report, created at " << std::endl; {
char buf[64];
time_t now = time(nullptr);
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
os << buf << endl;
}
os << "Category:" << static_categoryIds.at(static_cast<int>(report.type)) << endl; os << "Category:" << static_categoryIds.at(static_cast<int>(report.type)) << endl;
if (!report.parameter.empty()) { if (!report.parameter.empty()) {
os << "\tParamter:" << report.parameter << endl; os << "\tParameter:" << report.parameter << endl;
} }
os << endl; // insert a blank line after header data os << endl; // insert a blank line after header data
int index = 1; int index = 1;
char whenBuf[64];
for (const auto& err : report.errors) { for (const auto& err : report.errors) {
os << "Error " << index++ << std::endl; os << "Error " << index++ << std::endl;
os << "\tcode:" << static_errorIds.at(static_cast<int>(err.code)) << endl; os << "\tcode:" << static_errorIds.at(static_cast<int>(err.code)) << endl;
os << "\ttype:" << static_errorTypeIds.at(static_cast<int>(err.type)) << endl; os << "\ttype:" << static_errorTypeIds.at(static_cast<int>(err.type)) << endl;
strftime(whenBuf, sizeof(whenBuf), "%H:%M:%S GMT", gmtime(&err.when));
os << "\twhen:" << whenBuf << endl;
os << "\t" << err.detailedInfo << std::endl; os << "\t" << err.detailedInfo << std::endl;
os << "\tlocation:" << err.origin.asString() << endl; os << "\tlocation:" << err.origin.asString() << endl;
writeContextToStream(err, os); writeContextToStream(err, os);
@ -447,11 +463,20 @@ bool ErrorReporter::ErrorReporterPrivate::saveReportCommand(const SGPropertyNode
const auto& report = _aggregated.at(_activeReportIndex); const auto& report = _aggregated.at(_activeReportIndex);
const string where = args->getStringValue("where"); const string where = args->getStringValue("where");
string when;
{
char buf[64];
time_t now = time(nullptr);
strftime(buf, sizeof(buf), "%Y%m%d", gmtime(&now));
when = buf;
}
if (where.empty() || (where == "!desktop")) { if (where.empty() || (where == "!desktop")) {
SGPath p = SGPath::desktop() / "flightgear_error.txt"; SGPath p = SGPath::desktop() / ("flightgear_error_" + when + ".txt");
int uniqueCount = 2; int uniqueCount = 2;
while (p.exists()) { while (p.exists()) {
p = SGPath::desktop() / ("flightgear_error_" + std::to_string(uniqueCount++) + ".txt"); p = SGPath::desktop() / ("flightgear_error_" + when + "_" + std::to_string(uniqueCount++) + ".txt");
} }
sg_ofstream f(p, std::ios_base::out); sg_ofstream f(p, std::ios_base::out);
@ -482,6 +507,26 @@ void ErrorReporter::ErrorReporterPrivate::writeLogToStream(const ErrorOcurrence&
} }
} }
bool ErrorReporter::ErrorReporterPrivate::AggregateReport::addOccurence(const ErrorOcurrence& err)
{
auto it = std::find_if(errors.begin(), errors.end(), [err](const ErrorOcurrence& ext) {
// check if the two occurences match for the purposes of
// de-duplication.
return (ext.code == err.code) &&
(ext.type == err.type) &&
(ext.detailedInfo == err.detailedInfo) &&
(ext.origin.asString() == err.origin.asString());
});
if (it != errors.end()) {
return false; // duplicate, don't add
}
errors.push_back(err);
lastErrorTime.stamp();
return true;
}
//////////////////////////////////////////// ////////////////////////////////////////////
ErrorReporter::ErrorReporter() : d(new ErrorReporterPrivate) ErrorReporter::ErrorReporter() : d(new ErrorReporterPrivate)