mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
debug App Logs view
This commit is contained in:
parent
81839ea313
commit
6fc475da76
5 changed files with 73 additions and 56 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 { }
|
||||
|
|
|
|||
|
|
@ -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")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue