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

View File

@ -804,7 +804,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
event.externalId, event.bankName, arr, event.id, /*updateStatus=*/false); event.externalId, event.bankName, arr, event.id, /*updateStatus=*/false);
// Логируем в arc-monitoring сразу по каждой транзакции — как на apiBase. // Логируем в arc-monitoring сразу по каждой транзакции — как на apiBase.
sendTaskSuccess("FETCH_TRANSACTIONS", event.externalId, event.bankName, 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); {}, event.id, event.accountId);
// Фиксируем в глобальном кэше: будущие задачи того же материала // Фиксируем в глобальном кэше: будущие задачи того же материала
// не будут повторно подтверждать эту транзакцию (TTL 20 минут). // не будут повторно подтверждать эту транзакцию (TTL 20 минут).

View File

@ -56,6 +56,9 @@ bool awaitReply(QNetworkReply *reply, int timeoutMs) {
constexpr int kMaxRequestAttempts = 3; constexpr int kMaxRequestAttempts = 3;
constexpr int kDefaultTimeoutMs = 30000; constexpr int kDefaultTimeoutMs = 30000;
constexpr int kDumpTimeoutMs = 120000; constexpr int kDumpTimeoutMs = 120000;
// Мониторинг — best-effort: одна попытка, короткий таймаут. Failure не должен
// блокировать потоки и накапливать QThread'ы при недоступном сервере.
constexpr int kMonitoringTimeoutMs = 10000;
} // namespace } // namespace
NetworkService::NetworkService(QObject *parent) : QObject(parent) { NetworkService::NetworkService(QObject *parent) : QObject(parent) {
@ -1655,11 +1658,6 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
const int internalTaskId = m_extraData.value("internal_task_id").toInt(); 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); auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "type", type.toUpper()); addFormField(multiPart, "type", type.toUpper());
addFormField(multiPart, "event", event); addFormField(multiPart, "event", event);
@ -1678,21 +1676,14 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
multiPart->append(imagePart); multiPart->append(imagePart);
} }
reply = m_manager->post(request, multiPart); QNetworkReply *reply = m_manager->post(request, multiPart);
multiPart->setParent(reply); multiPart->setParent(reply);
if (awaitReply(reply, kDefaultTimeoutMs)) { timedOut = false; break; } if (!awaitReply(reply, kMonitoringTimeoutMs)) {
qWarning() << "[NetworkService] postMonitoringLog timeout attempt" qWarning() << "[NetworkService] postMonitoringLog timeout";
<< attempt << "/" << kMaxRequestAttempts;
reply->abort(); reply->abort();
}
if (timedOut) {
qWarning() << "[NetworkService] postMonitoringLog timeout after"
<< kMaxRequestAttempts << "attempts";
} else if (reply->error() != QNetworkReply::NoError) { } else if (reply->error() != QNetworkReply::NoError) {
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString() qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString();
<< "\n response body:" << reply->readAll();
} }
reply->deleteLater(); reply->deleteLater();
} }
@ -1778,11 +1769,6 @@ void NetworkService::postMonitoringScreenshot(const QString &desktopId,
request.setRawHeader("X-Monitoring-Token", m_monitoringToken.toUtf8()); 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); auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "desktop_id", desktopId); addFormField(multiPart, "desktop_id", desktopId);
addFormField(multiPart, "device_id", deviceId); addFormField(multiPart, "device_id", deviceId);
@ -1796,21 +1782,14 @@ void NetworkService::postMonitoringScreenshot(const QString &desktopId,
imagePart.setBody(jpeg); imagePart.setBody(jpeg);
multiPart->append(imagePart); multiPart->append(imagePart);
reply = m_manager->post(request, multiPart); QNetworkReply *reply = m_manager->post(request, multiPart);
multiPart->setParent(reply); multiPart->setParent(reply);
if (awaitReply(reply, kDefaultTimeoutMs)) { timedOut = false; break; } if (!awaitReply(reply, kMonitoringTimeoutMs)) {
qWarning() << "[NetworkService] postMonitoringScreenshot timeout attempt" qWarning() << "[NetworkService] postMonitoringScreenshot timeout";
<< attempt << "/" << kMaxRequestAttempts;
reply->abort(); reply->abort();
}
if (timedOut) {
qWarning() << "[NetworkService] postMonitoringScreenshot timeout after"
<< kMaxRequestAttempts << "attempts";
} else if (reply->error() != QNetworkReply::NoError) { } else if (reply->error() != QNetworkReply::NoError) {
qWarning() << "[NetworkService] postMonitoringScreenshot failed:" << reply->errorString() qWarning() << "[NetworkService] postMonitoringScreenshot failed:" << reply->errorString();
<< "\n response body:" << reply->readAll();
} }
reply->deleteLater(); reply->deleteLater();
} }

View File

@ -522,6 +522,17 @@ def monitoring_log():
return ok({}) 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>") @app.get("/_admin/screenshot/<image_id>")
def get_screenshot(image_id): def get_screenshot(image_id):
path = _SCREENSHOTS_DIR / f"{image_id}.png" path = _SCREENSHOTS_DIR / f"{image_id}.png"