Save and upload previous session logs before truncation; extend LogArchiveSender for specific log file handling.
This commit is contained in:
parent
49f1323706
commit
39e07ddb15
30
main.cpp
30
main.cpp
@ -9,6 +9,7 @@
|
||||
#include "database/dao/CommonDataDAO.h"
|
||||
#include "database/dao/DeviceDAO.h"
|
||||
#include "services/DeviceScreener.h"
|
||||
#include "services/LogArchiveSender.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
@ -341,6 +342,19 @@ int main(int argc, char *argv[]) {
|
||||
lightPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||
QApplication::setPalette(lightPalette);
|
||||
|
||||
// Сохраняем лог прошлой сессии ДО усечения: текущий application.log
|
||||
// переименовываем в application.prev.log. После успешной авторизации он
|
||||
// уходит в арк-мониторинг и удаляется (см. LogArchiveSender::dispatch).
|
||||
// Всегда сносим старый prev: он должен хранить ровно одну — предыдущую —
|
||||
// сессию и исчезать, если её не было.
|
||||
const QString prevLogPath = QDir::current().absoluteFilePath("application.prev.log");
|
||||
{
|
||||
const QString curLogPath = QDir::current().absoluteFilePath("application.log");
|
||||
QFile::remove(prevLogPath);
|
||||
if (QFile::exists(curLogPath))
|
||||
QFile::rename(curLogPath, prevLogPath);
|
||||
}
|
||||
|
||||
logFile.setFileName("application.log");
|
||||
// Очищаем лог-файл при каждом запуске
|
||||
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
|
||||
@ -378,13 +392,13 @@ int main(int argc, char *argv[]) {
|
||||
// 2. Если нет api_key — показываем окно для ввода
|
||||
const QString apiKey = SettingsDAO::get("api_key");
|
||||
|
||||
auto showRegistration = [&app]() {
|
||||
auto showRegistration = [&app, prevLogPath]() {
|
||||
auto *registrationWindow = new RegistrationWindow();
|
||||
registrationWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
registrationWindow->show();
|
||||
QObject::connect(
|
||||
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
|
||||
[registrationWindow, &app]() {
|
||||
[registrationWindow, &app, prevLogPath]() {
|
||||
auto *mainWindow = new MainWindow();
|
||||
mainWindow->show();
|
||||
registrationWindow->close();
|
||||
@ -396,6 +410,8 @@ int main(int argc, char *argv[]) {
|
||||
startDeviceScreener(app, net);
|
||||
startEventHandler(app);
|
||||
// startBlackPayByCardTest();
|
||||
// Авторизация прошла — шлём лог прошлой сессии в арк-мониторинг.
|
||||
LogArchiveSender::dispatch("STARTUP", -1, {}, prevLogPath);
|
||||
}, Qt::QueuedConnection);
|
||||
};
|
||||
|
||||
@ -406,7 +422,7 @@ int main(int argc, char *argv[]) {
|
||||
loader->setAttribute(Qt::WA_DeleteOnClose);
|
||||
loader->show();
|
||||
|
||||
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration](const int result) {
|
||||
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration, prevLogPath](const int result) {
|
||||
if (result == -1) {
|
||||
auto *errorWindow = new ErrorWindow(
|
||||
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
|
||||
@ -414,12 +430,12 @@ int main(int argc, char *argv[]) {
|
||||
);
|
||||
errorWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
errorWindow->show();
|
||||
QObject::connect(errorWindow, &ErrorWindow::retryRequested, errorWindow, [errorWindow, &app, showRegistration]() {
|
||||
QObject::connect(errorWindow, &ErrorWindow::retryRequested, errorWindow, [errorWindow, &app, showRegistration, prevLogPath]() {
|
||||
errorWindow->close();
|
||||
auto *newLoader = new LoaderWindow();
|
||||
newLoader->setAttribute(Qt::WA_DeleteOnClose);
|
||||
newLoader->show();
|
||||
QObject::connect(newLoader, &LoaderWindow::loadingFinished, newLoader, [newLoader, &app, showRegistration](const int r) {
|
||||
QObject::connect(newLoader, &LoaderWindow::loadingFinished, newLoader, [newLoader, &app, showRegistration, prevLogPath](const int r) {
|
||||
if (r == 1) {
|
||||
auto *mainWindow = new MainWindow();
|
||||
mainWindow->show();
|
||||
@ -431,6 +447,8 @@ int main(int argc, char *argv[]) {
|
||||
startDeviceScreener(app, net);
|
||||
startEventHandler(app);
|
||||
// startBlackPayByCardTest();
|
||||
// Авторизация прошла — шлём лог прошлой сессии в арк-мониторинг.
|
||||
LogArchiveSender::dispatch("STARTUP", -1, {}, prevLogPath);
|
||||
} else if (r == 0) {
|
||||
SettingsDAO::remove("api_key");
|
||||
showRegistration();
|
||||
@ -456,6 +474,8 @@ int main(int argc, char *argv[]) {
|
||||
startDeviceScreener(app, net);
|
||||
startEventHandler(app);
|
||||
// startBlackPayByCardTest();
|
||||
// Авторизация прошла — шлём лог прошлой сессии в арк-мониторинг.
|
||||
LogArchiveSender::dispatch("STARTUP", -1, {}, prevLogPath);
|
||||
} else if (result == 0) {
|
||||
SettingsDAO::remove("api_key");
|
||||
showRegistration();
|
||||
|
||||
@ -17,10 +17,10 @@
|
||||
namespace {
|
||||
|
||||
// Запускается в worker-потоке. Возвращает байты zip или пустой массив.
|
||||
QByteArray buildArchive() {
|
||||
QByteArray buildArchive(const QString &sourceLogPath) {
|
||||
const QString tmpDir = QDir::tempPath() + QStringLiteral("/arc_logs_auto_") +
|
||||
QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
if (!LogArchiveSender::collectInto(tmpDir)) {
|
||||
if (!LogArchiveSender::collectInto(tmpDir, sourceLogPath)) {
|
||||
QDir(tmpDir).removeRecursively();
|
||||
return {};
|
||||
}
|
||||
@ -58,15 +58,25 @@ QByteArray buildArchive() {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LogArchiveSender::collectInto(const QString &destDir) {
|
||||
const QString logPath = QCoreApplication::applicationDirPath() + "/application.log";
|
||||
bool LogArchiveSender::collectInto(const QString &destDir, const QString &sourceLogPath) {
|
||||
const bool wholeFile = !sourceLogPath.isEmpty();
|
||||
const QString logPath = wholeFile
|
||||
? sourceLogPath
|
||||
: QCoreApplication::applicationDirPath() + "/application.log";
|
||||
|
||||
if (!QFile::exists(logPath)) {
|
||||
qWarning() << "[LogArchiveSender] log file not found — nothing to collect";
|
||||
qWarning() << "[LogArchiveSender] log file not found — nothing to collect:" << logPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir().mkpath(destDir);
|
||||
|
||||
// Явно заданный файл (напр. лог прошлой сессии) шлём целиком — обрезка по
|
||||
// последнему часу относительно «сейчас» здесь не нужна и только урезала бы
|
||||
// давно закрытую сессию.
|
||||
if (wholeFile)
|
||||
return QFile::copy(logPath, destDir + "/application.log");
|
||||
|
||||
bool any = false;
|
||||
QFile srcLog(logPath);
|
||||
if (srcLog.open(QIODevice::ReadOnly)) {
|
||||
@ -125,7 +135,8 @@ bool LogArchiveSender::collectInto(const QString &destDir) {
|
||||
return any;
|
||||
}
|
||||
|
||||
bool LogArchiveSender::dispatch(const QString &label, int eventId, const QString &extraCaptionHtml) {
|
||||
bool LogArchiveSender::dispatch(const QString &label, int eventId, const QString &extraCaptionHtml,
|
||||
const QString &sourceLogPath) {
|
||||
const QString apiToken = SettingsDAO::get("api_key");
|
||||
const QDateTime now = QDateTime::currentDateTime();
|
||||
const QString timestamp = now.toString(Qt::ISODate);
|
||||
@ -152,8 +163,12 @@ bool LogArchiveSender::dispatch(const QString &label, int eventId, const QString
|
||||
if (eventId > 0) parts << QStringLiteral("ev%1").arg(eventId);
|
||||
const QString archiveFilename = parts.join(QStringLiteral("_")) + QStringLiteral(".zip");
|
||||
|
||||
auto *worker = QThread::create([text, archiveFilename]() {
|
||||
const QByteArray archive = buildArchive();
|
||||
auto *worker = QThread::create([text, archiveFilename, sourceLogPath]() {
|
||||
const QByteArray archive = buildArchive(sourceLogPath);
|
||||
// Исходник прошлой сессии уже скопирован в архив — удаляем, чтобы файлы
|
||||
// не плодились между запусками (zip и tmp-каталог чистит buildArchive).
|
||||
if (!sourceLogPath.isEmpty())
|
||||
QFile::remove(sourceLogPath);
|
||||
if (archive.isEmpty()) {
|
||||
qWarning() << "[LogArchiveSender] empty archive, skipping dispatch";
|
||||
return;
|
||||
|
||||
@ -15,11 +15,18 @@ namespace LogArchiveSender {
|
||||
// api-токену различать операторов.
|
||||
// Возвращает true если задача поставлена в очередь; реальный результат отправки
|
||||
// не отслеживается (fire-and-forget, как у sendMonitoringLog).
|
||||
bool dispatch(const QString &label, int eventId = -1, const QString &extraCaptionHtml = {});
|
||||
// `sourceLogPath` (если не пусто) — путь к конкретному лог-файлу: он шлётся
|
||||
// целиком, без обрезки по последнему часу. Нужен для отправки лога прошлой
|
||||
// сессии (application.prev.log), сохранённого до усечения при старте.
|
||||
bool dispatch(const QString &label, int eventId = -1, const QString &extraCaptionHtml = {},
|
||||
const QString &sourceLogPath = {});
|
||||
|
||||
// Копирует application.log в destDir (создаёт каталог при необходимости).
|
||||
// Копирует лог в destDir (создаёт каталог при необходимости).
|
||||
// Возвращает true, если файл скопирован.
|
||||
// Если `sourceLogPath` пуст — берётся текущий application.log рядом с exe и
|
||||
// обрезается по последнему часу (поведение для дампов задач). Если задан —
|
||||
// указанный файл копируется целиком.
|
||||
// Используется TaskDumpRecorder, чтобы вложить логи прямо в архив дампа
|
||||
// задачи и слать всё одним файлом, а не двумя отдельными архивами.
|
||||
bool collectInto(const QString &destDir);
|
||||
bool collectInto(const QString &destDir, const QString &sourceLogPath = {});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user