Handle edge cases in log processing, enhance JSON rendering in monitoring logs, and improve logging in AdbUtils.
This commit is contained in:
parent
a0583c4966
commit
521101463b
@ -243,13 +243,20 @@ bool AdbUtils::tryToStartApplication(const QString &deviceId, const QString &pac
|
||||
logMultiline("[AdbUtils::tryToStartApplication] monkey stdout:", stdOut);
|
||||
logMultiline("[AdbUtils::tryToStartApplication] monkey stderr:", stdErr);
|
||||
|
||||
if (stdOut.contains("Events injected: 1")) {
|
||||
const bool eventsInjected = stdOut.contains("Events injected: 1");
|
||||
const bool hadNativeCrash = stdOut.contains("NativeCrash");
|
||||
|
||||
if (eventsInjected && !hadNativeCrash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Фолбэк: am start с конкретной activity. У него осмысленный stderr и exit code,
|
||||
// в отличие от monkey, который при пустом resolve просто молча печатает "No activities found".
|
||||
// Также используется когда monkey дал "Events injected: 1" но приложение упало (NativeCrash).
|
||||
if (!launcherActivity.isEmpty()) {
|
||||
if (hadNativeCrash)
|
||||
qWarning().noquote() << "[AdbUtils::tryToStartApplication] NativeCrash detected, retrying via am start -n" << launcherActivity;
|
||||
else
|
||||
qDebug().noquote() << "[AdbUtils::tryToStartApplication] monkey не дал Events injected;"
|
||||
<< "пробую am start -n" << launcherActivity;
|
||||
QProcess am;
|
||||
|
||||
@ -19,6 +19,7 @@ import asyncio
|
||||
import hashlib
|
||||
import hmac
|
||||
import html as html_lib
|
||||
import json as json_lib
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@ -666,22 +667,45 @@ _ISO_RE = re.compile(r"^\d{4}-\d{2}-\d{2}T")
|
||||
_META_PREFIX_RE = re.compile(r"^(Device|Material|Amount|Phone|Card):", re.IGNORECASE)
|
||||
|
||||
|
||||
def _pretty_json(raw: str) -> str:
|
||||
"""Pretty-print JSON string; return original if not valid JSON."""
|
||||
try:
|
||||
obj = json_lib.loads(html_lib.unescape(raw.strip()))
|
||||
return html_lib.escape(json_lib.dumps(obj, ensure_ascii=False, indent=2))
|
||||
except (json_lib.JSONDecodeError, ValueError):
|
||||
return raw
|
||||
|
||||
|
||||
def _render_msg(message: str) -> str:
|
||||
"""Клиент шлёт логи ошибок уже обёрнутыми в структурные теги <b>…</b>,
|
||||
динамику внутри экранирует на стороне C++ (toHtmlEscaped). Прочие источники
|
||||
(AppLogger, task_success) шлют голый текст. Экранируем всё, затем возвращаем
|
||||
из белого списка только <b>/</b> — bold рендерится, инъекция HTML невозможна.
|
||||
Тело JSON (всё после маркера «JSON: » до конца сообщения) выносим в
|
||||
моноширинный <pre>-блок. Саму подпись «JSON:» не показываем — и так понятно.
|
||||
Содержимое уже экранировано, инъекции нет."""
|
||||
Тело JSON (всё после маркера «JSON: » до конца сообщения, либо bare-JSON на
|
||||
последней строке) выносим в моноширинный <pre>-блок с pretty-print.
|
||||
Содержимое уже экранировано, инъекций нет."""
|
||||
s = html_lib.escape(message or "")
|
||||
s = s.replace("<b>", "<b>").replace("</b>", "</b>")
|
||||
|
||||
json_body = None
|
||||
head = s
|
||||
|
||||
marker = "\nJSON: "
|
||||
idx = s.find(marker)
|
||||
if idx != -1:
|
||||
head = s[:idx]
|
||||
body = s[idx + len(marker):]
|
||||
s = f'{head}\n<pre class=json>{body}</pre>'
|
||||
json_body = s[idx + len(marker):]
|
||||
else:
|
||||
# Bare JSON block at end of message (no "JSON: " prefix)
|
||||
nl = s.rfind("\n")
|
||||
if nl != -1:
|
||||
last = s[nl + 1:].lstrip()
|
||||
if last.startswith("{") or last.startswith("["):
|
||||
head = s[:nl]
|
||||
json_body = s[nl + 1:]
|
||||
|
||||
if json_body is not None:
|
||||
s = f'{head}\n<pre class=json>{_pretty_json(json_body)}</pre>'
|
||||
return s
|
||||
|
||||
|
||||
|
||||
@ -84,8 +84,40 @@ bool LogArchiveSender::collectInto(const QString &destDir) {
|
||||
QDir().mkpath(destDir);
|
||||
bool any = false;
|
||||
if (hasLog) {
|
||||
QFile srcLog(logPath);
|
||||
if (srcLog.open(QIODevice::ReadOnly)) {
|
||||
const QDateTime cutoff = QDateTime::currentDateTime().addSecs(-3600);
|
||||
|
||||
// Binary search for the first line with ts >= cutoff.
|
||||
// Relies on monotonically non-decreasing timestamps in the log.
|
||||
qint64 lo = 0, hi = srcLog.size();
|
||||
while (lo < hi) {
|
||||
const qint64 mid = lo + (hi - lo) / 2;
|
||||
srcLog.seek(mid);
|
||||
if (mid > 0) srcLog.readLine(); // align to next line boundary
|
||||
const qint64 linePos = srcLog.pos();
|
||||
if (linePos >= hi) { hi = mid; continue; }
|
||||
const QByteArray probe = srcLog.readLine();
|
||||
if (probe.isEmpty()) { hi = mid; continue; }
|
||||
const QDateTime ts = QDateTime::fromString(
|
||||
QString::fromLatin1(probe.left(23)), "yyyy-MM-dd hh:mm:ss.zzz");
|
||||
if (!ts.isValid() || ts < cutoff)
|
||||
lo = srcLog.pos(); // this line is old, search upper half
|
||||
else
|
||||
hi = linePos; // this line is new, search lower half
|
||||
}
|
||||
srcLog.seek(lo);
|
||||
|
||||
QFile dstLog(destDir + "/application.log");
|
||||
if (dstLog.open(QIODevice::WriteOnly)) {
|
||||
while (!srcLog.atEnd())
|
||||
dstLog.write(srcLog.readLine());
|
||||
any = true;
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user