generated from StanfordSpezi/SpeziTemplateApplication
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e4fd6db
commit 8bb0834
Showing
7 changed files
with
310 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// | ||
// LogStore.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/8/24. | ||
// | ||
|
||
import Foundation | ||
import OSLog | ||
import Spezi | ||
import SwiftUI | ||
|
||
actor LogManager: Module, DefaultInitializable, EnvironmentAccessible { | ||
@Application(\.logger) private var logger | ||
|
||
func query( | ||
startDate: Date? = nil, | ||
endDate: Date? = nil, | ||
logType: OSLogEntryLog.Level? = nil | ||
) -> String { | ||
do { | ||
let store = try OSLogStore(scope: .currentProcessIdentifier) | ||
let position: OSLogPosition | ||
if let startDate = startDate { | ||
position = store.position(date: startDate) | ||
} else { | ||
position = store.position(timeIntervalSinceLatestBoot: 1) | ||
} | ||
|
||
let logs = try store.getEntries(at: position).compactMap { $0 as? OSLogEntryLog } | ||
|
||
return logs | ||
.filter { logEntry in | ||
/// Filter by subsystem | ||
guard logEntry.subsystem == Bundle.main.bundleIdentifier else { | ||
return false | ||
} | ||
|
||
/// Filter by log type if specified | ||
if let logType = logType, logEntry.level != logType { | ||
return false | ||
} | ||
|
||
/// Filter by date range if specified | ||
if let startDate = startDate, logEntry.date < startDate { | ||
return false | ||
} | ||
if let endDate = endDate, logEntry.date > endDate { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
.map { "[\($0.date.formatted())] [\($0.category)] \($0.composedMessage)" } | ||
.joined(separator: "\n") | ||
} catch { | ||
logger.warning("\(error.localizedDescription, privacy: .public)") | ||
return "" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// LogType.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/8/24. | ||
// | ||
|
||
import OSLog | ||
|
||
|
||
enum LogType: String, CaseIterable, Identifiable { | ||
case all = "All" | ||
case info = "Info" | ||
case debug = "Debug" | ||
case error = "Error" | ||
case fault = "Fault" | ||
|
||
var id: String { self.rawValue } | ||
|
||
var osLogLevel: OSLogEntryLog.Level? { | ||
switch self { | ||
case .all: | ||
return nil | ||
case .info: | ||
return .info | ||
case .debug: | ||
return .debug | ||
case .error: | ||
return .error | ||
case .fault: | ||
return .fault | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// LogShareView.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/8/24. | ||
// | ||
|
||
import OSLog | ||
import Spezi | ||
import SwiftUI | ||
|
||
|
||
struct LogViewer: View { | ||
@Environment(LogManager.self) var manager | ||
|
||
@State private var startDate: Date = Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() | ||
@State private var endDate = Date() | ||
@State private var selectedLogType: LogType = .all | ||
@State private var logs = "" | ||
@State private var isLoading = false | ||
|
||
var body: some View { | ||
VStack { | ||
/// Date range selection | ||
HStack { | ||
DatePicker("FROM", selection: $startDate, displayedComponents: .date) | ||
Spacer() | ||
DatePicker("TO", selection: $endDate, displayedComponents: .date) | ||
} | ||
.padding() | ||
|
||
/// Log type selection | ||
Picker("LOG_TYPE", selection: $selectedLogType) { | ||
ForEach(LogType.allCases) { type in | ||
Text(type.rawValue).tag(type) | ||
} | ||
} | ||
.pickerStyle(SegmentedPickerStyle()) | ||
.padding() | ||
|
||
ScrollView { | ||
if isLoading { | ||
ProgressView("LOADING_LOGS") | ||
.padding() | ||
} else { | ||
Text(logs) | ||
.padding() | ||
} | ||
} | ||
} | ||
.navigationTitle("LOG_VIEWER") | ||
.onAppear { | ||
Task { | ||
await queryLogs() | ||
} | ||
} | ||
.onChange(of: startDate) { | ||
Task { | ||
await queryLogs() | ||
} | ||
} | ||
.onChange(of: endDate) { | ||
Task { | ||
await queryLogs() | ||
} | ||
} | ||
.onChange(of: selectedLogType) { | ||
Task { | ||
await queryLogs() | ||
} | ||
} | ||
.toolbar { | ||
if !logs.isEmpty { | ||
ShareLink( | ||
item: logs, | ||
preview: SharePreview("LOGS", image: Image(systemName: "doc.text")) | ||
Check failure on line 76 in LifeSpace/Debug/LogViewer.swift GitHub Actions / SwiftLint / SwiftLint
|
||
) { | ||
Image(systemName: "square.and.arrow.up") | ||
Check failure on line 78 in LifeSpace/Debug/LogViewer.swift GitHub Actions / SwiftLint / SwiftLint
|
||
} | ||
} | ||
} | ||
} | ||
|
||
@MainActor | ||
private func queryLogs() async { | ||
isLoading = true | ||
|
||
/// This is very slow, so run as a detached task with high priority | ||
logs = await Task.detached(priority: .userInitiated) { [manager, startDate, endDate, selectedLogType] in | ||
await manager.query(startDate: startDate, endDate: endDate, logType: selectedLogType.osLogLevel) | ||
}.value | ||
|
||
isLoading = false | ||
} | ||
} |
Oops, something went wrong.