refactor(client): 抽统一日志函数 logLine + 去掉重复时间戳
- log_time.dart → log.dart:提供 logLine(tag, msg),格式 `[时:分:秒.毫秒] [tag] msg`, DesktopKernel / DesktopVpnBridge 散落的 print+拼接全部收敛到此。 - 下发配置 log.timestamp=false:sing-box 不再自带时间戳,避免一行打印两个时间 (之前 `[22:10:35.213] [stderr] +0800 2026-06-18 22:10:35 ...` 重复)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'kernel_process.dart';
|
||||
import 'log_time.dart';
|
||||
import 'log.dart';
|
||||
import 'vpn_bridge.dart';
|
||||
|
||||
/// 桌面端 VPN 桥接实现(macOS / Linux / Windows PoC)。
|
||||
@@ -101,9 +101,9 @@ class DesktopVpnBridge implements VpnBridge {
|
||||
|
||||
// 3. 写 config 到应用支持目录(0600 权限)
|
||||
final configPath = await writeConfig(enrichedJson);
|
||||
print('${logTime()}[DesktopVpnBridge] config written: $configPath');
|
||||
print('${logTime()}[DesktopVpnBridge] clash_api port=$port secret_len=${secret.length}');
|
||||
print('${logTime()}[DesktopVpnBridge] killSwitch=$_killSwitchEnabled');
|
||||
logLine('DesktopVpnBridge', 'config written: $configPath');
|
||||
logLine('DesktopVpnBridge', 'clash_api port=$port secret_len=${secret.length}');
|
||||
logLine('DesktopVpnBridge', 'killSwitch=$_killSwitchEnabled');
|
||||
|
||||
// 4. 启动内核子进程(blocking until Clash API ready or error)
|
||||
await _kernel.spawn(configPath);
|
||||
@@ -142,7 +142,7 @@ class DesktopVpnBridge implements VpnBridge {
|
||||
try {
|
||||
await _kernel.clashApiClient.selectProxy('proxy', tag);
|
||||
} catch (e) {
|
||||
print('${logTime()}[DesktopVpnBridge] selectOutbound(tag=$tag) error: $e');
|
||||
logLine('DesktopVpnBridge', 'selectOutbound(tag=$tag) error: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
@@ -178,11 +178,11 @@ class DesktopVpnBridge implements VpnBridge {
|
||||
Future<void> setKillSwitch({required bool on}) async {
|
||||
if (_killSwitchEnabled == on) return;
|
||||
_killSwitchEnabled = on;
|
||||
print('${logTime()}[DesktopVpnBridge] setKillSwitch=$on');
|
||||
logLine('DesktopVpnBridge', 'setKillSwitch=$on');
|
||||
|
||||
// 若内核正在运行,用新设置重载(会有约 1~2s 重连)
|
||||
if (_kernel.isRunning && _lastUserConfigJson != null) {
|
||||
print('${logTime()}[DesktopVpnBridge] reloading kernel for killSwitch change');
|
||||
logLine('DesktopVpnBridge', 'reloading kernel for killSwitch change');
|
||||
// 暂停自动重连,避免 kill 触发重连定时器
|
||||
final wasAutoReconnect = _shouldAutoReconnect;
|
||||
_shouldAutoReconnect = false;
|
||||
@@ -230,18 +230,18 @@ class DesktopVpnBridge implements VpnBridge {
|
||||
_reconnectAttempt.clamp(0, _kRetryDelaysSec.length - 1)];
|
||||
_reconnectAttempt++;
|
||||
|
||||
print(
|
||||
'${logTime()}[DesktopVpnBridge] auto-reconnect in ${delaySec}s (attempt $_reconnectAttempt)');
|
||||
logLine('DesktopVpnBridge',
|
||||
'auto-reconnect in ${delaySec}s (attempt $_reconnectAttempt)');
|
||||
|
||||
_reconnectTimer = Timer(Duration(seconds: delaySec), () async {
|
||||
if (!_shouldAutoReconnect || _lastUserConfigJson == null) return;
|
||||
try {
|
||||
print('${logTime()}[DesktopVpnBridge] auto-reconnect: attempting start...');
|
||||
logLine('DesktopVpnBridge', 'auto-reconnect: attempting start...');
|
||||
await start(_lastUserConfigJson!);
|
||||
_reconnectAttempt = 0; // 成功后重置退避计数
|
||||
print('${logTime()}[DesktopVpnBridge] auto-reconnect: success');
|
||||
logLine('DesktopVpnBridge', 'auto-reconnect: success');
|
||||
} catch (e) {
|
||||
print('${logTime()}[DesktopVpnBridge] auto-reconnect: start failed: $e');
|
||||
logLine('DesktopVpnBridge', 'auto-reconnect: start failed: $e');
|
||||
// start() 失败(内核未能启动)→ 手动调度下一次重试
|
||||
if (_shouldAutoReconnect) _scheduleReconnect();
|
||||
}
|
||||
@@ -342,7 +342,7 @@ class DesktopVpnBridge implements VpnBridge {
|
||||
try {
|
||||
await Process.run('chmod', ['600', file.path]);
|
||||
} catch (_) {
|
||||
print('${logTime()}[DesktopVpnBridge] warning: chmod 600 failed for ${file.path}');
|
||||
logLine('DesktopVpnBridge', 'warning: chmod 600 failed for ${file.path}');
|
||||
}
|
||||
}
|
||||
return file.path;
|
||||
|
||||
@@ -30,7 +30,7 @@ import 'dart:math' as math;
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'log_time.dart';
|
||||
import 'log.dart';
|
||||
import 'vpn_bridge.dart';
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════
|
||||
@@ -482,7 +482,7 @@ class DesktopKernelProcess implements KernelProcess {
|
||||
}
|
||||
|
||||
void _log(String msg) {
|
||||
print('${logTime()}[DesktopKernel] $msg');
|
||||
logLine('DesktopKernel', msg);
|
||||
if (!_logCtrl.isClosed) _logCtrl.add(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// log.dart — 桌面端统一日志出口。
|
||||
// 格式: `[时:分:秒.毫秒] [tag] msg`,全部走 logLine(),便于排查时序。
|
||||
// 注:sing-box 自身时间戳已在下发配置里关闭(log.timestamp=false),
|
||||
// 避免与这里的前缀重复打印两个时间。
|
||||
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
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');
|
||||
@@ -143,7 +143,9 @@ func BuildClientConfig(node *nodes.NodeRow, dpUUID, deriveKey string) ([]byte, e
|
||||
}
|
||||
|
||||
cfg := map[string]any{
|
||||
"log": map[string]any{"level": "warn", "timestamp": true},
|
||||
// timestamp=false:客户端日志出口(logLine)已统一加时间戳,
|
||||
// 关掉 sing-box 自带时间戳避免一行打印两个时间。
|
||||
"log": map[string]any{"level": "warn", "timestamp": false},
|
||||
"inbounds": []any{tunIn},
|
||||
"outbounds": append(proxyOutbounds,
|
||||
autoBest,
|
||||
|
||||
Reference in New Issue
Block a user