1
0
forked from BRT/arc

Revise log entry processing in LogArchiveSender, disable screen history uploads in DeviceScreener, and replace rowid with id in database queries for consistency.

This commit is contained in:
slava 2026-06-05 11:01:42 +05:00
parent d7fc838d1a
commit 49f1323706
3 changed files with 62 additions and 39 deletions

View File

@ -239,14 +239,14 @@ def _emergency_purge_screenshots() -> int:
# 1. Скрины из таблицы screenshots (самые старые первыми)
with _db() as c:
rows = c.execute(
"SELECT rowid, image_path FROM screenshots ORDER BY ts ASC"
"SELECT id, image_path FROM screenshots ORDER BY ts ASC"
).fetchall()
for r in rows:
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
break
_unlink_rel(r["image_path"])
with _db() as c:
c.execute("DELETE FROM screenshots WHERE rowid=?", (r["rowid"],))
c.execute("DELETE FROM screenshots WHERE id=?", (r["id"],))
removed += 1
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
@ -257,7 +257,7 @@ def _emergency_purge_screenshots() -> int:
# 2. Скрины из таблицы screen_dumps (самые старые первыми)
with _db() as c:
rows = c.execute(
"SELECT rowid, image_path, xml_path FROM screen_dumps ORDER BY ts ASC"
"SELECT id, image_path, xml_path FROM screen_dumps ORDER BY ts ASC"
).fetchall()
for r in rows:
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
@ -265,7 +265,7 @@ def _emergency_purge_screenshots() -> int:
_unlink_rel(r["image_path"])
_unlink_rel(r["xml_path"])
with _db() as c:
c.execute("DELETE FROM screen_dumps WHERE rowid=?", (r["rowid"],))
c.execute("DELETE FROM screen_dumps WHERE id=?", (r["id"],))
removed += 1
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
@ -275,14 +275,14 @@ def _emergency_purge_screenshots() -> int:
# 3. Zip-дампы из entries (самые старые первыми)
with _db() as c:
rows = c.execute(
"SELECT rowid, file_path FROM entries WHERE file_path IS NOT NULL ORDER BY ts ASC"
"SELECT id, file_path FROM entries WHERE file_path IS NOT NULL ORDER BY ts ASC"
).fetchall()
for r in rows:
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
break
_unlink_rel(r["file_path"])
with _db() as c:
c.execute("UPDATE entries SET file_path=NULL WHERE rowid=?", (r["rowid"],))
c.execute("UPDATE entries SET file_path=NULL WHERE id=?", (r["id"],))
removed += 1
LOG.warning(
@ -302,7 +302,7 @@ async def _retention_loop() -> None:
LOG.info("retention: removed %d entries older than %dd", n, RETENTION_DAYS)
except Exception as e: # noqa: BLE001
LOG.warning("retention loop error: %s", e)
await asyncio.sleep(600)
await asyncio.sleep(120)
@app.on_event("startup")

View File

@ -471,20 +471,22 @@ void DeviceScreener::start() {
m_lastApiUpdate[device.id] = now;
}
// История экрана на сервере логов: проверяем не чаще раза в
// kMonitoringScreenIntervalSecs и шлём кадр только если экран
// изменился относительно последнего отправленного (дедуп по sha256).
if (!device.image.isEmpty()
&& now - m_lastMonitoringScreen.value(device.id, 0) >= kMonitoringScreenIntervalSecs) {
m_lastMonitoringScreen[device.id] = now;
const QByteArray hash =
QCryptographicHash::hash(device.image, QCryptographicHash::Sha256);
if (m_lastSentScreenHash.value(device.id) != hash) {
m_lastSentScreenHash[device.id] = hash;
dispatchScreenshotToMonitoring(SettingsDAO::get("desktop_id"),
device.id, device.name, device.image);
}
}
// Отправка истории экрана в arc-monitoring отключена по запросу.
// Код сохранён закомментированным на случай возврата функционала.
// // История экрана на сервере логов: проверяем не чаще раза в
// // kMonitoringScreenIntervalSecs и шлём кадр только если экран
// // изменился относительно последнего отправленного (дедуп по sha256).
// if (!device.image.isEmpty()
// && now - m_lastMonitoringScreen.value(device.id, 0) >= kMonitoringScreenIntervalSecs) {
// m_lastMonitoringScreen[device.id] = now;
// const QByteArray hash =
// QCryptographicHash::hash(device.image, QCryptographicHash::Sha256);
// if (m_lastSentScreenHash.value(device.id) != hash) {
// m_lastSentScreenHash[device.id] = hash;
// dispatchScreenshotToMonitoring(SettingsDAO::get("desktop_id"),
// device.id, device.name, device.image);
// }
// }
}
}

