diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift index 3cd7ea4b50..98bc300fb9 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift @@ -406,7 +406,9 @@ public class KeymanPackage { } } } catch { - log.error("error parsing keyman package: \(error)") + // It's not an app or engine error when the package itself is invalid. + // Definitely worth noting, though. + SentryManager.breadcrumbAndLog("error parsing keyman package: \(error)", sentryLevel: .error) } return nil diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 585ebca500..3904aef79d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -343,7 +343,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if !Migrations.resourceHasPackageMetadata(keyboard) { let wrappedKbds = Migrations.migrateToKMPFormat([keyboard]) guard wrappedKbds.count == 1 else { - log.error("Could not properly import keyboard") + SentryManager.captureAndLog("Could not properly import keyboard") return } kbdToInstall = wrappedKbds[0] @@ -407,7 +407,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if !Migrations.resourceHasPackageMetadata(lexicalModel) { let wrappedModels = Migrations.migrateToKMPFormat([lexicalModel]) guard wrappedModels.count == 1 else { - log.error("Could not properly import lexical model") + SentryManager.captureAndLog("Could not properly import lexical model") return } modelToInstall = wrappedModels[0] @@ -459,12 +459,12 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { // rather than just 'no matching keyboards'. let keyboardDir = Storage.active.resourceDir(for: kb)! FontManager.shared.unregisterFonts(in: keyboardDir, fromSystemOnly: false) - log.info("Deleting directory \(keyboardDir)") + SentryManager.breadcrumbAndLog("Deleting directory \(keyboardDir)") if (try? FileManager.default.removeItem(at: keyboardDir)) == nil { - log.error("Failed to delete \(keyboardDir)") + SentryManager.captureAndLog("Failed to delete \(keyboardDir) when removing keyboard") } } else { - log.info("User has another language installed. Skipping delete of keyboard files.") + SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of keyboard files.") } NotificationCenter.default.post(name: Notifications.keyboardRemoved, object: self, value: kb) @@ -541,12 +541,12 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if !userLexicalModels.contains(where: { $0.id == lm.id }) { let lexicalModelDir = Storage.active.resourceDir(for: lm)! FontManager.shared.unregisterFonts(in: lexicalModelDir, fromSystemOnly: false) - log.info("Deleting directory \(lexicalModelDir)") + SentryManager.breadcrumbAndLog("Deleting directory \(lexicalModelDir)") if (try? FileManager.default.removeItem(at: lexicalModelDir)) == nil { - log.error("Failed to delete \(lexicalModelDir)") + SentryManager.captureAndLog("Failed to delete \(lexicalModelDir) when removing lexical model") } } else { - log.info("User has another language installed. Skipping delete of lexical model files.") + SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of lexical model files.") } NotificationCenter.default.post(name: Notifications.lexicalModelRemoved, object: self, value: lm) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift index 553c69d84e..86b068ab29 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift @@ -531,11 +531,11 @@ public class ResourceDownloadManager { // The reason we're deprecating it; only returns the first model, even if more language pairings are installed. return package.installables[0][0] } else { - log.error("Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)") + SentryManager.captureAndLog("Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)") return nil } } catch { - log.error("Error occurred while attempting to install package from \(packageURL): \(String(describing: error))") + SentryManager.captureAndLog(error, message: "Error occurred while attempting to install package from \(packageURL): \(String(describing: error))") return nil } } @@ -560,7 +560,7 @@ public class ResourceDownloadManager { try handler?(package, error) self.resourceDownloadCompleted(with: package) } catch { - log.error("Unhandled error occurred after resource successfully downloaded: \(String(describing: error))") + SentryManager.captureAndLog(error, message: "Unhandled error occurred after resource successfully downloaded: \(String(describing: error))") self.resourceDownloadFailed(withKey: packageKey, with: error) } @@ -615,7 +615,7 @@ public class ResourceDownloadManager { if let package = package { do { try ResourceFileManager.shared.install(resourceWithID: fullID, from: package) - log.info("succesfully parsed the keyboard in: \(package.sourceFolder)") + SentryManager.breadcrumbAndLog("successfully parsed the keyboard in: \(package.sourceFolder)") // Maintains legacy behavior; automatically sets the newly-downloaded keyboard as active. if let keyboard = package.findResource(withID: fullID) { @@ -626,12 +626,13 @@ public class ResourceDownloadManager { self.downloadLexicalModelsForLanguageIfExists(languageID: fullID.languageID) } } catch { - log.error("Keyboard installation error: \(String(describing: error))") + SentryManager.captureAndLog(error, message: "Keyboard installation error: \(String(describing: error))") } } else if let error = error { + // Often a download error. log.error("Installation failed: \(String(describing: error))") } else { - log.error("Unknown error when attempting to install \(fullID.description))") + SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description))") } } } @@ -642,18 +643,18 @@ public class ResourceDownloadManager { do { // A raw port of the queue's old installation method for lexical models. try ResourceFileManager.shared.finalizePackageInstall(package, isCustom: false) - log.info("successfully parsed the lexical model in: \(package.sourceFolder)") + SentryManager.breadcrumbAndLog("successfully parsed the lexical model in: \(package.sourceFolder)") if let installedLexicalModel = package.findResource(withID: fullID) { _ = Manager.shared.registerLexicalModel(installedLexicalModel) } } catch { - log.error("Error installing the lexical model: \(String(describing: error))") + SentryManager.captureAndLog(error, message: "Error installing the lexical model: \(String(describing: error))") } } else if let error = error { log.error("Error downloading the lexical model \(String(describing: error))") } else { - log.error("Unknown error when attempting to install \(fullID.description)") + SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description)") } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift index d719340f6a..55b1809519 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift @@ -279,7 +279,7 @@ class ResourceDownloadQueue: HTTPDownloadDelegate { do { try reachability = Reachability(hostname: KeymanHosts.API_KEYMAN_COM.host!) } catch { - log.error("Could not start Reachability object: \(error)") + SentryManager.captureAndLog("Could not start Reachability object: \(error)") } self.session = session diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift index 572afce019..79b1b5ff4c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift @@ -125,7 +125,7 @@ public class ResourceFileManager { try copyWithOverwrite(from: url, to: destinationUrl) return destinationUrl } catch { - log.error(error) + SentryManager.captureAndLog(error) return nil } } @@ -367,7 +367,9 @@ public class ResourceFileManager { internal func addResource(_ resource: Resource) { let path = Storage.active.resourceURL(for: resource)!.path if !FileManager.default.fileExists(atPath: path) { - log.error("Could not add resource of type: \(resource.fullID.type) with ID: \(resource.id) because the resource file does not exist") + // Is 'internal' and only called after packages have been installed, + // thus when the files should already be in-place. + SentryManager.captureAndLog("Could not add resource of type: \(resource.fullID.type) with ID: \(resource.id) because the resource file does not exist") return } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index fca93e38df..e8f8c96c0b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -358,6 +358,7 @@ class LanguageSettingsViewController: UITableViewController { } else { let event = Sentry.Event(level: .debug) event.message = SentryMessage(formatted: "Keyboard index requested for uninstalled keyboard") + event.extra = ["id": matchingFullID] SentrySDK.capture(event: event) log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!") @@ -388,6 +389,7 @@ class LanguageSettingsViewController: UITableViewController { } else { let event = Sentry.Event(level: .debug) event.message = SentryMessage(formatted: "Keyboard index requested for uninstalled keyboard") + event.extra = ["id": matchingFullID] SentrySDK.capture(event: event) log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!")