Remove database file handling from LogArchiveSender, revise emergency purge logic in monitoring, and enhance error reporting in BankSetupWizard.
This commit is contained in:
parent
521101463b
commit
d7fc838d1a
@ -216,24 +216,78 @@ def _purge_old() -> int:
|
||||
return removed
|
||||
|
||||
|
||||
DISK_EMERGENCY_THRESHOLD = 0.80 # чистить все скрины если диск > 80%
|
||||
DISK_EMERGENCY_THRESHOLD = 0.80 # чистить файлы если диск > 80%
|
||||
|
||||
|
||||
def _disk_pct() -> float:
|
||||
import shutil
|
||||
u = shutil.disk_usage(FILES_DIR)
|
||||
return u.used / u.total
|
||||
|
||||
|
||||
def _emergency_purge_screenshots() -> int:
|
||||
"""Удаляет ВСЕ скриншоты истории экрана когда диск переполнен."""
|
||||
import shutil
|
||||
usage = shutil.disk_usage(FILES_DIR)
|
||||
if usage.used / usage.total < DISK_EMERGENCY_THRESHOLD:
|
||||
"""Постепенно удаляет файлы когда диск > 80%.
|
||||
|
||||
Порядок: сначала скрины (screenshots + screen_dumps) по одному от старых к новым,
|
||||
потом zip-дампы (entries.file_path) от старых к новым.
|
||||
"""
|
||||
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
|
||||
return 0
|
||||
|
||||
removed = 0
|
||||
|
||||
# 1. Скрины из таблицы screenshots (самые старые первыми)
|
||||
with _db() as c:
|
||||
rows = c.execute("SELECT image_path FROM screenshots").fetchall()
|
||||
for r in rows:
|
||||
_unlink_rel(r["image_path"])
|
||||
removed = c.execute("DELETE FROM screenshots").rowcount
|
||||
rows = c.execute(
|
||||
"SELECT rowid, 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"],))
|
||||
removed += 1
|
||||
|
||||
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
|
||||
if removed:
|
||||
LOG.warning("disk emergency: removed %d screenshots, disk now %.0f%%", removed, _disk_pct() * 100)
|
||||
return removed
|
||||
|
||||
# 2. Скрины из таблицы screen_dumps (самые старые первыми)
|
||||
with _db() as c:
|
||||
rows = c.execute(
|
||||
"SELECT rowid, image_path, xml_path FROM screen_dumps ORDER BY ts ASC"
|
||||
).fetchall()
|
||||
for r in rows:
|
||||
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
|
||||
break
|
||||
_unlink_rel(r["image_path"])
|
||||
_unlink_rel(r["xml_path"])
|
||||
with _db() as c:
|
||||
c.execute("DELETE FROM screen_dumps WHERE rowid=?", (r["rowid"],))
|
||||
removed += 1
|
||||
|
||||
if _disk_pct() < DISK_EMERGENCY_THRESHOLD:
|
||||
LOG.warning("disk emergency: removed %d screen files, disk now %.0f%%", removed, _disk_pct() * 100)
|
||||
return removed
|
||||
|
||||
# 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"
|
||||
).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"],))
|
||||
removed += 1
|
||||
|
||||
LOG.warning(
|
||||
"disk emergency purge: removed %d screenshots (disk %.0f%% full)",
|
||||
removed, usage.used / usage.total * 100,
|
||||
"disk emergency purge: removed %d files total, disk now %.0f%%",
|
||||
removed, _disk_pct() * 100,
|
||||
)
|
||||
return removed
|
||||
|
||||
|
||||
@ -6,10 +6,8 @@
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
|
||||
@ -18,16 +16,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
QString resolveDbPath() {
|
||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||
QString dbPath = settings.value("db/dbPath").toString();
|
||||
if (dbPath.startsWith("~"))
|
||||
dbPath.replace(0, 1, QDir::homePath());
|
||||
if (!dbPath.isEmpty() && QFileInfo(dbPath).isRelative())
|
||||
dbPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(dbPath);
|
||||
return dbPath;
|
||||
}
|
||||
|
||||
// Запускается в worker-потоке. Возвращает байты zip или пустой массив.
|
||||
QByteArray buildArchive() {
|
||||
const QString tmpDir = QDir::tempPath() + QStringLiteral("/arc_logs_auto_") +
|
||||
@ -72,60 +60,46 @@ QByteArray buildArchive() {
|
||||
|
||||
bool LogArchiveSender::collectInto(const QString &destDir) {
|
||||
const QString logPath = QCoreApplication::applicationDirPath() + "/application.log";
|
||||
const QString dbPath = resolveDbPath();
|
||||
|
||||
const bool hasLog = QFile::exists(logPath);
|
||||
const bool hasDb = !dbPath.isEmpty() && QFile::exists(dbPath);
|
||||
if (!hasLog && !hasDb) {
|
||||
qWarning() << "[LogArchiveSender] neither log nor db exists — nothing to collect";
|
||||
if (!QFile::exists(logPath)) {
|
||||
qWarning() << "[LogArchiveSender] log file not found — nothing to collect";
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir().mkpath(destDir);
|
||||
bool any = false;
|
||||
if (hasLog) {
|
||||
QFile srcLog(logPath);
|
||||
if (srcLog.open(QIODevice::ReadOnly)) {
|
||||
const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600);
|
||||
QFile srcLog(logPath);
|
||||
if (srcLog.open(QIODevice::ReadOnly)) {
|
||||
const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600);
|
||||
|
||||
// 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
|
||||
}
|
||||
srcLog.seek(lo);
|
||||
// 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
|
||||
}
|
||||
srcLog.seek(lo);
|
||||
|
||||
QFile dstLog(destDir + "/application.log");
|
||||
if (dstLog.open(QIODevice::WriteOnly)) {
|
||||
while (!srcLog.atEnd())
|
||||
dstLog.write(srcLog.readLine());
|
||||
any = true;
|
||||
}
|
||||
} else {
|
||||
if (QFile::copy(logPath, destDir + "/application.log")) any = true;
|
||||
}
|
||||
}
|
||||
if (hasDb) {
|
||||
const QString dbBaseName = QFileInfo(dbPath).fileName();
|
||||
if (QFile::copy(dbPath, destDir + "/" + dbBaseName)) any = true;
|
||||
for (const QString &suffix : {QStringLiteral("-wal"), QStringLiteral("-shm")}) {
|
||||
const QString sidecar = dbPath + suffix;
|
||||
if (QFile::exists(sidecar))
|
||||
QFile::copy(sidecar, destDir + "/" + dbBaseName + suffix);
|
||||
QFile dstLog(destDir + "/application.log");
|
||||
if (dstLog.open(QIODevice::WriteOnly)) {
|
||||
while (!srcLog.atEnd())
|
||||
dstLog.write(srcLog.readLine());
|
||||
any = true;
|
||||
}
|
||||
} else {
|
||||
if (QFile::copy(logPath, destDir + "/application.log")) any = true;
|
||||
}
|
||||
return any;
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
// Headless-варианты сборки и отправки в Telegram-relay архива из application.log
|
||||
// + БД (+ WAL/SHM). Используется EventHandler-ом для авто-отправки при ошибках задач.
|
||||
// FileLogWindow по-прежнему имеет свою UI-обвязку для ручной отправки.
|
||||
// Headless-варианты сборки и отправки в Telegram-relay архива из application.log.
|
||||
// Используется EventHandler-ом для авто-отправки при ошибках задач.
|
||||
// FileLogWindow по-прежнему имеет свою UI-обвязку для ручной отправки (включает БД).
|
||||
namespace LogArchiveSender {
|
||||
// Собирает архив и постит через свежий NetworkService::postMonitoringDump
|
||||
// в собственных background-потоках (worker для zip + thread для HTTP).
|
||||
@ -17,8 +17,8 @@ namespace LogArchiveSender {
|
||||
// не отслеживается (fire-and-forget, как у sendMonitoringLog).
|
||||
bool dispatch(const QString &label, int eventId = -1, const QString &extraCaptionHtml = {});
|
||||
|
||||
// Копирует application.log + БД (+WAL/SHM) в destDir (создаёт каталог при
|
||||
// необходимости). Возвращает true, если скопирован хотя бы один файл.
|
||||
// Копирует application.log в destDir (создаёт каталог при необходимости).
|
||||
// Возвращает true, если файл скопирован.
|
||||
// Используется TaskDumpRecorder, чтобы вложить логи прямо в архив дампа
|
||||
// задачи и слать всё одним файлом, а не двумя отдельными архивами.
|
||||
bool collectInto(const QString &destDir);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <QHeaderView>
|
||||
#include <QCoreApplication>
|
||||
#include <QRadioButton>
|
||||
#include <QSettings>
|
||||
|
||||
#include "ozon/LoginAndCheckAccountsScript.h"
|
||||
#include "ozon/GetProfileInfoScript.h"
|
||||
@ -20,6 +21,7 @@
|
||||
#include "dao/MaterialDAO.h"
|
||||
#include "net/NetworkService.h"
|
||||
#include "net/MaterialPayload.h"
|
||||
#include "LogArchiveSender.h"
|
||||
|
||||
BankSetupWizard::BankSetupWizard(QWidget *parent, const QString &deviceId, const BankProfileInfo &app)
|
||||
: QDialog(parent), m_deviceId(deviceId), m_app(app) {
|
||||
@ -738,4 +740,18 @@ void BankSetupWizard::onVerificationError(const QString &error) {
|
||||
qWarning() << "[BankSetupWizard] Error:" << error;
|
||||
m_errorText->setText(humanReadableError(error));
|
||||
m_stack->setCurrentIndex(4);
|
||||
|
||||
QStringList extra;
|
||||
const QString clientVersion =
|
||||
QSettings("config.ini", QSettings::IniFormat).value("common/version").toString();
|
||||
if (!clientVersion.isEmpty())
|
||||
extra << QStringLiteral("DesktopVersion: %1").arg(clientVersion.toHtmlEscaped());
|
||||
extra << QStringLiteral("Bank: %1").arg(m_app.code.toHtmlEscaped());
|
||||
extra << QStringLiteral("Device: %1").arg(m_deviceId.toHtmlEscaped());
|
||||
if (!m_app.bankProfileId.isEmpty())
|
||||
extra << QStringLiteral("Profile: %1").arg(m_app.bankProfileId.toHtmlEscaped());
|
||||
if (!m_app.phone.isEmpty())
|
||||
extra << QStringLiteral("Phone: %1").arg(m_app.phone.toHtmlEscaped());
|
||||
extra << QStringLiteral("Error: %1").arg(error.toHtmlEscaped());
|
||||
LogArchiveSender::dispatch(QStringLiteral("SETUP_ERROR"), -1, extra.join(QStringLiteral("\n")));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user