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