mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-09-18 02:34:32 -04:00
fix(logs): read log4j event timestamps as milliseconds
log4j's XMLLayout writes the event time in milliseconds since the epoch, but parseAttributes() handed it to QDateTime::fromSecsSinceEpoch(). Every entry in the Minecraft Log tab therefore carried an instant about 55000 years out, and once rendered as HH:mm:ss it showed a clock time with no relation to when the line was actually logged. The launcher's own sample log shows it plainly: the first two events of testdata/TestLogs/vanilla-1.21.5.xml.log are stamped 1745005148589 and 1745005150587, just under two seconds apart, which is also how far apart they are in the plain text capture of the same startup sequence. Read as seconds they land 33 minutes apart, in the year 57267. The added test compares instants rather than rendered clock times, so it does not depend on the time zone it runs in. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: umutcagand <237324081+c8dhjp4tyv-bit@users.noreply.github.com>
This commit is contained in:
1 parent
c5df040ac0
commit
6bc731da54
2 files changed
+37
-1
No files matched your search
@@ -57,7 +57,8 @@ std::optional<LogParser::LogEntry> LogParser::parseAttributes()
|
||||
m_parser.raiseError("log4j:Event Missing required attribute: timestamp");
|
||||
return {};
|
||||
}
|
||||
entry.timestamp = QDateTime::fromSecsSinceEpoch(value.trimmed().toLongLong());
|
||||
// log4j's XMLLayout reports the event time in milliseconds
|
||||
entry.timestamp = QDateTime::fromMSecsSinceEpoch(value.trimmed().toLongLong());
|
||||
} else if (name == "level"_L1) {
|
||||
entry.levelText = value.trimmed().toString();
|
||||
entry.level = MessageLevel::fromName(entry.levelText);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QRegularExpression>
|
||||
@@ -50,6 +51,40 @@ class XmlLogParseTest : public QObject {
|
||||
MessageLevel::Error);
|
||||
}
|
||||
|
||||
void parseEventTimestamp()
|
||||
{
|
||||
// Taken verbatim from testdata/TestLogs/vanilla-1.21.5.xml.log. These two events sit just under
|
||||
// two seconds apart, as they do in the plain text capture of the same startup sequence - read as
|
||||
// seconds they would be 33 minutes apart, in the year 57267.
|
||||
const QStringList lines = {
|
||||
R"( <log4j:Event logger="com.mojang.datafixers.DataFixerBuilder" timestamp="1745005148589" level="INFO" thread="Datafixer Bootstrap">)",
|
||||
R"( <log4j:Message><![CDATA[263 Datafixer optimizations took 906 milliseconds]]></log4j:Message>)",
|
||||
R"( </log4j:Event>)",
|
||||
R"( <log4j:Event logger="com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService" timestamp="1745005150587" level="INFO" thread="Render thread">)",
|
||||
R"( <log4j:Message><![CDATA[Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]]]></log4j:Message>)",
|
||||
R"( </log4j:Event>)",
|
||||
};
|
||||
|
||||
LogParser parser;
|
||||
QList<QDateTime> timestamps;
|
||||
|
||||
for (const auto& line : lines) {
|
||||
parser.appendLine(line);
|
||||
|
||||
for (const auto& item : parser.parseAvailable()) {
|
||||
QVERIFY(std::holds_alternative<LogParser::LogEntry>(item));
|
||||
timestamps.append(std::get<LogParser::LogEntry>(item).timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
QCOMPARE(timestamps.length(), 2);
|
||||
// Comparing instants rather than rendered clock times keeps this independent of the time zone.
|
||||
QCOMPARE(timestamps[0], QDateTime::fromMSecsSinceEpoch(1745005148589));
|
||||
QCOMPARE(timestamps[1], QDateTime::fromMSecsSinceEpoch(1745005150587));
|
||||
QCOMPARE(timestamps[0].toUTC().date(), QDate(2025, 4, 18));
|
||||
QCOMPARE(timestamps[0].msecsTo(timestamps[1]), 1998);
|
||||
}
|
||||
|
||||
void parseXml_data()
|
||||
{
|
||||
QString source = QFINDTESTDATA("testdata/TestLogs");
|
||||
|
||||
Reference in new issue
Block a user