From ef6068321ddf4e1c3437fb0438de27cf909cf95c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 4 Jun 2024 22:41:45 -0700 Subject: [PATCH] Simple debug log --- Meshtastic.xcodeproj/project.pbxproj | 4 ++ .../xcschemes/WidgetsExtension.xcscheme | 1 + Meshtastic/Extensions/Logger.swift | 41 ++++++++++++++ Meshtastic/Views/Settings/AppLog.swift | 54 +++++++++++++++++++ Meshtastic/Views/Settings/Settings.swift | 4 +- 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 Meshtastic/Views/Settings/AppLog.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 47cce0cd..47259f78 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -207,6 +207,7 @@ DDF45C342BC1A48E005ED5F2 /* MQTTIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF45C332BC1A48E005ED5F2 /* MQTTIcon.swift */; }; DDF45C372BC46A5A005ED5F2 /* TimeZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */; }; DDF6B2482A9AEBF500BA6931 /* StoreForwardConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */; }; + DDF8E1F42C10125B0019C87E /* AppLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF8E1F32C10125B0019C87E /* AppLog.swift */; }; DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; }; DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */; }; DDFFA7472B3A7F3C004730DB /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFFA7462B3A7F3C004730DB /* Bundle.swift */; }; @@ -481,6 +482,7 @@ DDF6B2462A9AEB9E00BA6931 /* MeshtasticDataModelV17.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV17.xcdatamodel; sourceTree = ""; }; DDF6B2472A9AEBF500BA6931 /* StoreForwardConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForwardConfig.swift; sourceTree = ""; }; DDF6B24B2A9C2FC800BA6931 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + DDF8E1F32C10125B0019C87E /* AppLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLog.swift; sourceTree = ""; }; DDF924C926FBB953009FE055 /* ConnectedDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectedDevice.swift; sourceTree = ""; }; DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentConditionsCompact.swift; sourceTree = ""; }; DDFFA7462B3A7F3C004730DB /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; @@ -590,6 +592,7 @@ DD93800C2BA74CE3008BEC06 /* Channels */, DD97E96728EFE9A00056DDA4 /* About.swift */, DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */, + DDF8E1F32C10125B0019C87E /* AppLog.swift */, DD4A911D2708C65400501B7E /* AppSettings.swift */, DDAB580C2B0DAA9E00147258 /* Routes.swift */, DDE9659B2B1C3B6A00531070 /* RouteRecorder.swift */, @@ -1237,6 +1240,7 @@ DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, DD5E5202298EE33B00D21B61 /* admin.pb.swift in Sources */, DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */, + DDF8E1F42C10125B0019C87E /* AppLog.swift in Sources */, DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */, DDB75A112A059258006ED576 /* Url.swift in Sources */, DDAD49ED2AFB39DC00B4425D /* MeshMap.swift in Sources */, diff --git a/Meshtastic.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme b/Meshtastic.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme index 880339bc..decd8381 100644 --- a/Meshtastic.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme +++ b/Meshtastic.xcodeproj/xcshareddata/xcschemes/WidgetsExtension.xcscheme @@ -89,6 +89,7 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" launchAutomaticallySubstyle = "2"> diff --git a/Meshtastic/Extensions/Logger.swift b/Meshtastic/Extensions/Logger.swift index e5c29a95..74dcdb4a 100644 --- a/Meshtastic/Extensions/Logger.swift +++ b/Meshtastic/Extensions/Logger.swift @@ -23,4 +23,45 @@ extension Logger { /// All logs related to tracking and analytics. static let statistics = Logger(subsystem: subsystem, category: "📈 Stats") + + /// Fetch from the logstore + static public func fetch(since date: Date, predicateFormat: String) async throws -> [String] { + + 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] = [] + 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 + """) + } else { + logs.append("\(entry.date): \(entry.composedMessage)\n") + } + } + + if logs.isEmpty { logs = ["Nothing found"] } + return logs + } +} + +extension OSLogEntryLog.Level { + fileprivate var description: String { + switch self { + case .undefined: "undefined" + case .debug: "Debug" + case .info: "Info" + case .notice: "Notice" + case .error: "Error" + case .fault: "Fault" + @unknown default: "default" + } + } } diff --git a/Meshtastic/Views/Settings/AppLog.swift b/Meshtastic/Views/Settings/AppLog.swift new file mode 100644 index 00000000..7ec291a7 --- /dev/null +++ b/Meshtastic/Views/Settings/AppLog.swift @@ -0,0 +1,54 @@ +// +// AppLog.swift +// Meshtastic +// +// Created by Garth Vander Houwen on 6/4/24. +// + +import SwiftUI +import OSLog + +struct AppLog: View { + @State private var text = "Loading..." + + var body: some View { + ScrollView { + Text(text) + .textSelection(.enabled) + .fontDesign(.monospaced) + .font(.caption2) + .padding() + } + .task { + text = await fetchLogs() + } + } +} + +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 + } + } +} diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 86c83e3d..dda4f5ba 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -426,10 +426,10 @@ struct Settings: View { .tag(SettingsSidebar.adminMessageLog) #if DEBUG NavigationLink { - // AppLogs() + AppLog() } label: { Label { - Text("app.log") + Text("Debug Logs") } icon: { Image(systemName: "building.columns") }