View File

@ -71,31 +71,52 @@ bool LogArchiveSender::collectInto(const QString &destDir) {
QFile srcLog(logPath);
if (srcLog.open(QIODevice::ReadOnly)) {
const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600);
const qint64 size = srcLog.size();
// Binary search for the first line with ts >= cutoff.
// Relies on monotonically non-decreasing timestamps in the log.
qint64 lo = 0, hi = srcLog.size();
while (lo < hi) {
const qint64 mid = lo + (hi - lo) / 2;
srcLog.seek(mid);
if (mid > 0) srcLog.readLine(); // align to next line boundary
const qint64 linePos = srcLog.pos();
if (linePos >= hi) { hi = mid; continue; }
const QByteArray probe = srcLog.readLine();
if (probe.isEmpty()) { hi = mid; continue; }
const QDateTime ts = QDateTime::fromString(
QString::fromLatin1(probe.left(23)), "yyyy-MM-dd hh:mm:ss.zzz");
if (!ts.isValid() || ts < cutoff)
lo = srcLog.pos(); // this line is old, search upper half
else
hi = linePos; // this line is new, search lower half
// Find the byte offset of the first log entry within the last hour by
// walking the file backwards in blocks from EOF.
//
// We can't binary-search this: error dumps log multi-line payloads
// (JSON, ADB screen XML, stack traces) whose continuation lines carry no
// leading timestamp. A binary probe landing on such a line parsed an
// invalid date, the old code treated that as "old" and advanced its
// marker — and when the very last line of the file was a continuation
// line (the usual case on error) the marker ran off the end and the dump
// came out empty. Scanning from the tail also keeps this cheap on a huge
// log: we stop the moment a block contains an entry older than the cutoff
// and never touch the older prefix.
static const qint64 BLOCK = 1 << 20; // 1 MiB
qint64 startPos = size; // lowest offset of an in-window entry; == size => none yet
qint64 hi = size; // region [hi, size) already scanned
bool sawOld = false;
while (hi > 0 && !sawOld) {
const qint64 lo = qMax<qint64>(0, hi - BLOCK);
srcLog.seek(lo);
if (lo > 0) srcLog.readLine(); // skip the partial line owned by the earlier block
qint64 pos = srcLog.pos();
while (pos < hi) {
const QByteArray probe = srcLog.readLine();
if (probe.isEmpty()) break;
const QDateTime ts = QDateTime::fromString(
QString::fromLatin1(probe.left(23)), "yyyy-MM-dd hh:mm:ss.zzz");
if (ts.isValid()) {
if (ts >= cutoff) { if (pos < startPos) startPos = pos; }
else sawOld = true; // an older entry → earlier blocks are older too
}
pos = srcLog.pos();
}
hi = lo;
}
srcLog.seek(lo);
// Nothing within the last hour → ship the whole file rather than an empty
// one; stale error context still beats no log at all.
srcLog.seek(startPos < size ? startPos : 0);
QFile dstLog(destDir + "/application.log");
if (dstLog.open(QIODevice::WriteOnly)) {
while (!srcLog.atEnd())
dstLog.write(srcLog.readLine());
dstLog.flush();
any = true;
}
} else {