1
0
forked from BRT/arc

Refactor monitoring logs handling: use concise JSON formatting for transaction logs, add /monitoring_logs admin endpoint for testing, increase screen monitoring interval to 120s, and simplify network retry logic with shorter monitoring timeouts.

This commit is contained in:
slava 2026-06-04 13:01:47 +05:00
parent 2b4bd5a9d1
commit c2994d9041
4 changed files with 56 additions and 66 deletions

View File

@ -16,7 +16,7 @@ public:
static constexpr qint64 kOfflineDebounceSecs = 30;
// Как часто проверять/слать кадр истории экрана на сервер логов (с дедупом).
static constexpr qint64 kMonitoringScreenIntervalSecs = 30;
static constexpr qint64 kMonitoringScreenIntervalSecs = 120;
// Таймаут одной adb-команды в scanDeviceIo. На порядок выше нормального
// времени (<1с), но ограничивает зависшую команду на флапающем устройстве,
// чтобы один телефон не держал весь цикл опроса.

View File

@ -804,7 +804,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
event.externalId, event.bankName, arr, event.id, /*updateStatus=*/false);
// Логируем в arc-monitoring сразу по каждой транзакции — как на apiBase.
sendTaskSuccess("FETCH_TRANSACTIONS", event.externalId, event.bankName,
QString::fromUtf8(QJsonDocument(tx).toJson(QJsonDocument::Compact)),
QStringLiteral("JSON: %1").arg(QString::fromUtf8(QJsonDocument(tx).toJson(QJsonDocument::Indented))),
{}, event.id, event.accountId);
// Фиксируем в глобальном кэше: будущие задачи того же материала
// не будут повторно подтверждать эту транзакцию (TTL 20 минут).

View File

@ -56,6 +56,9 @@ bool awaitReply(QNetworkReply *reply, int timeoutMs) {
constexpr int kMaxRequestAttempts = 3;
constexpr int kDefaultTimeoutMs = 30000;
constexpr int kDumpTimeoutMs = 120000;
// Мониторинг — best-effort: одна попытка, короткий таймаут. Failure не должен
// блокировать потоки и накапливать QThread'ы при недоступном сервере.
constexpr int kMonitoringTimeoutMs = 10000;
} // namespace
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
@ -1655,44 +1658,32 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
const int internalTaskId = m_extraData.value("internal_task_id").toInt();
QNetworkReply *reply = nullptr;
bool timedOut = true;
for (int attempt = 1; attempt <= kMaxRequestAttempts; ++attempt) {
if (reply) { reply->deleteLater(); reply = nullptr; }
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "type", type.toUpper());
addFormField(multiPart, "event", event);
addFormField(multiPart, "message", message);
if (internalTaskId > 0) {
addFormField(multiPart, "internal_task_id", QString::number(internalTaskId));
}
if (!image.isEmpty()) {
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
imagePart.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"image\"; filename=\"screenshot.png\"")
);
imagePart.setBody(image);
multiPart->append(imagePart);
}
reply = m_manager->post(request, multiPart);
multiPart->setParent(reply);
if (awaitReply(reply, kDefaultTimeoutMs)) { timedOut = false; break; }
qWarning() << "[NetworkService] postMonitoringLog timeout attempt"
<< attempt << "/" << kMaxRequestAttempts;
reply->abort();
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "type", type.toUpper());
addFormField(multiPart, "event", event);
addFormField(multiPart, "message", message);
if (internalTaskId > 0) {
addFormField(multiPart, "internal_task_id", QString::number(internalTaskId));
}
if (!image.isEmpty()) {
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
imagePart.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"image\"; filename=\"screenshot.png\"")
);
imagePart.setBody(image);
multiPart->append(imagePart);
}
if (timedOut) {
qWarning() << "[NetworkService] postMonitoringLog timeout after"
<< kMaxRequestAttempts << "attempts";
QNetworkReply *reply = m_manager->post(request, multiPart);
multiPart->setParent(reply);
if (!awaitReply(reply, kMonitoringTimeoutMs)) {
qWarning() << "[NetworkService] postMonitoringLog timeout";
reply->abort();
} else if (reply->error() != QNetworkReply::NoError) {
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString()
<< "\n response body:" << reply->readAll();
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString();
}
reply->deleteLater();
}
@ -1778,39 +1769,27 @@ void NetworkService::postMonitoringScreenshot(const QString &desktopId,
request.setRawHeader("X-Monitoring-Token", m_monitoringToken.toUtf8());
}
QNetworkReply *reply = nullptr;
bool timedOut = true;
for (int attempt = 1; attempt <= kMaxRequestAttempts; ++attempt) {
if (reply) { reply->deleteLater(); reply = nullptr; }
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "desktop_id", desktopId);
addFormField(multiPart, "device_id", deviceId);
addFormField(multiPart, "name", name);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg");
imagePart.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"image\"; filename=\"screen.jpg\"")
);
imagePart.setBody(jpeg);
multiPart->append(imagePart);
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "desktop_id", desktopId);
addFormField(multiPart, "device_id", deviceId);
addFormField(multiPart, "name", name);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg");
imagePart.setHeader(
QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"image\"; filename=\"screen.jpg\"")
);
imagePart.setBody(jpeg);
multiPart->append(imagePart);
QNetworkReply *reply = m_manager->post(request, multiPart);
multiPart->setParent(reply);
reply = m_manager->post(request, multiPart);
multiPart->setParent(reply);
if (awaitReply(reply, kDefaultTimeoutMs)) { timedOut = false; break; }
qWarning() << "[NetworkService] postMonitoringScreenshot timeout attempt"
<< attempt << "/" << kMaxRequestAttempts;
if (!awaitReply(reply, kMonitoringTimeoutMs)) {
qWarning() << "[NetworkService] postMonitoringScreenshot timeout";
reply->abort();
}
if (timedOut) {
qWarning() << "[NetworkService] postMonitoringScreenshot timeout after"
<< kMaxRequestAttempts << "attempts";
} else if (reply->error() != QNetworkReply::NoError) {
qWarning() << "[NetworkService] postMonitoringScreenshot failed:" << reply->errorString()
<< "\n response body:" << reply->readAll();
qWarning() << "[NetworkService] postMonitoringScreenshot failed:" << reply->errorString();
}
reply->deleteLater();
}

View File

@ -522,6 +522,17 @@ def monitoring_log():
return ok({})
@app.get("/_admin/monitoring_logs")
def admin_monitoring_logs():
"""Return collected monitoring logs for testing."""
event_filter = request.args.get("event")
with _lock:
logs = list(_monitoring_logs)
if event_filter:
logs = [l for l in logs if l["event"] == event_filter]
return jsonify(logs)
@app.get("/_admin/screenshot/<image_id>")
def get_screenshot(image_id):
path = _SCREENSHOTS_DIR / f"{image_id}.png"