taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 3745daec876d0e23aca0b827481fb9ad6d76c287
parent 5a206a0935844ddfea8079e4fc1108fcfd5ddc2d
Author: Marc Stibane <marc@taler.net>
Date:   Tue, 27 Jun 2023 14:40:22 +0200

Logging

Diffstat:
MTalerWallet1/Backend/WalletCore.swift | 37+++++++++++++++++++++----------------
MTalerWallet1/Controllers/DebugViewC.swift | 15+++++++++++++--
MTalerWallet1/Controllers/TalerWallet1App.swift | 25+++++++++++++------------
MTalerWallet1/Model/Model+Balances.swift | 1-
MTalerWallet1/Model/Model+Exchange.swift | 7++-----
MTalerWallet1/Model/WalletModel.swift | 29++++++++++-------------------
MTalerWallet1/Views/Balances/BalancesListView.swift | 2+-
MTalerWallet1/Views/Balances/BalancesSectionView.swift | 5++++-
8 files changed, 64 insertions(+), 57 deletions(-)

diff --git a/TalerWallet1/Backend/WalletCore.swift b/TalerWallet1/Backend/WalletCore.swift @@ -70,43 +70,46 @@ class WalletCore: QuickjsMessageHandler { } deinit { - symLog.log() + logger.log("shutdown Quickjs") // TODO: send shutdown message to talerWalletInstance // quickjs.waitStopped() } init() throws { - symLog.log("alloc.init wallet-core") + logger.info("init Quickjs") requestsMade = 0 queue = DispatchQueue(label: "net.taler.myQueue", attributes: .concurrent) semaphore = DispatchSemaphore(value: 1) quickjs = Quickjs() quickjs.messageHandler = self - symLog.log("wallet-core done") + logger.log("Quickjs running") } } // MARK: - completionHandler functions extension WalletCore { private func handleError(_ decoded: ResponseOrNotification) throws { guard let requestId = decoded.id else { - symLog.log(decoded) // TODO: .error + logger.error("didn't find requestId in error response") + // TODO: show error alert throw WalletBackendError.deserializationError } guard let (timeSent, completion) = completions[requestId] else { - symLog.log("requestId \(requestId) not in list") // TODO: .error + logger.error("requestId \(requestId, privacy: .public) not in list") + // TODO: show error alert throw WalletBackendError.deserializationError } completions[requestId] = nil if let walletError = decoded.error { // wallet-core sent an error message do { let jsonData = try JSONEncoder().encode(walletError) - // TODO: Error handling } catch { // JSON encoding of response.result failed / should never happen - symLog.log(walletError) // TODO: .error + symLog.log(decoded) + logger.error("cannot encode wallet-core Error") + // TODO: show error alert completion(requestId, timeSent, nil, WalletCore.parseFailureError()) } - // TODO: decode jsonData to WalletBackendResponseError - or HTTPError + logger.error("wallet-core sent back an error for request \(requestId, privacy: .public)") // completion(requestId, timeSent, nil, walletError) completion(requestId, timeSent, nil, WalletCore.parseFailureError()) } else { // JSON decoding of error message failed @@ -115,21 +118,23 @@ extension WalletCore { } private func handleResponse(_ decoded: ResponseOrNotification) throws { guard let requestId = decoded.id else { + logger.error("didn't find requestId in response") symLog.log(decoded) // TODO: .error throw WalletBackendError.deserializationError } guard let (timeSent, completion) = completions[requestId] else { - symLog.log("requestId \(requestId) not in list") // TODO: .error + logger.error("requestId \(requestId, privacy: .public) not in list") throw WalletBackendError.deserializationError } completions[requestId] = nil guard let result = decoded.result else { - symLog.log("requestId \(requestId) got no result") // TODO: .error + logger.error("requestId \(requestId, privacy: .public) got no result") throw WalletBackendError.deserializationError } do { let jsonData = try JSONEncoder().encode(result) - symLog.log(result) // TODO: .info + symLog.log(result) +// logger.info(result) TODO: log result completion(requestId, timeSent, jsonData, nil) } catch { // JSON encoding of response.result failed / should never happen symLog.log(result) // TODO: .error @@ -149,7 +154,7 @@ extension WalletCore { Task { if let userInfo { symLog.log(userInfo) } else { symLog.log(aName) } await postNotificationM(aName, object: anObject, userInfo: userInfo) - symLog.log("sent " + aName.rawValue) + logger.log("Notification sent: \(aName.rawValue)") } } @@ -174,8 +179,7 @@ extension WalletCore { // symLog.log("\(pendingOp): \(id)") } else { // TODO: handle other pending-operation-processed -print("\n❗️ \(pendingOp): \(id)\n") // this is a new pendingOp I haven't seen before - print("\n") + logger.log("❗️ \(pendingOp, privacy: .public): \(id, privacy: .public)") // this is a new pendingOp I haven't seen before } } private func handleStateTransition(_ jsonData: Data) throws { @@ -298,16 +302,17 @@ print("\n❗️ WalletCore.swift:226 Notification: ", anyPayload, "\n") / let now = Date.now do { let full = FullRequest(operation: request.operation, id: requestId, args: request.args) -// symLog.log(full) +// symLog.log(full) let encoded = try JSONEncoder().encode(full) guard let jsonString = String(data: encoded, encoding: .utf8) else { throw WalletBackendError.serializationError } self.completions[requestId] = (now, completionHandler) self.requestsMade += 1 self.semaphore.signal() // free requestsMade - self.symLog.log(jsonString) + self.symLog.log(jsonString) self.quickjs.sendMessage(message: jsonString) } catch { // call completion self.semaphore.signal() + self.symLog.log(error) completionHandler(requestId, now, nil, WalletCore.serializeRequestError()); } } diff --git a/TalerWallet1/Controllers/DebugViewC.swift b/TalerWallet1/Controllers/DebugViewC.swift @@ -21,6 +21,7 @@ */ import SwiftUI import SymLog +import os.log // Numbering Scheme for Views // MARK: - Main View @@ -97,8 +98,8 @@ struct DebugViewV: View { var body: some View { #if DEBUG - let _ = Self._printChanges() - let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear +// let _ = Self._printChanges() +// let _ = symLog.vlog() // just to get the # to compare it with .onAppear & onDisappear #endif let viewIDString = debugViewC.viewID > 0 ? String(debugViewC.viewID) : "" @@ -118,6 +119,7 @@ class DebugViewC: ObservableObject { private let symLog = SymLogC(0) // 0 to switch off viewID change logging public static let shared = DebugViewC() @AppStorage("developerMode") var developerMode: Bool = false + let logger = Logger (subsystem: "net.taler.gnu", category: "DebugView") @Published var viewID: Int = 0 @Published var sheetID: Int = 0 @@ -126,19 +128,24 @@ class DebugViewC: ObservableObject { if developerMode { if viewID == 0 { symLog.log("switching ON, \(newID)") + logger.info("switching ON, \(newID, privacy: .public)") viewID = newID // publish ON } else if viewID != newID { symLog.log("switching from \(viewID) to \(newID)") + logger.info("switching from \(self.viewID, privacy: .public) to \(newID, privacy: .public)") viewID = newID // publish new viewID } else { symLog.log("\(newID) stays") + logger.info("\(newID, privacy: .public) stays") // don't set viewID to the same value, it would just trigger an unneccessary redraw } } else if viewID > 0 { symLog.log("switching OFF, will not use \(newID)") + logger.info("switching OFF, will not use \(newID, privacy: .public)") viewID = 0 // publish OFF } else { symLog.log("off, will not use \(newID)") + logger.info("off, will not use \(newID, privacy: .public)") // don't set viewID from 0 to 0 again, it would just trigger an unneccessary redraw } } @@ -147,17 +154,21 @@ class DebugViewC: ObservableObject { if developerMode { if sheetID != newID { symLog.log("switching from \(sheetID) to \(newID)") + logger.info("switching from \(self.sheetID, privacy: .public) to \(newID, privacy: .public) for sheet") sheetID = newID // publish new sheetID } else { symLog.log("\(newID) stays") + logger.info("\(newID, privacy: .public) stays for sheet") // don't set sheetID to the same value, it would just trigger an unneccessary redraw } } else if sheetID > 0 { // might happen after switching DevMode off, if sheetID still has the old value of the last sheet symLog.log("switching OFF, will not use \(newID)") + logger.info("switching OFF, will not use \(newID, privacy: .public) for sheet") sheetID = 0 // publish OFF } else { symLog.log("off, will not use \(newID)") + logger.info("off, will not use \(newID, privacy: .public) for sheet") // don't set sheetID from 0 to 0 again, it would just trigger an unneccessary redraw } } diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift b/TalerWallet1/Controllers/TalerWallet1App.swift @@ -11,6 +11,7 @@ import BackgroundTasks import SwiftUI import SymLog +import os.log @main struct TalerWallet1App: App { @@ -24,9 +25,10 @@ struct TalerWallet1App: App { private let controller = Controller.shared private let model = WalletModel.shared private let debugViewC = DebugViewC.shared + let logger = Logger (subsystem: "net.taler.gnu", category: "Main App") func scheduleAppRefresh() { - let request = BGAppRefreshTaskRequest(identifier: "net.taler.refresh") + let request = BGAppRefreshTaskRequest(identifier: "net.taler.gnu.refresh") request.earliestBeginDate = .now.addingTimeInterval(24 * 3600) try? BGTaskScheduler.shared.submit(request) } @@ -42,33 +44,31 @@ struct TalerWallet1App: App { /// we handle them in .onOpenURL in MainView.swift .handlesExternalEvents(preferring: ["*"], allowing: ["*"]) .task { - symLog.log("task -> initWalletCore") - try! await controller.initWalletCoreM(model) // will (and should) crash on failure - symLog.log("task -> initWalletCore done") + try! await controller.initWalletCore(model) // will (and should) crash on failure } .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification, object: nil)) { _ in - symLog.log("❗️App Did Become Active notification") + logger.log("❗️App Did Become Active") } .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification, object: nil)) { _ in - symLog.log("❗️App Will Resign notification") + logger.log("❗️App Will Resign") isActive = false } .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification, object: nil)) { _ in - symLog.log("❗️App Will Enter Foreground notification") + logger.log("❗️App Will Enter Foreground") isActive = true } .onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification, object: nil)) { _ in - symLog.log("❗️App Will Terminate notification") + logger.log("❗️App Will Terminate") } } .onChange(of: phase) { newPhase in switch newPhase { case .active: - symLog.log("❗️ .onChange(of: phase) ==> newPhase: Active") + logger.log("❗️.onChange() ==> Active") case .background: - symLog.log("❗️ .onChange(of: phase) ==> newPhase: Background)") - scheduleAppRefresh() + logger.log("❗️.onChange() ==> Background)") +// scheduleAppRefresh() default: break } } @@ -101,9 +101,10 @@ struct TalerWallet1App: App { final class ViewState : ObservableObject { static let shared = ViewState() @Published var rootViewId = UUID() + let logger = Logger (subsystem: "net.taler.gnu", category: "ViewState") public func popToRootView() -> Void { - let _ = symLog() + logger.info("popToRootView") rootViewId = UUID() // setting a new ID will cause tableView popToRootView behaviour } diff --git a/TalerWallet1/Model/Model+Balances.swift b/TalerWallet1/Model/Model+Balances.swift @@ -47,7 +47,6 @@ extension WalletModel { async -> [Balance] { // M for MainActor do { let request = Balances() - logger.info("balancesM") let response = try await sendRequest(request, ASYNCDELAY) return response.balances // trigger view update in BalancesListView } catch { diff --git a/TalerWallet1/Model/Model+Exchange.swift b/TalerWallet1/Model/Model+Exchange.swift @@ -81,14 +81,12 @@ fileprivate struct AddExchange: WalletBackendFormattedRequest { extension WalletModel { /// ask wallet-core for its list of known exchanges @MainActor func listExchangesM() - async -> [Exchange] { // M for MainActor + async -> [Exchange] { // M for MainActor do { let request = ListExchanges() - logger.info("ListExchanges") let response = try await sendRequest(request, ASYNCDELAY) return response.exchanges } catch { - logger.error("listExchangesM failed: \(error)") return [] // empty, but not nil } } @@ -96,9 +94,8 @@ extension WalletModel { /// add a new exchange with URL to the wallet's list of known exchanges func addExchange(url: String) async throws { - symLog?.log("adding exchange: \(url)") // TODO: .notice let request = AddExchange(exchangeBaseUrl: url) - logger.info("adding exchange: \(url, privacy: .public)") + logger.info("adding exchange: \(url, privacy: .public)") _ = try await sendRequest(request) } } diff --git a/TalerWallet1/Model/WalletModel.swift b/TalerWallet1/Model/WalletModel.swift @@ -4,7 +4,7 @@ */ import Foundation import SymLog -import os +import os.log fileprivate let DATABASE = "talerwalletdb-v30" fileprivate let ASYNCDELAY: UInt = 0 //set e.g to 6 or 9 seconds for debugging @@ -16,31 +16,28 @@ class WalletModel: ObservableObject { public static let shared = WalletModel(0) static func className() -> String {"\(self)"} var symLog: SymLogC? - let logger = Logger (subsystem: "net.taler.gnu", category: "wallet-core") init(_ symbol: Int) { // init with 0 to disable logging for this class self.symLog = SymLogC(symbol == 0 ? 0 : -1, funcName: Self.className()) } + let logger = Logger (subsystem: "net.taler.gnu", category: "WalletModel") func sendRequest<T: WalletBackendFormattedRequest> (_ request: T, _ delay: UInt = 0) async throws -> T.Response { // T for any Thread - symLog?.log("sending: \(request)") + logger.log("sending: \(request.operation(), privacy: .public)") let sendTime = Date.now do { let (response, id) = try await WalletCore.shared.sendFormattedRequest(request: request) let timeUsed = Date.now - sendTime + logger.log("received: \(request.operation(), privacy: .public) (\(id, privacy: .public)) after \(timeUsed.milliseconds, privacy: .public) ms") let asyncDelay: UInt = delay > 0 ? delay : UInt(ASYNCDELAY) if asyncDelay > 0 { // test LoadingView, sleep some seconds - symLog?.log("received: (\(id)) after \(timeUsed.milliseconds) ms, going to sleep for \(asyncDelay) seconds...") try? await Task.sleep(nanoseconds: 1_000_000_000 * UInt64(asyncDelay)) - symLog?.log("(\(id)) waking up again after \(asyncDelay) seconds, will deliver \(response)") - } else { - symLog?.log("received: (\(id)) after \(timeUsed.milliseconds) ms, \(response)") } return response } catch { // rethrows let timeUsed = Date.now - sendTime - symLog?.log("Yikes❗️ \(request.operation()) failed after \(timeUsed.milliseconds) ms") + logger.error("\(request.operation(), privacy: .public) failed after \(timeUsed.milliseconds, privacy: .public) ms\n\(error)") throw error } } @@ -106,16 +103,11 @@ fileprivate struct InitRequest: WalletBackendFormattedRequest { extension WalletModel { /// initalize Wallet-Core. Will do networking func initWalletCoreT() async throws -> VersionInfo { - do { // T for any Thread - let docPath = try docPath() - let request = InitRequest(persistentStoragePath: docPath) - logger.info("initWalletCoreT") - let response = try await sendRequest(request, 0) // no Delay - return response.versionInfo - } catch { - logger.error("initWalletCoreT failed: \(error)") - throw error - } + // T for any Thread + let docPath = try docPath() + let request = InitRequest(persistentStoragePath: docPath) + let response = try await sendRequest(request, 0) // no Delay + return response.versionInfo } private func docPath () throws -> String { @@ -128,7 +120,6 @@ extension WalletModel { logger.debug("\(docPath)") return docPath } else { // should never happen - symLog?.log("Yikes❗️ documentURLs empty") logger.error("documentURLs empty") throw WalletBackendError.initializationError } diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift b/TalerWallet1/Views/Balances/BalancesListView.swift @@ -95,7 +95,7 @@ struct BalancesListView: View { Sheet(sheetView: sheet) } // sheet .task { - symLog.log(".task fetchBalancesM") + symLog.log(".task getBalances") await reloadAction() } // task } diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift b/TalerWallet1/Views/Balances/BalancesSectionView.swift @@ -190,12 +190,15 @@ struct BalancesSectionView: View { }.id(sectionID) .task { if shownSectionID != sectionID { - symLog.log("task for BalancesSectionView \(sectionID)") let response = await model.fetchTransactionsT(currency: currency) + symLog.log("task for BalancesSectionView \(sectionID) - reload Transactions") transactions = response pendingTransactions = WalletModel.pendingTransactions(response) uncompletedTransactions = WalletModel.uncompletedTransactions(response) shownSectionID = sectionID + } else { + symLog.log("task for BalancesSectionView \(sectionID) ❗️ skip reloading Transactions") + } } } // body