refactor(logging): remove remote logging subsystem

This commit is contained in:
chenjintao
2026-10-08 16:08:41 +08:00
parent 5d849033c3
commit 3160a33f50
22 changed files with 12 additions and 2546 deletions
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/env python3
"""Minimal HTTP receiver for TQ remote log batches.
Usage:
python tools/remote_log_receiver.py --host 0.0.0.0 --port 8080 --dir ./remote_logs
Configure the device endpoint as:
http://<server-ip>:8080/log/batch
For production, put this behind HTTPS or a reverse proxy that terminates TLS.
"""
from __future__ import annotations
import argparse
import datetime as dt
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
class LogReceiver(BaseHTTPRequestHandler):
server_version = "TQRemoteLogReceiver/1.0"
def do_POST(self) -> None:
if self.path != "/log/batch":
self._send_json(404, {"code": 404, "message": "not found"})
return
length = int(self.headers.get("Content-Length", "0"))
if length <= 0 or length > self.server.max_body_bytes:
self._send_json(413, {"code": 413, "message": "payload too large"})
return
raw = self.rfile.read(length)
try:
payload = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
self._send_json(400, {"code": 400, "message": f"invalid json: {exc}"})
return
now = dt.datetime.now(dt.timezone.utc)
day_dir = self.server.output_dir / now.strftime("%Y%m%d")
day_dir.mkdir(parents=True, exist_ok=True)
device_id = str(payload.get("device_id") or "unknown").replace("/", "_")
batch_id = str(payload.get("batch_id") or now.strftime("%H%M%S%f")).replace("/", "_")
json_path = day_dir / f"{device_id}_{batch_id}.json"
jsonl_path = day_dir / f"{device_id}.jsonl"
with json_path.open("w", encoding="utf-8") as file:
json.dump(payload, file, ensure_ascii=False, indent=2)
file.write("\n")
entries = payload.get("entries") or []
with jsonl_path.open("a", encoding="utf-8") as file:
for entry in entries:
row = {
"received_at": now.isoformat(),
"device_id": payload.get("device_id"),
"serial_number": payload.get("serial_number"),
"batch_id": payload.get("batch_id"),
**entry,
}
file.write(json.dumps(row, ensure_ascii=False, separators=(",", ":")))
file.write("\n")
self._send_json(
200,
{
"code": 200,
"message": "OK",
"data": {
"accepted": len(entries),
"stored": str(json_path),
},
},
)
def log_message(self, fmt: str, *args: object) -> None:
print(f"{self.address_string()} - {fmt % args}")
def _send_json(self, status: int, payload: dict) -> None:
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
class LogServer(ThreadingHTTPServer):
def __init__(self, server_address: tuple[str, int], handler_class, output_dir: Path, max_body_bytes: int):
super().__init__(server_address, handler_class)
self.output_dir = output_dir
self.max_body_bytes = max_body_bytes
def main() -> None:
parser = argparse.ArgumentParser(description="Receive TQ device remote log batches.")
parser.add_argument("--host", default="0.0.0.0")
parser.add_argument("--port", type=int, default=8080)
parser.add_argument("--dir", type=Path, default=Path("remote_logs"))
parser.add_argument("--max-body-bytes", type=int, default=256 * 1024)
args = parser.parse_args()
args.dir.mkdir(parents=True, exist_ok=True)
server = LogServer((args.host, args.port), LogReceiver, args.dir, args.max_body_bytes)
print(f"listening on http://{args.host}:{args.port}/log/batch")
print(f"writing logs under {args.dir.resolve()}")
server.serve_forever()
if __name__ == "__main__":
main()
-198
View File
@@ -1,198 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
LOGFS_OFFSET="0x210000"
LOGFS_SIZE="0x40000"
LOGFS_BLOCK_SIZE="4096"
pause_before_exit() {
if [[ -t 0 ]]; then
printf '\n按回车键关闭窗口...'
read -r _
fi
}
fail() {
printf '\n错误:%s\n' "$1" >&2
pause_before_exit
exit 1
}
find_serial_port() {
local requested_port="${1:-}"
if [[ -n "$requested_port" ]]; then
[[ -e "$requested_port" ]] || fail "串口不存在:$requested_port"
printf '%s' "$requested_port"
return
fi
local ports=()
local candidate
for candidate in /dev/cu.usbmodem* /dev/cu.usbserial* \
/dev/cu.SLAB_USBtoUART* /dev/cu.wchusbserial*; do
[[ -e "$candidate" ]] && ports+=("$candidate")
done
if [[ ${#ports[@]} -eq 0 ]]; then
fail "没有检测到设备串口。请连接设备后重试。"
fi
if [[ ${#ports[@]} -eq 1 ]]; then
printf '%s' "${ports[0]}"
return
fi
printf '检测到多个串口:\n' >&2
local index
for ((index = 0; index < ${#ports[@]}; index++)); do
printf ' %d) %s\n' "$((index + 1))" "${ports[index]}" >&2
done
printf '请选择序号:' >&2
local selection
read -r selection
[[ "$selection" =~ ^[0-9]+$ ]] || fail "输入的序号无效。"
((selection >= 1 && selection <= ${#ports[@]})) || fail "输入的序号超出范围。"
printf '%s' "${ports[selection - 1]}"
}
find_build_tool() {
local relative_path="$1"
local candidate="$PROJECT_DIR/$relative_path"
if [[ -x "$candidate" ]]; then
printf '%s' "$candidate"
return
fi
return 1
}
find_idf_value() {
local key="$1"
local cache="$PROJECT_DIR/build/CMakeCache.txt"
[[ -f "$cache" ]] || return 1
sed -n "s|^${key}:[^=]*=||p" "$cache" | head -n 1
}
find_idf_python() {
local candidate
candidate="$(find_idf_value PYTHON || true)"
if [[ -n "$candidate" && -x "$candidate" ]]; then
printf '%s' "$candidate"
return
fi
for candidate in "$HOME"/.espressif/tools/python/*/venv/bin/python; do
if [[ -x "$candidate" ]]; then
printf '%s' "$candidate"
return
fi
done
return 1
}
find_esptool() {
local idf_path
idf_path="$(find_idf_value esp-idf_SOURCE_DIR || true)"
local candidate="$idf_path/components/esptool_py/esptool/esptool.py"
if [[ -n "$idf_path" && -f "$candidate" ]]; then
printf '%s' "$candidate"
return
fi
for candidate in "$HOME"/.espressif/v*/esp-idf/components/esptool_py/esptool/esptool.py; do
if [[ -f "$candidate" ]]; then
printf '%s' "$candidate"
return
fi
done
return 1
}
PORT="$(find_serial_port "${1:-}")"
IDF_PYTHON="$(find_idf_python || true)"
ESPTOOL="$(find_esptool || true)"
LITTLEFS_TOOL="$(find_build_tool build/littlefs_py_venv/bin/littlefs-python || true)"
[[ -n "$IDF_PYTHON" ]] || fail "找不到 ESP-IDF Python 环境,请先成功构建一次工程。"
[[ -n "$ESPTOOL" ]] || fail "找不到 esptool.py,请检查 ESP-IDF 是否已安装。"
[[ -n "$LITTLEFS_TOOL" ]] || fail "找不到 LittleFS 解包工具,请先成功构建一次工程。"
TIMESTAMP="$(date '+%Y%m%d-%H%M%S')"
OUTPUT_DIR="$PROJECT_DIR/flash_log_dump/$TIMESTAMP"
IMAGE_PATH="$OUTPUT_DIR/logfs.bin"
EXTRACT_DIR="$OUTPUT_DIR/extracted"
REPORT_PATH="$OUTPUT_DIR/remote_logs.txt"
mkdir -p "$EXTRACT_DIR"
printf '设备串口:%s\n' "$PORT"
printf '输出目录:%s\n' "$OUTPUT_DIR"
printf '\n注意:请先关闭正在使用该串口的 monitor。\n'
printf '读取 Flash 会让设备进入下载模式,完成后设备会自动重启。\n'
printf '正在读取 logfs 分区,请勿拔线...\n\n'
if ! "$IDF_PYTHON" "$ESPTOOL" \
--chip esp32s3 \
--port "$PORT" \
--baud 460800 \
--before default_reset \
--after hard_reset \
read_flash "$LOGFS_OFFSET" "$LOGFS_SIZE" "$IMAGE_PATH"; then
fail "读取 Flash 失败。请关闭 monitor、检查 USB 连接后重试。"
fi
printf '\n正在解包 LittleFS...\n'
if ! "$LITTLEFS_TOOL" extract \
--block-size "$LOGFS_BLOCK_SIZE" \
"$IMAGE_PATH" "$EXTRACT_DIR"; then
fail "LittleFS 解包失败,请确认设备使用的是当前 16m.csv 分区表。"
fi
if ! "$IDF_PYTHON" - "$EXTRACT_DIR" "$REPORT_PATH" <<'PY'
import json
import sys
from pathlib import Path
extract_dir = Path(sys.argv[1])
report_path = Path(sys.argv[2])
log_dir = extract_dir / "remote_logs"
log_files = sorted(log_dir.glob("log_*.jsonl")) if log_dir.is_dir() else []
with report_path.open("w", encoding="utf-8") as report:
report.write("TalkingQ cached remote logs\n")
report.write(f"Source: {log_dir}\n")
report.write(f"Files: {len(log_files)}\n\n")
if not log_files:
report.write("No pending remote log files were found.\n")
for log_file in log_files:
report.write(f"===== {log_file.name} =====\n")
with log_file.open("r", encoding="utf-8", errors="replace") as source:
for raw_line in source:
line = raw_line.strip()
if not line:
continue
try:
entry = json.loads(line)
timestamp = entry.get("ts_ms", "?")
level = entry.get("level", "?")
truncated = " [truncated]" if entry.get("truncated") else ""
message = entry.get("message", "")
report.write(f"[{timestamp:>10} ms] {level}{truncated} {message}\n")
except (json.JSONDecodeError, TypeError, ValueError):
report.write(f"[invalid JSON] {line}\n")
report.write("\n")
PY
then
fail "生成可读日志文件失败。"
fi
printf '\n读取完成:%s\n' "$REPORT_PATH"
if command -v open >/dev/null 2>&1; then
open "$REPORT_PATH"
else
printf '当前系统无法自动打开文件,请手动查看上述路径。\n'
fi
pause_before_exit