Files
ShadowsocksX-NG/ShadowsocksX-NG2/Domain/RuntimeEventStore.swift
T
Qiu Yuzhou a6825aaafa feat(ng2): diagnostics log viewer and redacted export (#34)
主窗口诊断区(spec #21 D5/D11):

- RuntimeLog 增加事件接收缝,GUI 侧接入 RuntimeEventStore 内存环形缓冲
  (容量 500),wrapper 侧不变(stderr → agent.log 收敛)
- 主窗口侧栏新增「服务器/诊断」分区切换;诊断区日志查看器双源:GUI 事件
  实时呈现 + agent.log 尾部(1s 轮询),均可复制
- DiagnosticReportBuilder 显式触发的脱敏诊断导出:只含状态、存在性、权限、
  大小/时间、数量与脱敏元数据;不读 Keychain 与契约/目录内容;原始错误
  detail 不入导出,家目录前缀改写为「~」;服务器地址与备注不进入任何输出
- 投毒夹具测试验证导出不含任何敏感类目(密码/插件参数/地址/备注/token/JSON)

Closes #34
2026-09-20 22:41:28 +08:00

57 lines
1.8 KiB
Swift
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 Foundation
/// GUI 侧运行事件环形缓冲(spec #21 D5,issue #34):收集 `RuntimeLog.emit`
/// 的事件供主窗口日志查看器实时呈现与诊断导出取用。事件文本来自封闭枚举的
/// 渲染,构造上不含敏感值;容量封顶只留最近事件(够诊断即可,不做复杂轮转)。
/// wrapper 进程不使用本存储(其 stderr 已收敛进 agent.log)。
final class RuntimeEventStore: RuntimeEventSink, @unchecked Sendable {
struct Entry: Equatable, Identifiable {
let id: Int
let timestamp: Date
let text: String
/// 查看器与导出共用的行格式;固定 POSIX locale 保证报障对照不随用户
/// 区域漂移。
var renderedLine: String {
"\(Self.timestampFormatter.string(from: timestamp)) \(text)"
}
/// DateFormatter 在现代 Foundation 中线程安全;本格式器只被读取。
static let timestampFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
return formatter
}()
}
/// 进程级共享实例:GUI 启动时注册为 RuntimeLog 的接收缝。
static let shared = RuntimeEventStore()
private let lock = NSLock()
private var entries: [Entry] = []
private var nextID = 0
private let capacity: Int
init(capacity: Int = 500) {
self.capacity = max(1, capacity)
}
/// 当前缓冲快照(旧→新)。
var snapshot: [Entry] {
lock.lock()
defer { lock.unlock() }
return entries
}
func append(text: String, timestamp: Date = Date()) {
lock.lock()
defer { lock.unlock() }
entries.append(Entry(id: nextID, timestamp: timestamp, text: text))
nextID += 1
if entries.count > capacity {
entries.removeFirst(entries.count - capacity)
}
}
}