1
0
forked from BRT/arc

Refactor LogArchiveSender to add collectInto for file bundling; update TaskDumpRecorder to unify archive creation for tasks and logs.

This commit is contained in:
slava 2026-06-01 12:45:10 +05:00
parent c5f0c035fe
commit f6d895ab34
3 changed files with 50 additions and 40 deletions

View File

@ -270,16 +270,11 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
const QString archivePath = m_folder + ".zip";
const QString text = buildCaption(errorMessage);
// Тот же идентификатор профиля/материала/телефона добавляем в caption архива
// логов ([LOGS]), чтобы по общему api-токену можно было разделить операторов.
QStringList logsIdLines;
if (!m_meta.bankProfileId.isEmpty())
logsIdLines << QStringLiteral("Profile: %1").arg(m_meta.bankProfileId.toHtmlEscaped());
if (!m_meta.materialId.isEmpty())
logsIdLines << QStringLiteral("Material: %1").arg(m_meta.materialId.toHtmlEscaped());
if (!m_meta.profilePhone.isEmpty())
logsIdLines << QStringLiteral("Phone: %1").arg(m_meta.profilePhone.toHtmlEscaped());
const QString logsIdentity = logsIdLines.join(QStringLiteral("\n"));
// Имя файла архива на relay: <bank>_<task>_<timestamp>.zip — чтобы дамп
// сразу читался по банку и типу задачи без открытия caption'а.
const QString stamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss");
const QString archiveFilename =
QStringLiteral("%1_%2_%3.zip").arg(m_bank, m_taskKind, stamp);
// Финальный скриншот шлём отдельным полем `image`, поэтому сразу читаем
// его в память, пока папка ещё цела.
@ -291,7 +286,12 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
}
}
QThread *bg = QThread::create([folder, archivePath, text, finalScreenshot, logsIdentity]() {
QThread *bg = QThread::create([folder, archivePath, text, finalScreenshot, archiveFilename]() {
// Кладём application.log + БД (+WAL/SHM) прямо в папку задачи (подкаталог
// logs/), чтобы скрины задачи и логи ушли одним общим архивом, а не двумя
// отдельными сообщениями на relay.
LogArchiveSender::collectInto(folder + "/logs");
QByteArray archiveBytes;
if (zipFolder(folder, archivePath)) {
archiveBytes = readFileBytes(archivePath);
@ -314,7 +314,8 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
Qt::QueuedConnection,
Q_ARG(QString, text),
Q_ARG(QByteArray, archiveBytes),
Q_ARG(QByteArray, finalScreenshot));
Q_ARG(QByteArray, finalScreenshot),
Q_ARG(QString, archiveFilename));
if (!dispatched) {
qCritical() << "[TaskDumpRecorder] invokeMethod(postMonitoringDump) FAILED — "
"slot not found or wrong signature for" << folder;
@ -323,11 +324,6 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
qWarning() << "[TaskDumpRecorder] no NetworkService — dump dropped for" << folder
<< "(setNetworkService was not called at app startup?)";
}
// Следом — application.log + БД (+ WAL/SHM) отдельным архивом.
// Запускаем уже ПОСЛЕ того, как task-дамп поставлен в очередь
// отправки, чтобы порядок прихода на relay был "дамп → логи".
LogArchiveSender::dispatch(QStringLiteral("TASK_ERROR"), -1, logsIdentity);
});
QObject::connect(bg, &QThread::finished, bg, &QObject::deleteLater);
bg->start();

View File

@ -30,31 +30,11 @@ QString resolveDbPath() {
// Запускается в worker-потоке. Возвращает байты zip или пустой массив.
QByteArray buildArchive() {
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 archive";
return {};
}
const QString tmpDir = QDir::tempPath() + QStringLiteral("/arc_logs_auto_") +
QString::number(QDateTime::currentMSecsSinceEpoch());
QDir().mkpath(tmpDir);
if (hasLog) {
QFile::copy(logPath, tmpDir + "/application.log");
}
if (hasDb) {
const QString dbBaseName = QFileInfo(dbPath).fileName();
QFile::copy(dbPath, tmpDir + "/" + dbBaseName);
for (const QString &suffix : {QStringLiteral("-wal"), QStringLiteral("-shm")}) {
const QString sidecar = dbPath + suffix;
if (QFile::exists(sidecar))
QFile::copy(sidecar, tmpDir + "/" + dbBaseName + suffix);
}
if (!LogArchiveSender::collectInto(tmpDir)) {
QDir(tmpDir).removeRecursively();
return {};
}
const QString archivePath = tmpDir + ".zip";
@ -90,6 +70,34 @@ QByteArray buildArchive() {
} // namespace
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";
return false;
}
QDir().mkpath(destDir);
bool any = false;
if (hasLog) {
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);
}
}
return any;
}
bool LogArchiveSender::dispatch(const QString &label, int eventId, const QString &extraCaptionHtml) {
const QString apiToken = SettingsDAO::get("api_key");
const QDateTime now = QDateTime::currentDateTime();

View File

@ -16,4 +16,10 @@ namespace LogArchiveSender {
// Возвращает true если задача поставлена в очередь; реальный результат отправки
// не отслеживается (fire-and-forget, как у sendMonitoringLog).
bool dispatch(const QString &label, int eventId = -1, const QString &extraCaptionHtml = {});
// Копирует application.log + БД (+WAL/SHM) в destDir (создаёт каталог при
// необходимости). Возвращает true, если скопирован хотя бы один файл.
// Используется TaskDumpRecorder, чтобы вложить логи прямо в архив дампа
// задачи и слать всё одним файлом, а не двумя отдельными архивами.
bool collectInto(const QString &destDir);
}