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.
Creates a log viewer for debugging (#49)
* Create log viewer * Add flag for debug options * Fix swiftlint errors * Share logs as a text file * Improve filtering * Make the task cancellable * Display logs in a list view * Update UI * Adjust styling of error messages in list * Surface errors in UI * Update log levels * Update log entry style * Add colors to level labels * Update log manager * Additional improvements to log manager and view
- Loading branch information
1 parent
e4fd6db
commit 555e4dd
Showing
10 changed files
with
523 additions
and
7 deletions.
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,75 @@ | ||
// | ||
// LogLevel.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/8/24. | ||
// | ||
|
||
import OSLog | ||
import SwiftUI | ||
|
||
|
||
enum LogLevel: String, CaseIterable, Identifiable { | ||
case all = "All" | ||
case info = "Info" | ||
case debug = "Debug" | ||
case error = "Error" | ||
case fault = "Fault" | ||
case notice = "Notice" | ||
case undefined = "Undefined" | ||
|
||
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 | ||
case .notice: | ||
return .notice | ||
case .undefined: | ||
return .undefined | ||
} | ||
} | ||
|
||
var color: Color { | ||
switch self { | ||
case .info: | ||
return .blue | ||
case .debug: | ||
return .green | ||
case .error: | ||
return .red | ||
case .fault: | ||
return .purple | ||
case .notice: | ||
return .orange | ||
case .all, .undefined: | ||
return .gray | ||
} | ||
} | ||
|
||
init(from osLogLevel: OSLogEntryLog.Level) { | ||
switch osLogLevel { | ||
case .info: | ||
self = .info | ||
case .debug: | ||
self = .debug | ||
case .error: | ||
self = .error | ||
case .fault: | ||
self = .fault | ||
case .notice: | ||
self = .notice | ||
@unknown default: | ||
self = .undefined | ||
} | ||
} | ||
} |
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,68 @@ | ||
// | ||
// LogStore.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/8/24. | ||
// | ||
|
||
import Foundation | ||
import OSLog | ||
import Spezi | ||
import SwiftUI | ||
|
||
/// Manages log entries within the application using `OSLogStore`, allowing querying | ||
/// based on date ranges and log levels. | ||
class LogManager { | ||
/// Reference to the `OSLogStore`, which provides access to system logs. | ||
private let store: OSLogStore? | ||
|
||
/// Initializes the `LogManager` and attempts to set the `OSLogStore` with | ||
/// a scope limited to the current process identifier. | ||
init() { | ||
self.store = try? OSLogStore(scope: .currentProcessIdentifier) | ||
} | ||
|
||
/// Queries logs within a specified date range and optional log level. | ||
/// | ||
/// - Parameters: | ||
/// - startDate: The start date from which logs should be queried. | ||
/// - endDate: An optional end date up to which logs should be queried. | ||
/// - logLevel: An optional log level filter, returning only entries of this level if specified. | ||
/// - Returns: An array of `OSLogEntryLog` entries that match the specified criteria. | ||
/// - Throws: `LogManagerError.invalidLogStore` if `OSLogStore` is unavailable, or | ||
/// `LogManagerError.invalidBundleIdentifier` if the bundle identifier cannot be retrieved. | ||
func query( | ||
startDate: Date, | ||
endDate: Date? = nil, | ||
logLevel: OSLogEntryLog.Level? = nil | ||
) throws -> [OSLogEntryLog] { | ||
guard let store else { | ||
throw LogManagerError.invalidLogStore | ||
} | ||
|
||
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { | ||
throw LogManagerError.invalidBundleIdentifier | ||
} | ||
|
||
let position = store.position(date: startDate) | ||
let predicate = NSPredicate(format: "subsystem == %@", bundleIdentifier) | ||
let logs = try store.getEntries(at: position, matching: predicate) | ||
.reversed() | ||
.compactMap { $0 as? OSLogEntryLog } | ||
|
||
return logs | ||
.filter { logEntry in | ||
/// Filter by log type if specified | ||
if let logLevel, logEntry.level != logLevel { | ||
return false | ||
} | ||
|
||
/// Filter by end date if specified | ||
if let endDate, logEntry.date > endDate { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
} | ||
} |
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,25 @@ | ||
// | ||
// LogManagerError.swift | ||
// LifeSpace | ||
// | ||
// Created by Vishnu Ravi on 11/11/24. | ||
// | ||
|
||
|
||
enum LogManagerError: Error { | ||
/// Throw when the log store is invalid | ||
case invalidLogStore | ||
/// Throw when the bundle identifier is invalid | ||
case invalidBundleIdentifier | ||
} | ||
|
||
extension LogManagerError: CustomStringConvertible { | ||
public var description: String { | ||
switch self { | ||
case .invalidLogStore: | ||
return "The OSLogStore is invalid." | ||
case .invalidBundleIdentifier: | ||
return "The bundle identifier is invalid." | ||
} | ||
} | ||
} |
Oops, something went wrong.