Files
desktop-plugin/lib/agent.dart
T
dzc 726210edcb feat: 本地日志文件系统 + 修复双击打不开/向导完成无反应
用户实测两个问题(先装好驱动的机器):
1. 双击 App 图标不一定能打开——根因 single_instance:唤醒端口 38210 被
   残留占用(上次异常退出/TIME_WAIT)时,connect 无应答仍返回 false →
   main 直接 exit(0)。修复:端口被占但无应答判定为残留占用,照常启动
   (仅唤醒功能降级,FileLog 留痕);有应答才作为第二实例退出。
2. 向导第三步点"完成"没反应——根因 _finish 只有 finally 没有 catch:
   Autostart/UrlScheme(reg 三连)/FirstRun 任一抛异常被静默吞,Navigator.pop
   永不执行=对话框锁死。修复:每步独立 try-catch + 分步日志,成败都关闭
   向导,失败汇总 SnackBar 提示"详见运行日志"。

本地日志文件系统(用户要求:仔细记录、可发回分析、定时删除):
- lib/utils/file_log.dart:按天滚动 app-YYYYMMDD.log、单文件 5MB 轮转加
  序号、启动清理 7 天前旧文件(按文件名日期判定);全同步追加写(低频
  日志,保证异常退出前落盘);目录 mac ~/Library/Logs/zax-desktop-plugin/
  win %APPDATA%\zax-desktop-plugin\logs\(不引第三方依赖)
- AgentLog.write 挂文件输出(一处改动覆盖全部 22 个现有调用点);
  main.dart FileLog.init 提到最前,bootWarning/startError 全部落日志,
  启动横幅记录平台/exe 路径/参数
- pavo isolate 的 [pavo-diag] 诊断日志传日志路径(纯数据,isolate 安全)
  直接写文件——GUI 模式下 isolate stdout 本来就丢
- 插件页技术详情加"打开日志文件夹"按钮(Explorer/Finder 直达,用户打包
  app-*.log 发回分析)

19 个测试全过、analyze 零问题。
2026-09-22 15:09:24 +08:00

95 lines
2.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'print/backend.dart';
import 'print/mock_backend.dart';
import 'server/http_server.dart';
import 'server/print_queue.dart';
import 'utils/file_log.dart';
/// 启动参数:--mock 模拟打印;--port=NNN / --port NNN 指定监听端口(默认 38200-38202 顺延)。
class AgentOptions {
AgentOptions({this.mock = false, this.port});
final bool mock;
final int? port;
factory AgentOptions.parse(List<String> args) {
var mock = false;
int? port;
for (var i = 0; i < args.length; i++) {
final arg = args[i];
if (arg == '--mock') {
mock = true;
} else if (arg.startsWith('--port=')) {
port = int.tryParse(arg.substring('--port='.length));
} else if ((arg == '--port' || arg == '-p') && i + 1 < args.length) {
port = int.tryParse(args[++i]);
}
}
return AgentOptions(mock: mock, port: port);
}
}
/// 运行日志环形缓冲(GUI 展示用),同时打到 stdout。
class AgentLog {
final lines = <String>[];
final _controller = StreamController<String>.broadcast();
Stream<String> get stream => _controller.stream;
void write(String line) {
final stamped = '${DateTime.now().toIso8601String().substring(11, 19)} $line';
lines.add(stamped);
if (lines.length > 500) {
lines.removeRange(0, lines.length - 500);
}
// ignore: avoid_print
print(stamped);
FileLog.write(line); // 文件持久化(供现场排查,用户可取回分析)
_controller.add(stamped);
}
void dispose() => _controller.close();
}
/// 助手核心:组合打印后端、任务队列与 HTTP 服务(Flutter 窗口与 headless 入口共用)。
class Agent {
Agent(this.options) : log = AgentLog();
final AgentOptions options;
final AgentLog log;
late final PrintQueue queue;
late final PrintBackend backend;
late final AgentHttpServer server;
int? port;
Future<int> start() async {
final backendName = osBackendName();
backend = options.mock ? MockBackend(onLog: log.write) : createOsBackend(onLog: log.write);
queue = PrintQueue(backend, onLog: log.write);
server = AgentHttpServer(
queue: queue,
backend: backend,
mock: options.mock,
manifestBackendName: backendName,
);
final ports = options.port == null ? const [38200, 38201, 38202] : <int>[options.port!];
port = await server.start(candidatePorts: ports);
log.write('资安信插件助手已启动 http://127.0.0.1:$port'
'(后端=$backendName,mock=${options.mock},agent=desktop-plugin)');
return port!;
}
bool _stopped = false;
/// 幂等停止:托盘退出与窗口销毁可能先后各调一次。
Future<void> stop() async {
if (_stopped) return;
_stopped = true;
await server.stop();
queue.dispose();
log.dispose();
}
}