debug App Logs view

This commit is contained in:
Garth Vander Houwen 2024-06-05 08:44:46 -07:00
parent 81839ea313
commit 6fc475da76
5 changed files with 73 additions and 56 deletions

View file

@ -25,29 +25,30 @@ extension Logger {
static let statistics = Logger(subsystem: subsystem, category: "📈 Stats")
/// Fetch from the logstore
static public func fetch(since date: Date, predicateFormat: String) async throws -> [String] {
static public func fetch(since date: Date, predicateFormat: String) async throws -> [OSLogEntryLog] {
let store = try OSLogStore(scope: .currentProcessIdentifier)
let position = store.position(date: date)
let predicate = NSPredicate(format: predicateFormat)
let entries = try store.getEntries(at: position, matching: predicate)
var logs: [String] = []
var logs: [OSLogEntryLog] = []
for entry in entries {
try Task.checkCancellation()
if let log = entry as? OSLogEntryLog {
logs.append("""
\(entry.date.formatted(.dateTime.hour(.twoDigits(amPM: .omitted)).minute().second().secondFraction(.fractional(3)))) \(log.level.description) \
\(log.category) \(entry.composedMessage)\n
""")
logs.append(log)
// logs.append("""
// \(entry.date.formatted(.dateTime.hour(.twoDigits(amPM: .omitted)).minute().second().secondFraction(.fractional(3)))) \(log.level.description) \
// \(log.category) \(entry.composedMessage)\n
// """)
} else {
logs.append("\(entry.date): \(entry.composedMessage)\n")
// logs.append("\(entry.date): \(entry.composedMessage)\n")
}
}
if logs.isEmpty { logs = ["Nothing found"] }
if logs.isEmpty { logs = [] }
return logs
}
}

View file

@ -85,15 +85,15 @@ import OSLog
if smartPostion {
let age = -location.timestamp.timeIntervalSinceNow
if age > 10 {
Logger.services.warning("📍 Bad Location \(self.count): Too Old \(age) seconds ago \(location)")
Logger.services.warning("📍 Bad Location \(self.count): Too Old \(age) seconds ago \(location, privacy: .private)")
return false
}
if location.horizontalAccuracy < 0 {
Logger.services.warning("📍 Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location)")
Logger.services.warning("📍 Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private)")
return false
}
if location.horizontalAccuracy > 5 {
Logger.services.warning("📍 Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location)")
Logger.services.warning("📍 Bad Location \(self.count): Horizontal Accuracy: \(location.horizontalAccuracy) \(location, privacy: .private)")
return false
}
}

View file

@ -74,7 +74,8 @@ struct PositionPopover: View {
.padding(.bottom, 5)
/// Altitude
Label {
Text("Altitude: \(distanceFormatter.string(fromDistance: Double(position.altitude)))")
let altitude = Measurement(value: Double(position.altitude), unit: UnitLength.meters)
Text("Altitude: \(altitude.formatted())")
.foregroundColor(.primary)
} icon: {
Image(systemName: "mountain.2.fill")

View file

@ -8,47 +8,60 @@
import SwiftUI
import OSLog
@available(iOS 17.4, *)
struct AppLog: View {
@State private var text = "Loading..."
@State private var logs: [OSLogEntryLog] = []
@State private var sortOrder = [KeyPathComparator(\OSLogEntryLog.date)]
@State private var selection = Set<OSLogEntryLog.ID>()
private var idiom : UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
var body: some View {
ScrollView {
Text(text)
.textSelection(.enabled)
.fontDesign(.monospaced)
.font(.caption2)
.padding()
Table(logs, selection: $selection, sortOrder: $sortOrder) {
if idiom != .phone {
TableColumn("Date", value: \.date) { value in
Text("\(value.date, format: .dateTime)")
}
.width(min: 150, max: 200)
TableColumn("Category", value: \.category)
.width(min: 100, max: 125)
}
TableColumn("Message", value: \.composedMessage)
.width(ideal: 200, max: .infinity)
}
.onChange(of: sortOrder) { _, sortOrder in
logs.sort(using: sortOrder)
}
.task {
text = await fetchLogs()
logs = await fetchLogs()
}
.presentationCompactAdaptation(.fullScreenCover)
}
}
@available(iOS 17.4, *)
extension AppLog {
static private let template = NSPredicate(format: "(subsystem BEGINSWITH $PREFIX) || ((subsystem IN $SYSTEM) && ((messageType == error) || (messageType == fault)))")
@MainActor
private func fetchLogs() async -> [OSLogEntryLog] {
let calendar = Calendar.current
guard let dayAgo = calendar.date(byAdding: .day, value: -1, to: Date.now) else {
return []
}
do {
let predicate = AppLog.template.withSubstitutionVariables(
[
"PREFIX": "gvh.MeshtasticClient",
"SYSTEM": ["com.apple.coredata"]
])
let logs = try await Logger.fetch(since: dayAgo, predicateFormat: predicate.predicateFormat)
return logs
} catch {
return []
}
}
}
extension AppLog {
static private let template = NSPredicate(format:
"(subsystem BEGINSWITH $PREFIX) || ((subsystem IN $SYSTEM) && ((messageType == error) || (messageType == fault)))")
@MainActor
private func fetchLogs() async -> String {
let calendar = Calendar.current
guard let dayAgo = calendar.date(byAdding: .day,
value: -1, to: Date.now) else {
return "Invalid calendar"
}
do {
let predicate = AppLog.template.withSubstitutionVariables(
[
"PREFIX": "gvh.MeshtasticClient",
"SYSTEM": ["com.apple.coredata"]
])
let logs = try await Logger.fetch(since: dayAgo,
predicateFormat: predicate.predicateFormat)
return logs.joined()
} catch {
return error.localizedDescription
}
}
}
extension OSLogEntry: Identifiable { }

View file

@ -425,16 +425,18 @@ struct Settings: View {
}
.tag(SettingsSidebar.adminMessageLog)
#if DEBUG
NavigationLink {
AppLog()
} label: {
Label {
Text("Debug Logs")
} icon: {
Image(systemName: "building.columns")
if #available (iOS 17.4, *) {
NavigationLink {
AppLog()
} label: {
Label {
Text("Debug Logs")
} icon: {
Image(systemName: "building.columns")
}
}
.tag(SettingsSidebar.appLog)
}
.tag(SettingsSidebar.appLog)
#endif
}
Section(header: Text("Firmware")) {