mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-20 08:02:16 +00:00
yes
This commit is contained in:
parent
8b2ee00c90
commit
e9d9101526
5 changed files with 110 additions and 62 deletions
|
|
@ -20,8 +20,9 @@ class Logger {
|
|||
private var logs: [LogEntry] = []
|
||||
private let logFileURL: URL
|
||||
private let logFilterViewModel = LogFilterViewModel.shared
|
||||
|
||||
|
||||
private let maxFileSize = 1024 * 512
|
||||
private let maxLogEntries = 1000
|
||||
|
||||
private init() {
|
||||
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
|
|
@ -35,6 +36,11 @@ class Logger {
|
|||
|
||||
queue.async(flags: .barrier) {
|
||||
self.logs.append(entry)
|
||||
|
||||
if self.logs.count > self.maxLogEntries {
|
||||
self.logs.removeFirst(self.logs.count - self.maxLogEntries)
|
||||
}
|
||||
|
||||
self.saveLogToFile(entry)
|
||||
self.debugLog(entry)
|
||||
}
|
||||
|
|
@ -51,6 +57,18 @@ class Logger {
|
|||
return result
|
||||
}
|
||||
|
||||
func getLogsAsync() async -> String {
|
||||
return await withCheckedContinuation { continuation in
|
||||
queue.async {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "dd-MM HH:mm:ss"
|
||||
let result = self.logs.map { "[\(dateFormatter.string(from: $0.timestamp))] [\($0.type)] \($0.message)" }
|
||||
.joined(separator: "\n----\n")
|
||||
continuation.resume(returning: result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clearLogs() {
|
||||
queue.async(flags: .barrier) {
|
||||
self.logs.removeAll()
|
||||
|
|
@ -58,6 +76,16 @@ class Logger {
|
|||
}
|
||||
}
|
||||
|
||||
func clearLogsAsync() async {
|
||||
await withCheckedContinuation { continuation in
|
||||
queue.async(flags: .barrier) {
|
||||
self.logs.removeAll()
|
||||
try? FileManager.default.removeItem(at: self.logFileURL)
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func saveLogToFile(_ log: LogEntry) {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "dd-MM HH:mm:ss"
|
||||
|
|
@ -69,61 +97,52 @@ class Logger {
|
|||
return
|
||||
}
|
||||
|
||||
if FileManager.default.fileExists(atPath: logFileURL.path) {
|
||||
do {
|
||||
do {
|
||||
if FileManager.default.fileExists(atPath: logFileURL.path) {
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: logFileURL.path)
|
||||
let fileSize = attributes[.size] as? UInt64 ?? 0
|
||||
|
||||
if fileSize + UInt64(data.count) > maxFileSize {
|
||||
guard var content = try? String(contentsOf: logFileURL, encoding: .utf8) else { return }
|
||||
|
||||
// Ensure content is not empty and contains valid UTF-8
|
||||
guard !content.isEmpty else {
|
||||
try? data.write(to: logFileURL)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove old entries until we have space
|
||||
while (content.data(using: .utf8)?.count ?? 0) + data.count > maxFileSize {
|
||||
if let rangeOfFirstLine = content.range(of: "\n---\n") {
|
||||
// Ensure we don't try to remove beyond the string's bounds
|
||||
let endIndex = min(rangeOfFirstLine.upperBound, content.endIndex)
|
||||
content.removeSubrange(content.startIndex..<endIndex)
|
||||
} else {
|
||||
// If we can't find a separator, clear the content
|
||||
content = ""
|
||||
break
|
||||
}
|
||||
|
||||
// Safety check to prevent infinite loops
|
||||
if content.isEmpty {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Append new log entry
|
||||
content += logString
|
||||
|
||||
// Write back to file
|
||||
if let finalData = content.data(using: .utf8) {
|
||||
try? finalData.write(to: logFileURL)
|
||||
}
|
||||
} else {
|
||||
if let handle = try? FileHandle(forWritingTo: logFileURL) {
|
||||
handle.seekToEndOfFile()
|
||||
handle.write(data)
|
||||
handle.closeFile()
|
||||
}
|
||||
self.truncateLogFile()
|
||||
}
|
||||
} catch {
|
||||
print("Error managing log file: \(error)")
|
||||
|
||||
if let handle = try? FileHandle(forWritingTo: logFileURL) {
|
||||
handle.seekToEndOfFile()
|
||||
handle.write(data)
|
||||
handle.closeFile()
|
||||
}
|
||||
} else {
|
||||
try data.write(to: logFileURL)
|
||||
}
|
||||
} else {
|
||||
} catch {
|
||||
print("Error managing log file: \(error)")
|
||||
try? data.write(to: logFileURL)
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints log messages to the Xcode console only in DEBUG mode
|
||||
private func truncateLogFile() {
|
||||
do {
|
||||
guard let content = try? String(contentsOf: logFileURL, encoding: .utf8),
|
||||
!content.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let entries = content.components(separatedBy: "\n---\n")
|
||||
guard entries.count > 10 else { return }
|
||||
|
||||
let keepCount = entries.count / 2
|
||||
let truncatedEntries = Array(entries.suffix(keepCount))
|
||||
let truncatedContent = truncatedEntries.joined(separator: "\n---\n")
|
||||
|
||||
if let truncatedData = truncatedContent.data(using: .utf8) {
|
||||
try truncatedData.write(to: logFileURL)
|
||||
}
|
||||
} catch {
|
||||
print("Error truncating log file: \(error)")
|
||||
try? FileManager.default.removeItem(at: logFileURL)
|
||||
}
|
||||
}
|
||||
|
||||
private func debugLog(_ entry: LogEntry) {
|
||||
#if DEBUG
|
||||
let dateFormatter = DateFormatter()
|
||||
|
|
|
|||
|
|
@ -203,8 +203,6 @@ struct SettingsViewData: View {
|
|||
}
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
if isCalculatingSize {
|
||||
ProgressView()
|
||||
.scaleEffect(0.7)
|
||||
|
|
@ -233,9 +231,9 @@ struct SettingsViewData: View {
|
|||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
}
|
||||
Spacer()
|
||||
|
||||
Divider().padding(.horizontal, 16)
|
||||
|
||||
Divider()
|
||||
|
||||
Button(action: clearAllCaches) {
|
||||
Text("Clear All Caches")
|
||||
.foregroundColor(.red)
|
||||
|
|
|
|||
|
|
@ -61,26 +61,39 @@ fileprivate struct SettingsSection<Content: View>: View {
|
|||
|
||||
struct SettingsViewLogger: View {
|
||||
@State private var logs: String = ""
|
||||
@State private var isLoading: Bool = true
|
||||
@StateObject private var filterViewModel = LogFilterViewModel.shared
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 24) {
|
||||
SettingsSection(title: "Logs") {
|
||||
Text(logs)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.textSelection(.enabled)
|
||||
if isLoading {
|
||||
HStack {
|
||||
ProgressView()
|
||||
.scaleEffect(0.8)
|
||||
Text("Loading logs...")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.vertical, 20)
|
||||
} else {
|
||||
Text(logs)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
.navigationTitle("Logs")
|
||||
.onAppear {
|
||||
logs = Logger.shared.getLogs()
|
||||
loadLogsAsync()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
|
|
@ -93,8 +106,7 @@ struct SettingsViewLogger: View {
|
|||
Label("Copy to Clipboard", systemImage: "doc.on.doc")
|
||||
}
|
||||
Button(role: .destructive, action: {
|
||||
Logger.shared.clearLogs()
|
||||
logs = Logger.shared.getLogs()
|
||||
clearLogsAsync()
|
||||
}) {
|
||||
Label("Clear Logs", systemImage: "trash")
|
||||
}
|
||||
|
|
@ -111,4 +123,23 @@ struct SettingsViewLogger: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadLogsAsync() {
|
||||
Task {
|
||||
let loadedLogs = await Logger.shared.getLogsAsync()
|
||||
await MainActor.run {
|
||||
self.logs = loadedLogs
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func clearLogsAsync() {
|
||||
Task {
|
||||
await Logger.shared.clearLogsAsync()
|
||||
await MainActor.run {
|
||||
self.logs = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,6 +217,7 @@
|
|||
0402DA122DE7B5EC003BB42C /* SearchView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
133D7C7C2D2BE2630075467E /* SearchView.swift */,
|
||||
0402DA162DE7B7B8003BB42C /* SearchViewComponents.swift */,
|
||||
0402DA0F2DE7B5EC003BB42C /* SearchComponents.swift */,
|
||||
0402DA102DE7B5EC003BB42C /* SearchResultsGrid.swift */,
|
||||
|
|
@ -348,7 +349,6 @@
|
|||
133D7C7B2D2BE2630075467E /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
133D7C7C2D2BE2630075467E /* SearchView.swift */,
|
||||
72443C7C2DC8036500A61321 /* DownloadView.swift */,
|
||||
0402DA122DE7B5EC003BB42C /* SearchView */,
|
||||
133D7C7F2D2BE2630075467E /* MediaInfoView */,
|
||||
|
|
|
|||
Loading…
Reference in a new issue