diff --git a/launcher/logs/LogParser.cpp b/launcher/logs/LogParser.cpp index 13ff5f072..2c363a088 100644 --- a/launcher/logs/LogParser.cpp +++ b/launcher/logs/LogParser.cpp @@ -57,7 +57,8 @@ std::optional 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); diff --git a/tests/XmlLogs_test.cpp b/tests/XmlLogs_test.cpp index 4df846e3e..f604f8be6 100644 --- a/tests/XmlLogs_test.cpp +++ b/tests/XmlLogs_test.cpp @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -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"( )", + R"( )", + R"( )", + R"( )", + R"( )", + R"( )", + }; + + LogParser parser; + QList timestamps; + + for (const auto& line : lines) { + parser.appendLine(line); + + for (const auto& item : parser.parseAvailable()) { + QVERIFY(std::holds_alternative(item)); + timestamps.append(std::get(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");