Files
ShadowsocksX-NG/ShadowsocksX-NG2/Domain/Runtime/RuntimeEventStore.swift
T

66 lines
2.2 KiB
Swift

import Foundation
/// GUI 侧运行事件环形缓冲:收集 `RuntimeLog.emit`
/// 的事件供主窗口日志查看器实时呈现与诊断导出取用。事件文本来自封闭枚举的
/// 渲染,构造上不含敏感值;容量封顶只留最近事件(够诊断即可,不做复杂轮转)。
/// wrapper 进程不使用本存储(其 stderr 已收敛进 agent.log)。
final class RuntimeEventStore: RuntimeEventSink, @unchecked Sendable {
struct Entry: Equatable, Identifiable {
let id: Int
let timestamp: Date
/// 事件原值(封闭枚举):原始呈现与报告白名单投影共用同一事实。
let event: RuntimeLogEvent
/// 事件文本(查看器与原始行共用)。
var text: String { event.description }
/// 查看器共用的行格式;固定 POSIX locale 保证报障对照不随用户
/// 区域漂移。
var renderedLine: String {
renderedLine(eventText: text)
}
/// 指定事件文本的行格式;报告侧先经白名单清洗,再复用相同时间戳与间隔。
func renderedLine(eventText: String) -> String {
"\(Self.timestampFormatter.string(from: timestamp)) \(eventText)"
}
/// 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(event: RuntimeLogEvent, timestamp: Date = Date()) {
lock.lock()
defer { lock.unlock() }
entries.append(Entry(id: nextID, timestamp: timestamp, event: event))
nextID += 1
if entries.count > capacity {
entries.removeFirst(entries.count - capacity)
}
}
}