fix(client): 自动连接回调改用强类型 ref.read + 日志落文件(1.0.21)
- 自动连接回调不再用 listenManual 的 next(裸订阅会退化成 dynamic → valueOrNull 扩展崩), 改 ref.read(nodesProvider) 拿强类型 AsyncValue,彻底躲开 dynamic 扩展坑。 - logLine 除 stdout 外落桌面端固定文件(%LOCALAPPDATA%\Pangolin\pangolin.log,每次启动截断), GUI/管理员启动也能拿日志,免 Tee。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,14 +2,61 @@
|
||||
// 格式: `[时:分:秒.毫秒] [tag] msg`,全部走 logLine(),便于排查时序。
|
||||
// 注:sing-box 自身时间戳已在下发配置里关闭(log.timestamp=false),
|
||||
// 避免与这里的前缀重复打印两个时间。
|
||||
//
|
||||
// 除 stdout 外,桌面端还把日志落到固定文件(每次启动截断重写),便于在 GUI/管理员
|
||||
// 启动、无法用控制台 Tee 抓 stdout 时排障:
|
||||
// Windows: %LOCALAPPDATA%\Pangolin\pangolin.log
|
||||
// macOS/Linux: ~/.pangolin/pangolin.log
|
||||
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
String _ts() {
|
||||
final n = DateTime.now();
|
||||
String p(int v, [int w = 2]) => v.toString().padLeft(w, '0');
|
||||
return '${p(n.hour)}:${p(n.minute)}:${p(n.second)}.${p(n.millisecond, 3)}';
|
||||
}
|
||||
|
||||
/// 打印一行带时间戳与标签的桌面端日志。
|
||||
void logLine(String tag, String msg) => print('[${_ts()}] [$tag] $msg');
|
||||
File? _logFile;
|
||||
bool _logInit = false;
|
||||
|
||||
File? _openLogFile() {
|
||||
try {
|
||||
String? base;
|
||||
String sep = '/';
|
||||
String sub = '.pangolin';
|
||||
if (Platform.isWindows) {
|
||||
base = Platform.environment['LOCALAPPDATA'];
|
||||
sep = '\\';
|
||||
sub = 'Pangolin';
|
||||
} else if (Platform.isMacOS || Platform.isLinux) {
|
||||
base = Platform.environment['HOME'];
|
||||
} else {
|
||||
return null; // 移动端不落文件
|
||||
}
|
||||
if (base == null || base.isEmpty) return null;
|
||||
final dir = Directory('$base$sep$sub');
|
||||
if (!dir.existsSync()) dir.createSync(recursive: true);
|
||||
final f = File('${dir.path}${sep}pangolin.log');
|
||||
f.writeAsStringSync('=== session ${DateTime.now()} ===\n'); // 每次启动截断重写
|
||||
return f;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 打印一行带时间戳与标签的桌面端日志(stdout + 桌面端日志文件)。
|
||||
void logLine(String tag, String msg) {
|
||||
final line = '[${_ts()}] [$tag] $msg';
|
||||
print(line);
|
||||
try {
|
||||
if (!_logInit) {
|
||||
_logInit = true;
|
||||
_logFile = _openLogFile();
|
||||
}
|
||||
_logFile?.writeAsStringSync('$line\n', mode: FileMode.append);
|
||||
} catch (_) {
|
||||
// 落盘失败不影响运行。
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,13 +129,14 @@ class _RootFlowState extends ConsumerState<RootFlow> {
|
||||
// 注意:不在此处早读 settingsProvider.autoConnect —— 其 _load 是异步(SharedPreferences),
|
||||
// 启动这一刻多半还没加载完,会读到默认 false 而永久跳过。改到「节点就绪」回调里再判:
|
||||
// 节点要走网络拉取,远慢于本地设置读取,那时 autoConnect 必已加载到位。
|
||||
// 必须标全泛型:裸 ProviderSubscription(=<dynamic>)会让 listenManual 推断成 dynamic,
|
||||
// next 变 dynamic → valueOrNull(扩展 getter)在 dynamic 上不解析 → 运行期 NoSuchMethod。
|
||||
// 不用回调的 next(裸 ProviderSubscription 会让它退化成 dynamic,valueOrNull 这种
|
||||
// 扩展 getter 在 dynamic 上不解析 → 运行期 NoSuchMethod)。直接 ref.read 拿强类型值。
|
||||
late final ProviderSubscription<AsyncValue<List<Node>>> sub;
|
||||
sub = ref.listenManual(nodesProvider, (prev, next) {
|
||||
final nodes = next.valueOrNull ?? const <Node>[];
|
||||
final av = ref.read(nodesProvider);
|
||||
final nodes = av.valueOrNull ?? const <Node>[];
|
||||
final ready = nodes.any((n) => n.uuid.isNotEmpty);
|
||||
logLine('AutoConnect', 'nodes update: total=${nodes.length} ready=$ready loading=${next.isLoading} err=${next.hasError}');
|
||||
logLine('AutoConnect', 'nodes update: total=${nodes.length} ready=$ready loading=${av.isLoading} err=${av.hasError}');
|
||||
if (!ready) return;
|
||||
sub.close();
|
||||
final ac = ref.read(settingsProvider).autoConnect;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: pangolin_vpn
|
||||
description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。
|
||||
publish_to: "none"
|
||||
version: 1.0.20+21
|
||||
version: 1.0.21+22
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在
|
||||
; ..\..\build\windows\x64\runner\Release
|
||||
#define MyAppName "穿山甲 Pangolin"
|
||||
#define MyAppVersion "1.0.20"
|
||||
#define MyAppVersion "1.0.21"
|
||||
#define MyAppPublisher "Pangolin"
|
||||
#define MyAppExeName "pangolin_vpn.exe"
|
||||
#define BuildDir "..\..\build\windows\x64\runner\Release"
|
||||
|
||||
Reference in New Issue
Block a user