diag(client/macos): 抓 sing-box urltest 内核日志,定位探测为何失败
确认 urltest 为空是内核探测 100% 失败(传输/读取链路均正常)。本次注入 log.level=debug, StatsCollector 新增 Log 命令客户端,抓取内核日志里 urltest/gstatic 相关行(失败原因: DNS/连接/超时),塞进 stats 的 klog 字段经主 app 可读日志透出 —— 直接看到内核报错。 CURRENT_PROJECT_VERSION 51→52。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,11 @@ final class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
experimental["cache_file"] = ["enabled": true]
|
||||
}
|
||||
root["experimental"] = experimental
|
||||
// 诊断:开 debug 日志,让 urltest 探测的失败原因(DNS/连接/超时)出现在内核日志里,
|
||||
// 由 StatsCollector 的 Log 客户端抓取透出。
|
||||
var logCfg = (root["log"] as? [String: Any]) ?? [:]
|
||||
logCfg["level"] = "debug"
|
||||
root["log"] = logCfg
|
||||
guard let data = try? JSONSerialization.data(withJSONObject: root),
|
||||
let out = String(data: data, encoding: .utf8) else { return configJSON }
|
||||
return out
|
||||
@@ -374,6 +379,7 @@ private func simpleError(_ msg: String) -> NSError {
|
||||
final class StatsCollector: NSObject {
|
||||
private let queue = DispatchQueue(label: "pangolin.stats.collector")
|
||||
private var statusClient: LibboxCommandClient?
|
||||
private var logClient: LibboxCommandClient?
|
||||
private var started = false
|
||||
private var clashTimer: DispatchSourceTimer?
|
||||
|
||||
@@ -396,23 +402,24 @@ final class StatsCollector: NSObject {
|
||||
guard let self, !self.started else { return }
|
||||
self.clashBase = "http://127.0.0.1:\(clashPort)"
|
||||
self.clashSecret = clashSecret
|
||||
self.statusClient = self.connectStatusClient()
|
||||
self.statusClient = self.connectCommand(LibboxCommandStatus, "status")
|
||||
self.logClient = self.connectCommand(LibboxCommandLog, "log") // 抓内核 urltest 报错
|
||||
self.started = true
|
||||
self.startClashTimer()
|
||||
}
|
||||
}
|
||||
|
||||
/// libbox Status client(取速率)。command.sock 刚建好可能稍滞后,重试。
|
||||
private func connectStatusClient() -> LibboxCommandClient? {
|
||||
/// 为单一命令开一个 libbox client 并连上。command.sock 刚建好可能稍滞后,重试。
|
||||
private func connectCommand(_ command: Int32, _ label: String) -> LibboxCommandClient? {
|
||||
let options = LibboxCommandClientOptions()
|
||||
options.statusInterval = 1_000_000_000 // 1s
|
||||
options.addCommand(LibboxCommandStatus)
|
||||
options.addCommand(command)
|
||||
guard let c = LibboxNewCommandClient(self, options) else { return nil }
|
||||
for _ in 0..<20 {
|
||||
do { try c.connect(); NSLog("[pangolin/collector] status connected"); return c }
|
||||
do { try c.connect(); NSLog("[pangolin/collector] %@ connected", label); return c }
|
||||
catch { Thread.sleep(forTimeInterval: 0.25) }
|
||||
}
|
||||
NSLog("[pangolin/collector] status gave up")
|
||||
NSLog("[pangolin/collector] %@ gave up", label)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -421,7 +428,9 @@ final class StatsCollector: NSObject {
|
||||
guard let self else { return }
|
||||
self.clashTimer?.cancel(); self.clashTimer = nil
|
||||
if let c = self.statusClient { try? c.disconnect() }
|
||||
if let c = self.logClient { try? c.disconnect() }
|
||||
self.statusClient = nil
|
||||
self.logClient = nil
|
||||
self.started = false
|
||||
self.lock.lock()
|
||||
self.latest = StatsCollector.zero
|
||||
@@ -564,7 +573,19 @@ extension StatsCollector: LibboxCommandClientHandlerProtocol {
|
||||
func connected() {}
|
||||
func disconnected(_ message: String?) {}
|
||||
func clearLogs() {}
|
||||
func writeLogs(_ messageList: (any LibboxLogIteratorProtocol)?) {}
|
||||
func writeLogs(_ messageList: (any LibboxLogIteratorProtocol)?) {
|
||||
guard let it = messageList else { return }
|
||||
while it.hasNext() {
|
||||
guard let entry = it.next() else { break }
|
||||
let msg = entry.message
|
||||
let low = msg.lowercased()
|
||||
// 只抓 urltest 探测相关行(含失败原因:DNS/连接/超时)。
|
||||
guard low.contains("urltest") || low.contains("url-test")
|
||||
|| low.contains("gstatic") || low.contains("generate_204") else { continue }
|
||||
NSLog("[pangolin/klog] %@", msg)
|
||||
lock.lock(); latest["klog"] = String(msg.prefix(180)); lock.unlock()
|
||||
}
|
||||
}
|
||||
func setDefaultLogLevel(_ level: Int32) {}
|
||||
func initializeClashMode(_ modeList: (any LibboxStringIteratorProtocol)?, currentMode: String?) {}
|
||||
func updateClashMode(_ newMode: String?) {}
|
||||
|
||||
@@ -659,7 +659,7 @@
|
||||
baseConfigurationReference = C57BBFD43F9175D1E23685EF /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -674,7 +674,7 @@
|
||||
baseConfigurationReference = F8904897A48DC81799B8752E /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -689,7 +689,7 @@
|
||||
baseConfigurationReference = B21E68FC1F5D33DD67A0DF5E /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -960,7 +960,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -1010,7 +1010,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -1059,7 +1059,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 52;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
|
||||
@@ -67,9 +67,9 @@ final class StatsClient: NSObject {
|
||||
if self.diagTick % 5 == 0 {
|
||||
let urls = (obj["urltestResults"] as? [[String: Any]]) ?? []
|
||||
let pairs = urls.map { "\($0["tag"] ?? "?")=\($0["delayMs"] ?? -1)" }.joined(separator: ",")
|
||||
NSLog("[pangolin/stats] dl=%@ ul=%@ urltest[%d]=%@ diag=%@",
|
||||
NSLog("[pangolin/stats] dl=%@ ul=%@ urltest[%d]=%@ diag=%@ klog=%@",
|
||||
"\(obj["downloadSpeed"] ?? 0)", "\(obj["uploadSpeed"] ?? 0)", urls.count, pairs,
|
||||
"\(obj["diag"] ?? "-")")
|
||||
"\(obj["diag"] ?? "-")", "\(obj["klog"] ?? "-")")
|
||||
}
|
||||
self.emit(obj)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user