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

View File

@ -471,20 +471,22 @@ void DeviceScreener::start() {
m_lastApiUpdate[device.id] = now; m_lastApiUpdate[device.id] = now;
} }
// История экрана на сервере логов: проверяем не чаще раза в // Отправка истории экрана в arc-monitoring отключена по запросу.
// kMonitoringScreenIntervalSecs и шлём кадр только если экран // Код сохранён закомментированным на случай возврата функционала.
// изменился относительно последнего отправленного (дедуп по sha256). // // История экрана на сервере логов: проверяем не чаще раза в
if (!device.image.isEmpty() // // kMonitoringScreenIntervalSecs и шлём кадр только если экран
&& now - m_lastMonitoringScreen.value(device.id, 0) >= kMonitoringScreenIntervalSecs) { // // изменился относительно последнего отправленного (дедуп по sha256).
m_lastMonitoringScreen[device.id] = now; // if (!device.image.isEmpty()
const QByteArray hash = // && now - m_lastMonitoringScreen.value(device.id, 0) >= kMonitoringScreenIntervalSecs) {
QCryptographicHash::hash(device.image, QCryptographicHash::Sha256); // m_lastMonitoringScreen[device.id] = now;
if (m_lastSentScreenHash.value(device.id) != hash) { // const QByteArray hash =
m_lastSentScreenHash[device.id] = hash; // QCryptographicHash::hash(device.image, QCryptographicHash::Sha256);
dispatchScreenshotToMonitoring(SettingsDAO::get("desktop_id"), // if (m_lastSentScreenHash.value(device.id) != hash) {
device.id, device.name, device.image); // 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); QFile srcLog(logPath);
if (srcLog.open(QIODevice::ReadOnly)) { if (srcLog.open(QIODevice::ReadOnly)) {
const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600); const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600);
const qint64 size = srcLog.size();
// Binary search for the first line with ts >= cutoff. // Find the byte offset of the first log entry within the last hour by
// Relies on monotonically non-decreasing timestamps in the log. // walking the file backwards in blocks from EOF.
qint64 lo = 0, hi = srcLog.size(); //
while (lo < hi) { // We can't binary-search this: error dumps log multi-line payloads
const qint64 mid = lo + (hi - lo) / 2; // (JSON, ADB screen XML, stack traces) whose continuation lines carry no
srcLog.seek(mid); // leading timestamp. A binary probe landing on such a line parsed an
if (mid > 0) srcLog.readLine(); // align to next line boundary // invalid date, the old code treated that as "old" and advanced its
const qint64 linePos = srcLog.pos(); // marker — and when the very last line of the file was a continuation
if (linePos >= hi) { hi = mid; continue; } // line (the usual case on error) the marker ran off the end and the dump
const QByteArray probe = srcLog.readLine(); // came out empty. Scanning from the tail also keeps this cheap on a huge
if (probe.isEmpty()) { hi = mid; continue; } // log: we stop the moment a block contains an entry older than the cutoff
const QDateTime ts = QDateTime::fromString( // and never touch the older prefix.
QString::fromLatin1(probe.left(23)), "yyyy-MM-dd hh:mm:ss.zzz"); static const qint64 BLOCK = 1 << 20; // 1 MiB
if (!ts.isValid() || ts < cutoff) qint64 startPos = size; // lowest offset of an in-window entry; == size => none yet
lo = srcLog.pos(); // this line is old, search upper half qint64 hi = size; // region [hi, size) already scanned
else bool sawOld = false;
hi = linePos; // this line is new, search lower half 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"); QFile dstLog(destDir + "/application.log");
if (dstLog.open(QIODevice::WriteOnly)) { if (dstLog.open(QIODevice::WriteOnly)) {
while (!srcLog.atEnd()) while (!srcLog.atEnd())
dstLog.write(srcLog.readLine()); dstLog.write(srcLog.readLine());
dstLog.flush();
any = true; any = true;
} }
} else { } else {