From 6134dcb1fbe3d89c28602eefd70fc06c9d863e9a Mon Sep 17 00:00:00 2001 From: sgschantz Date: Wed, 17 Jan 2024 16:06:41 +0700 Subject: [PATCH 01/11] remove XCGLogger from Keyman, replacing with calls directly to os_log API --- .../KeymanEngine.xcodeproj/project.pbxproj | 4 +++ ios/engine/KMEI/KeymanEngineLogger.swift | 22 ++++++++++++ .../Keyman/Keyman.xcodeproj/project.pbxproj | 10 +++--- ios/keyman/Keyman/Keyman/AppDelegate.swift | 1 - .../InfoViewController.swift | 5 +-- .../MainViewController.swift | 3 +- .../SetUpViewController.swift | 3 +- ios/keyman/Keyman/Keyman/KeymanLogger.swift | 23 +++++++++++++ ios/keyman/Keyman/Keyman/Log.swift | 34 ------------------- .../ImageBannerViewController.swift | 4 ++- .../SWKeyboard/KeyboardViewController.swift | 1 - 11 files changed, 65 insertions(+), 45 deletions(-) create mode 100644 ios/engine/KMEI/KeymanEngineLogger.swift create mode 100644 ios/keyman/Keyman/Keyman/KeymanLogger.swift delete mode 100644 ios/keyman/Keyman/Keyman/Log.swift diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 374f08bdcc..68e21af380 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 1645D5972036C9F80076C51B /* KMPKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1645D5962036C9F80076C51B /* KMPKeyboard.swift */; }; 165EB3A12098993900040A69 /* KeyboardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3A02098993900040A69 /* KeyboardError.swift */; }; 296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */; }; + 29B30C232B564F9900C342A4 /* KeymanEngineLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */; }; 377D10DE26846B8900467431 /* SpacebarTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377D10DD26846B8900467431 /* SpacebarTextViewController.swift */; }; 6CD5DFAA150F6DC8007A5DDE /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA8150F6DC8007A5DDE /* icon.png */; }; 6CD5DFAB150F6DC8007A5DDE /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA9150F6DC8007A5DDE /* icon@2x.png */; }; @@ -327,6 +328,7 @@ 29B27FEF29062CF50036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/ResourceInfoView.strings; sourceTree = ""; }; 29B27FF029062D100036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; 29B27FF129062D190036917E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = ""; }; + 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanEngineLogger.swift; sourceTree = ""; }; 29BFA75E28293287009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ResourceInfoView.strings; sourceTree = ""; }; 29BFA75F282934B4009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 29BFA760282934BB009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -925,6 +927,7 @@ F243887314BBD43000A3E055 = { isa = PBXGroup; children = ( + 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */, C06D372C1F81F4E100F61AE0 /* KeymanEngine */, 9A079DD0223194B100581263 /* KeymanEngineTests */, CE1679B8265F315C008D6FCE /* Demos */, @@ -1525,6 +1528,7 @@ C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */, C0959CD41F99C44E00B616BC /* Constants.swift in Sources */, C0452BAD1F9F21270064431A /* Keyboard.swift in Sources */, + 29B30C232B564F9900C342A4 /* KeymanEngineLogger.swift in Sources */, CE79B24923C711FF007E72AE /* KeyboardScaleMap.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/engine/KMEI/KeymanEngineLogger.swift b/ios/engine/KMEI/KeymanEngineLogger.swift new file mode 100644 index 0000000000..34500609b3 --- /dev/null +++ b/ios/engine/KMEI/KeymanEngineLogger.swift @@ -0,0 +1,22 @@ +/** + * Keyman is copyright (C) SIL International. MIT License. + * + * KeymanEngineLogger.swift + * KeymanEngine + * + * Created by Shawn Schantz on 2024-01-16. + * + * Basic logging capability provided by wrapping Apple's Unified Logging Framework. + */ + +import Foundation +import os.log + +private let subsystem = "org.sil.KeymanEngine4Mac" + +public struct KeymanEngineLogger { + static let engine = OSLog(subsystem: subsystem, category: "engine") + static let migration = OSLog(subsystem: subsystem, category: "migration") + static let sentry = OSLog(subsystem: subsystem, category: "sentry") + static let settings = OSLog(subsystem: subsystem, category: "settings") +} diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index ddaa7a831c..96e8cf4b62 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 165EB39C2097181D00040A69 /* KMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB39B2097181D00040A69 /* KMView.swift */; }; 296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; }; 296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 29B30C212B56325D00C342A4 /* KeymanLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */; }; + 29CA9A622B4F9B0B00EAB099 /* KeymanLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */; }; 981AFA8F19EF44DE006706BF /* 724-info@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725518E4F0930014DF0C /* 724-info@2x.png */; }; 981AFA9619EF44DE006706BF /* MainViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98ABADCE176935E500B62590 /* MainViewController_iPhone.xib */; }; 981AFA9819EF44DE006706BF /* 724-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725418E4F0930014DF0C /* 724-info.png */; }; @@ -116,7 +118,6 @@ C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B01F60E8D400140735 /* GetStartedViewController.swift */; }; C055F0B31F60F7A400140735 /* DropDownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B21F60F7A400140735 /* DropDownView.swift */; }; C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055F0B41F610FB200140735 /* DropDownListView.swift */; }; - C059FCC01FD927EF00BD1A64 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C059FCBF1FD927EF00BD1A64 /* Log.swift */; }; C0A1A6311F5E4F4300EAF822 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6301F5E4F4300EAF822 /* InfoViewController.swift */; }; C0A1A6331F5E749000EAF822 /* SetUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6321F5E749000EAF822 /* SetUpViewController.swift */; }; C0A1A6351F5E786A00EAF822 /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6341F5E786A00EAF822 /* BookmarksViewController.swift */; }; @@ -215,6 +216,7 @@ 29BFA75D282931F8009FCCC3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 29BFA769282946EC009FCCC3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; 29C1E17028001EC200759EDE /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = ""; }; + 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeymanLogger.swift; sourceTree = ""; }; 37378F4222C9AA610043F22B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainViewController_iPad.xib; sourceTree = ""; }; 37378F4322C9AA630043F22B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainViewController_iPhone.xib; sourceTree = ""; }; 9815725018E4F0930014DF0C /* 702-share.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "702-share.png"; path = "Keyman Icons/702-share.png"; sourceTree = ""; }; @@ -319,7 +321,6 @@ C055F0B01F60E8D400140735 /* GetStartedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = GetStartedViewController.swift; sourceTree = ""; tabWidth = 2; }; C055F0B21F60F7A400140735 /* DropDownView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DropDownView.swift; sourceTree = ""; tabWidth = 2; }; C055F0B41F610FB200140735 /* DropDownListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = DropDownListView.swift; sourceTree = ""; tabWidth = 2; }; - C059FCBF1FD927EF00BD1A64 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; C0A1A6301F5E4F4300EAF822 /* InfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = ""; tabWidth = 2; }; C0A1A6321F5E749000EAF822 /* SetUpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SetUpViewController.swift; sourceTree = ""; tabWidth = 2; }; C0A1A6341F5E786A00EAF822 /* BookmarksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = BookmarksViewController.swift; sourceTree = ""; tabWidth = 2; }; @@ -568,8 +569,8 @@ 16A9229D20325253003CC98E /* Info.plist */, 162E2C9420926C8600F40769 /* Classes */, C0E943F91F6124E100E7D98C /* AppDelegate.swift */, - C059FCBF1FD927EF00BD1A64 /* Log.swift */, CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */, + 29CA9A612B4F9B0B00EAB099 /* KeymanLogger.swift */, ); path = Keyman; sourceTree = ""; @@ -1042,9 +1043,9 @@ buildActionMask = 2147483647; files = ( 165EB39C2097181D00040A69 /* KMView.swift in Sources */, + 29CA9A622B4F9B0B00EAB099 /* KeymanLogger.swift in Sources */, C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */, C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */, - C059FCC01FD927EF00BD1A64 /* Log.swift in Sources */, CE7FF1F0239A0293007859D9 /* PackageBrowserViewController.swift in Sources */, C0E943F81F61234C00E7D98C /* UIImage+Helpers.swift in Sources */, C0E943FE1F61377900E7D98C /* MainViewController.swift in Sources */, @@ -1070,6 +1071,7 @@ buildActionMask = 2147483647; files = ( CEF4E55A23E967140065B9C7 /* ImageBannerViewController.swift in Sources */, + 29B30C212B56325D00C342A4 /* KeymanLogger.swift in Sources */, C0FF769B1F5D368C00BD23C3 /* KeyboardViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index c92a85a5f8..9c03987338 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -60,7 +60,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { SentryManager.start() // Forces the logs to initialize, as their definitions result in lazy init. // These references have been configured to also log app details. - _ = log _ = KeymanEngine.log // In iOS 15, navigation bars become transparent by default when the edge diff --git a/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift b/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift index 5ce4a5345f..923bcab84a 100644 --- a/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift @@ -9,6 +9,7 @@ import KeymanEngine import WebKit import Reachability +import os class InfoViewController: UIViewController, WKNavigationDelegate { @IBOutlet var webView: WKWebView! @@ -67,7 +68,7 @@ class InfoViewController: UIViewController, WKNavigationDelegate { func webView(_ webView: WKWebView, didFailNavigation error: Error) { UIApplication.shared.isNetworkActivityIndicatorVisible = false updateButtons() - log.debug(error) + os_log("%s", log: KeymanLogger.ui, type: .error, error.localizedDescription) } @IBAction func back(_ sender: Any) { @@ -100,6 +101,6 @@ class InfoViewController: UIViewController, WKNavigationDelegate { let appVersion = Version.current.majorMinor let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)/" webView.load(URLRequest(url: URL(string: url)!)) - log.debug("Info page URL: \(url)") + os_log("Info page URL: %s", log: KeymanLogger.ui, type: .debug, url) } } diff --git a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift index 56673bfa3f..2bb26f0863 100644 --- a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift @@ -9,6 +9,7 @@ import KeymanEngine import UIKit import QuartzCore +import os // Internal strings private let baseUri = "https://r.keymanweb.com/20/fonts/get_mobileconfig.php?id=" @@ -191,7 +192,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg userData.set(self.textView?.text, forKey: userTextKey) userData.set(self.textSize.description, forKey: userTextSizeKey) userData.synchronize() - log.debug("saving text size: \(textSize.description).") + os_log("saving text size: %s", log: KeymanLogger.settings, type: .debug, textSize.description) } private func calculateDefaultTextSize() -> CGFloat { diff --git a/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift b/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift index cd124eba79..9ce55088dd 100644 --- a/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift @@ -9,6 +9,7 @@ import KeymanEngine import WebKit import Reachability +import os // TODO: Refactor common functionality from InfoViewController class SetUpViewController: UIViewController, WKNavigationDelegate { @@ -74,6 +75,6 @@ class SetUpViewController: UIViewController, WKNavigationDelegate { let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)" + "/start/installing-system-keyboard?embed=ios" webView.load(URLRequest(url: URL(string: url)!)) - log.debug("Set up page URL: \(url)") + os_log("Set up page URL: %s", log: KeymanLogger.resources, type: .debug, url) } } diff --git a/ios/keyman/Keyman/Keyman/KeymanLogger.swift b/ios/keyman/Keyman/Keyman/KeymanLogger.swift new file mode 100644 index 0000000000..b1ff40e8d6 --- /dev/null +++ b/ios/keyman/Keyman/Keyman/KeymanLogger.swift @@ -0,0 +1,23 @@ +/** + * Keyman is copyright (C) SIL International. MIT License. + * + * KeymanLogger.swift + * Keyman + * + * Created by Shawn Schantz on 2024-01-11. + * + * Basic logging capability provided by wrapping Apple's Unified Logging Framework. + * Works with iOS 10 and later. + * Each time we call the `os_log` API + */ + +import Foundation +import os.log + +private let subsystem = "keyman.inputmethod.Keyman" + +public struct KeymanLogger { + static let ui = OSLog(subsystem: subsystem, category: "ui") + static let resources = OSLog(subsystem: subsystem, category: "resources") + static let settings = OSLog(subsystem: subsystem, category: "settings") +} diff --git a/ios/keyman/Keyman/Keyman/Log.swift b/ios/keyman/Keyman/Keyman/Log.swift deleted file mode 100644 index c074921570..0000000000 --- a/ios/keyman/Keyman/Keyman/Log.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// Log.swift -// Keyman -// -// Created by Gabriel Wong on 2017-12-07. -// Copyright © 2017 SIL International. All rights reserved. -// - -import XCGLogger - -// From XCGLogger docs: -// Note: This creates the log object lazily, which means it's not created until it's actually needed. -public let log: XCGLogger = { - // Default: the 'console', which is read by Xcode but doesn't reach the system logs. - let mainLog = XCGLogger(identifier: "Keyman", includeDefaultDestinations: false) - - // Ensures our log messages go out to the device's system log as well as the console. - let systemLogDest = AppleSystemLogDestination(identifier: "") - systemLogDest.showLogIdentifier = true - - mainLog.add(destination: systemLogDest) - - // Temporary logging level to ensure that app details are reported properly. - mainLog.outputLevel = .info - mainLog.logAppDetails() - -#if DEBUG - mainLog.outputLevel = .debug -#else - mainLog.outputLevel = .warning -#endif - - return mainLog -}() diff --git a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift index 76692b6761..ae6a0ac385 100644 --- a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import KeymanEngine // for log commands +import os /** * Takes in a XIB spec for an image banner and makes it renderable, applying size constraints when rendered @@ -49,7 +50,8 @@ class ImageBannerViewController: UIViewController { let frame = CGRect(origin: self.view.frame.origin, size: size) - log.debug("Rendering banner image of size \(size)") + os_log("Rendering banner image of size %s", log: KeymanLogger.ui, type: .info, NSCoder.string(for: size)) + self.view.frame = frame widthConstraint?.constant = size.width heightConstraint?.constant = size.height diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index 58b0654418..1a97abc1d1 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -21,7 +21,6 @@ class KeyboardViewController: InputViewController { // is enabled. They seem to get blocked otherwise, except in the Simulator. SentryManager.start(sendingEnabled: true) } - _ = log _ = KeymanEngine.log Manager.applicationGroupIdentifier = "group.KM4I" From 542dc83b4445216ef417204ade551b180509b3df Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 10:47:54 +0700 Subject: [PATCH 02/11] remove XCGLogger dependency from Keyman Engine --- .../KMEI/KeymanEngine/Classes/Alerts.swift | 5 +- .../Classes/Errors/SentryManager.swift | 47 ++---- .../Extension/UserDefaults+Types.swift | 33 +++- .../KeymanEngine/Classes/HTTPDownloader.swift | 9 +- .../PackageInstallViewController.swift | 3 +- .../Keyboard/InputViewController.swift | 7 +- .../Classes/Keyboard/KeyboardScaleMap.swift | 15 +- .../Keyboard/KeymanWebViewController.swift | 102 ++++++++---- .../Classes/KeyboardKeymanPackage.swift | 5 +- .../KeymanEngine/Classes/KeymanPackage.swift | 10 +- .../Classes/KeymanURLProtocol.swift | 3 +- .../LanguageLMDetailViewController.swift | 15 +- .../KeyboardSwitcherViewController.swift | 3 +- .../Classes/LexicalModelKeymanPackage.swift | 5 +- .../LexicalModelPickerViewController.swift | 25 +-- .../KMEI/KeymanEngine/Classes/Manager.swift | 83 +++++++--- .../NotificationCenter+Typed.swift | 5 +- .../Queries/Queries+LexicalModel.swift | 5 +- .../Queries/Queries+PackageVersion.swift | 5 +- .../Classes/Resource Data/FontManager.swift | 28 ++-- .../Resource Data/InstallableKeyboard.swift | 5 +- .../Resource Management/Migrations.swift | 153 +++++++++++++----- .../ResourceDownloadManager.swift | 58 +++++-- .../ResourceDownloadQueue.swift | 5 +- .../ResourceFileManager.swift | 36 +++-- .../Classes/Resource Management/Storage.swift | 29 +++- .../Classes/ResourceInfoViewController.swift | 6 +- .../InstalledLanguagesViewController.swift | 26 +-- .../LanguageSettingsViewController.swift | 25 ++- .../Classes/SettingsViewController.swift | 42 +++-- .../KMEI/KeymanEngine/Classes/TextField.swift | 20 ++- .../KMEI/KeymanEngine/Classes/TextView.swift | 20 ++- ios/engine/KMEI/KeymanEngineLogger.swift | 21 ++- .../KeyboardScaleTests.swift | 4 +- .../KeymanPackageTests.swift | 2 - .../ResourceUpdateTests.swift | 3 +- .../TestUtils/EngineStateBundler.swift | 13 +- .../TestUtils/TestUtils.swift | 11 +- ios/keyman/Keyman/Keyman/AppDelegate.swift | 9 +- .../InfoViewController.swift | 8 +- .../MainViewController.swift | 2 +- .../SetUpViewController.swift | 6 +- ios/keyman/Keyman/Keyman/KeymanLogger.swift | 20 ++- .../ImageBannerViewController.swift | 2 +- .../SWKeyboard/KeyboardViewController.swift | 5 +- 45 files changed, 637 insertions(+), 307 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Alerts.swift b/ios/engine/KMEI/KeymanEngine/Classes/Alerts.swift index ce5dfd32bd..345eeb9312 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Alerts.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Alerts.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import Reachability +import os.log open class Alerts { public typealias AcceptanceHandler = ((UIAlertAction)) -> Void @@ -48,8 +49,8 @@ open class Alerts { do { try networkReachable = Reachability(hostname: KeymanHosts.KEYMAN_COM.host!) } catch { - log.debug("reachability could not start") - } + os_log("Reachability could not start", log:KeymanEngineLogger.ui, type: .debug) + } if networkReachable?.connection == Reachability.Connection.unavailable || networkReachable == nil { showConnectionErrorAlert(in: vc, handler: handler) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index 94f9885076..69bb9d4662 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -7,8 +7,8 @@ // import Foundation -import XCGLogger import Sentry +import os.log /** * This class centralizes the methods used among the KeymanEngine library, Keyman app, and SWKeyboard app-ex for Sentry-based @@ -26,10 +26,10 @@ public class SentryManager { #if NO_SENTRY // If doing development debugging (and NOT for Sentry code), silence Sentry reporting. let allowEnabled = false - log.debug("Sentry error logging disabled for development mode.") + os_log("Sentry error logging disabled for development mode.", log: KeymanEngineLogger.settings, type: .debug) #else let allowEnabled = true - log.debug("Sentry error logging enabled.") + os_log("Sentry error logging enabled.", log: KeymanEngineLogger.sentry, type: .debug) #endif let infoDict = Bundle(for: SentryManager.self).infoDictionary @@ -90,25 +90,6 @@ public class SentryManager { #endif } - private static func mapLoggingLevel(_ level: Sentry.SentryLevel) -> XCGLogger.Level { - switch(level) { - case .none: - return XCGLogger.Level.none - case .debug: - return .debug - case .info: - return .info - case .warning: - return .warning - case .error: - return .error - case .fatal: - return .severe - default: - return .info - } - } - /** * Captures a Sentry event and copies its message to the engine's logging mechanism. * If the logging level is not specified, the Sentry event's log-level will be used as a default. @@ -116,14 +97,11 @@ public class SentryManager { * Will safely bypass the Sentry component if not activated by the app, only logging the * message in such scenarios. */ - public static func captureAndLog(_ event: Sentry.Event, logLevel: XCGLogger.Level? = nil) { + public static func capture(_ event: Sentry.Event) { // Guarded in case a library consumer decides against initializing Sentry. if _started { SentrySDK.capture(event: event) } - - let level = logLevel ?? mapLoggingLevel(event.level) - log.logln(event.message?.formatted, level: level) } /** @@ -133,14 +111,14 @@ public class SentryManager { * Will safely bypass the Sentry component if not activated by the app, only logging the * message in such scenarios. */ - public static func captureAndLog(_ error: Error, message: String? = nil, sentryLevel: Sentry.SentryLevel = .error, logLevel: XCGLogger.Level? = nil) { + public static func capture(_ error: Error, message: String? = nil, sentryLevel: Sentry.SentryLevel = .error) { let event = Sentry.Event(error: error) event.level = sentryLevel if let message = message { event.message = SentryMessage(formatted: message) } - self.captureAndLog(event, logLevel: logLevel) + self.capture(event) } /** @@ -150,11 +128,11 @@ public class SentryManager { * Will safely bypass the Sentry component if not activated by the app, only logging the * message in such scenarios. */ - public static func captureAndLog(_ message: String, sentryLevel: Sentry.SentryLevel = .error, logLevel: XCGLogger.Level? = nil) { + public static func capture(_ message: String, sentryLevel: Sentry.SentryLevel = .error) { let event = Sentry.Event(level: sentryLevel) event.message = SentryMessage(formatted: message) - self.captureAndLog(event, logLevel: logLevel) + self.capture(event) } /** @@ -164,14 +142,11 @@ public class SentryManager { * Will safely bypass the Sentry component if not activated by the app, only logging the * message in such scenarios. */ - public static func breadcrumbAndLog(crumb: Sentry.Breadcrumb, logLevel: XCGLogger.Level? = nil) { + public static func breadcrumb(crumb: Sentry.Breadcrumb) { // Guarded in case a library consumer decides against initializing Sentry. if _started { SentrySDK.addBreadcrumb(crumb) } - - let level = logLevel ?? mapLoggingLevel(crumb.level) - log.logln(crumb.message, level: level) } /** @@ -181,7 +156,7 @@ public class SentryManager { * Will safely bypass the Sentry component if not activated by the app, only logging the * message in such scenarios. */ - public static func breadcrumbAndLog(_ message: String, category: String? = nil, sentryLevel: Sentry.SentryLevel = .info, logLevel: XCGLogger.Level? = nil) { + public static func breadcrumb(_ message: String, category: String? = nil, sentryLevel: Sentry.SentryLevel = .info) { let crumb = Sentry.Breadcrumb() crumb.level = sentryLevel if let category = category { @@ -189,7 +164,7 @@ public class SentryManager { } crumb.message = message - self.breadcrumbAndLog(crumb: crumb, logLevel: logLevel) + self.breadcrumb(crumb: crumb) } public static func forceError() { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift index 7ed5ad93eb..d4912069b3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log public extension UserDefaults { func installableKeyboards(forKey key: String) -> [InstallableKeyboard]? { @@ -18,7 +19,9 @@ public extension UserDefaults { do { return try array.map { try decoder.decode(InstallableKeyboard.self, from: $0) } } catch { - SentryManager.captureAndLog(error, message: "Error decoding keyboards: \(error)") + let message = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) return nil } } @@ -31,7 +34,9 @@ public extension UserDefaults { do { return try array.map { try decoder.decode(InstallableLexicalModel.self, from: $0) } } catch { - SentryManager.captureAndLog(error, message: "Error decoding lexical models: \(error)") + let message = "Error decoding lexical models: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) return nil } } @@ -61,7 +66,9 @@ public extension UserDefaults { let array = try keyboards.map { try encoder.encode($0) } set(array, forKey: key) } catch { - SentryManager.captureAndLog(error, message: "Error encoding keyboards: \(error)") + let message = "Error encoding keyboards: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -75,7 +82,9 @@ public extension UserDefaults { let array = try lexicalModels.map { try encoder.encode($0) } set(array, forKey: key) } catch { - SentryManager.captureAndLog(error, message: "Error encoding lexicalModels: \(error)") + let message = "Error encoding lexicalModels: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -86,7 +95,9 @@ public extension UserDefaults { do { return try PropertyListDecoder().decode(FullKeyboardID.self, from: data) } catch { - SentryManager.captureAndLog(error, message: "Error decoding FullKeyboardID: \(error)") + let message = "Error decoding FullKeyboardID: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) return nil } } @@ -100,7 +111,9 @@ public extension UserDefaults { let data = try PropertyListEncoder().encode(id) set(data, forKey: key) } catch { - SentryManager.captureAndLog(error, message: "Error encoding FullKeyboardID: \(error)") + let message = "Error encoding FullKeyboardID: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -111,7 +124,9 @@ public extension UserDefaults { do { return try PropertyListDecoder().decode(FullLexicalModelID.self, from: data) } catch { - SentryManager.captureAndLog(error, message: "Error decoding FullLexicalModelID: \(error)") + let message = "Error decoding FullLexicalModelID: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) return nil } } @@ -125,7 +140,9 @@ public extension UserDefaults { let data = try PropertyListEncoder().encode(id) set(data, forKey: key) } catch { - SentryManager.captureAndLog(error, message: "Error encoding FullLexicalModelID: \(error)") + let message = "Error encoding FullLexicalModelID: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message: message) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift b/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift index df2cc7fd02..09f81762f8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/HTTPDownloader.swift @@ -7,8 +7,9 @@ // import Foundation +import os.log -protocol HTTPDownloadDelegate: class { +protocol HTTPDownloadDelegate: AnyObject { func downloadRequestStarted(_ request: HTTPDownloadRequest) func downloadRequestFinished(_ request: HTTPDownloadRequest) func downloadRequestFailed(_ request: HTTPDownloadRequest, with: Error?) @@ -99,8 +100,8 @@ class HTTPDownloader: NSObject { } // Successful download. - log.debug("Downloaded file \(currentRequest.url) as \(location), " + - "to be copied to \(currentRequest.destinationFile ?? "nil")") + let message = "Downloaded file \(currentRequest.url) as \(location), to be copied to \(currentRequest.destinationFile ?? "nil")" + os_log("%{public}s", log: KeymanEngineLogger.resources, type: .debug, message) // If a destination file for the download has already been specified, let's go ahead and copy it over. if let destFile = currentRequest.destinationFile { @@ -115,7 +116,7 @@ class HTTPDownloader: NSObject { try FileManager.default.copyItem(at: location, to: destFileUrl) } catch { - log.error("Error saving the download: \(error)") + os_log("Error saving the download: %{public}s", log: KeymanEngineLogger.resources, type: .error, error.localizedDescription) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Installation/PackageInstallViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Installation/PackageInstallViewController.swift index 717a824c5e..988a3feea8 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Installation/PackageInstallViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Installation/PackageInstallViewController.swift @@ -9,6 +9,7 @@ import Foundation import WebKit import DeviceKit +import os.log public class PackageInstallViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITabBarControllerDelegate, UIAdaptivePresentationControllerDelegate { private enum NavigationMode: Int { // b/c hashable @@ -97,7 +98,7 @@ public class PackageInstallViewController: UIViewCon var preinstalleds: Set = Set() guard let typedPackage = package as? TypedKeymanPackage else { - log.warning("Cannot check for previously-installed resources of unexpected type") + os_log("Cannot check for previously-installed resources of unexpected type", log:KeymanEngineLogger.resources, type: .error) return preinstalleds } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift index ec7807110f..dc2b4a2e41 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/InputViewController.swift @@ -8,6 +8,7 @@ import AudioToolbox import UIKit +import os.log public enum GlobeKeyTapBehaviour { case switchToNextKeyboard @@ -367,7 +368,7 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { // This should allow us to debug any failures of this assumption. // So far, only occurs when debugging a breakpoint during a touch event on BKSP, // so all seems good. - log.verbose("Failed to swallow a recent textDidChange call!") + os_log("Failed to swallow a recent textDidChange call!", log: KeymanEngineLogger.ui, type: .default) } self.swallowBackspaceTextChange = true break @@ -538,7 +539,9 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate { func clearText() { setContextState(text: nil, range: NSRange(location: 0, length: 0)) keymanWeb.resetContext() - SentryManager.breadcrumbAndLog("Cleared text.") + let message = "Cleared text." + os_log("%{public}s", log: KeymanEngineLogger.ui, type: .info, message) + SentryManager.breadcrumb(message) } func resetContext() { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift index 93338dd575..520af29cc5 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift @@ -9,6 +9,7 @@ import Foundation import DeviceKit import UIKit +import os.log /** * Documents the basic size properties of the default system keyboard, _as seen from within a `UIInputView` or its `UIInputViewController`_. @@ -168,13 +169,13 @@ class KeyboardScaleMap { * the largest device smaller than or equal to the detected dimensions. */ private static func getUnknownDeviceMapping(screenSize _screenSize: CGSize = UIScreen.main.bounds.size, asPhone: Bool? = nil) -> Device { - // Shouldn't happen, but just in case. - if _screenSize == CGSize.zero { -// // This would notify us whenever new devices are out that we haven't build a mapping for. -// SentryManager.captureAndLog("Cannot detect device dimensions; defaulting to smallest device for form factor.", sentryLevel: .info) - + // Shouldn't happen, but just in case. + if _screenSize == CGSize.zero { + // This would notify us whenever new devices are out that we haven't build a mapping for. // We haven't actually updated things here in a while, so we'll just breadcrumb for now. - SentryManager.breadcrumbAndLog("Cannot detect device dimensions; defaulting to smallest device for form factor.", sentryLevel: .error) + let message = "Cannot detect device dimensions; defaulting to smallest device for form factor." + os_log("%{public}s", log: KeymanEngineLogger.ui, type: .error, message) + SentryManager.breadcrumb(message, sentryLevel: .error) } // Convert to CGSize in portrait orientation. @@ -228,7 +229,7 @@ class KeyboardScaleMap { default: if !isUnknown { // We can still perform a mapping, but it's not ideal. - log.warning("Keyboard scaling definition missing for device \(device.description)") + os_log("Keyboard scaling definition missing for device %{public}s", log: KeymanEngineLogger.ui, type: .default, device.description) } // The expected case: isUnknown = true. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift index 6ab2ea2a68..af7d195039 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeymanWebViewController.swift @@ -10,7 +10,7 @@ import UIKit import WebKit import AudioToolbox import Sentry -import XCGLogger +import os.log private let keyboardChangeHelpText = NSLocalizedString("keyboard-help-change", bundle: engineBundle, comment: "") @@ -195,7 +195,9 @@ extension KeymanWebViewController { let cmd = "executePopupKey(\"\(id)\",\"\(escapedText)\");" webView!.evaluateJavaScript(cmd, completionHandler: nil) } catch { - SentryManager.captureAndLog(error) + let message = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .error, message) + SentryManager.capture(error, message: message) return } } @@ -246,7 +248,8 @@ extension KeymanWebViewController { self.currentText = String(jsonText) webView!.evaluateJavaScript("setKeymanVal(\"\(jsonText)\");", completionHandler: nil) } catch { - SentryManager.captureAndLog(error.localizedDescription) + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, error.localizedDescription) + SentryManager.capture(error.localizedDescription) } } @@ -288,12 +291,14 @@ extension KeymanWebViewController { // failed to initialize properly. guard FileManager.default.fileExists(atPath: fileURL.path) else { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "File missing for keyboard") + let errorMessage = "File missing for keyboard" + event.message = SentryMessage(formatted: errorMessage) event.extra = [ "id": keyboard.id, "file": fileURL ] if let packageID = keyboard.packageID { event.extra?["package"] = packageID } - SentryManager.captureAndLog(event) + SentryManager.capture(event) + os_log("%{public}s id: %s file: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, keyboard.id, fileURL as CVarArg) throw KeyboardError.fileMissing } @@ -311,12 +316,14 @@ extension KeymanWebViewController { data = try JSONSerialization.data(withJSONObject: stub, options: []) } catch { let event = Sentry.Event(error: error) + let errorMessage = "Failed to serialize keyboard stub:" event.message = SentryMessage(formatted: "Failed to serialize keyboard stub: \(error)") event.extra = [:] event.extra!["id"] = stub["KI"] event.extra!["package"] = stub["KP"] - SentryManager.captureAndLog(event) + SentryManager.capture(event) + os_log("%{public}s id: %{public}s file: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, keyboard.id, fileURL as CVarArg) throw KeyboardError.keyboardLoadingError } guard let stubString = String(data: data, encoding: .utf8) else { @@ -326,12 +333,13 @@ extension KeymanWebViewController { event.extra!["id"] = stub["KI"] event.extra!["package"] = stub["KP"] - SentryManager.captureAndLog(event) + os_log("Failed to create keyboard stub string for embedded KMW", log: KeymanEngineLogger.ui, type: .error) + SentryManager.capture(event) throw KeyboardError.keyboardLoadingError } - - SentryManager.breadcrumbAndLog("Keyboard stub built for \(keyboard.id)", logLevel: XCGLogger.Level.none) - log.info("Keyboard stub: \(stubString)") + let message = "Keyboard stub built for \(keyboard.id)" + os_log("%{public}s", log: KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) webView!.evaluateJavaScript("setKeymanLanguage(\(stubString));", completionHandler: nil) } @@ -349,12 +357,15 @@ extension KeymanWebViewController { guard FileManager.default.fileExists(atPath: fileURL.path) else { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "File missing for lexical model") + let errorMessage = "File missing for lexical model" + event.message = SentryMessage(formatted: errorMessage) event.extra = [ "id": lexicalModel.id, "file": fileURL ] if let packageID = lexicalModel.packageID { event.extra?["package"] = packageID } - SentryManager.captureAndLog(event) + + os_log("%{public}s: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, lexicalModel.id) + SentryManager.capture(event) throw KeyboardError.fileMissing } @@ -363,25 +374,30 @@ extension KeymanWebViewController { data = try JSONSerialization.data(withJSONObject: stub, options: []) } catch { let event = Sentry.Event(error: error) + let errorMessage = "Failed to serialize lexical model stub:" event.message = SentryMessage(formatted: "Failed to serialize lexical model stub: \(error)") event.extra = [:] event.extra!["id"] = stub["id"] - SentryManager.captureAndLog(event) + os_log("%{public}s: %{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage, error.localizedDescription) + SentryManager.capture(event) throw KeyboardError.lexicalModelLoadingError } guard let stubString = String(data: data, encoding: .utf8) else { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "Failed to create lexical model stub string for embedded KMW") + let errorMessage = "Failed to create lexical model stub string for embedded KMW" + event.message = SentryMessage(formatted: errorMessage) event.extra = [:] event.extra!["id"] = stub["id"] - SentryManager.captureAndLog(event) + os_log("%{public}s", log: KeymanEngineLogger.resources, type: .error, errorMessage) + SentryManager.capture(event) throw KeyboardError.lexicalModelLoadingError } - SentryManager.breadcrumbAndLog("LexicalModel stub built for \(lexicalModel.id)", logLevel: XCGLogger.Level.none) - log.info("LexicalModel stub: \(stubString)") + let message = "LexicalModel stub built for \(lexicalModel.id)" + os_log("%{public}s", log: KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) if lexicalModel.languageID == Manager.shared.currentKeyboardID?.languageID { // We're registering a lexical model for the now-current keyboard. @@ -404,7 +420,9 @@ extension KeymanWebViewController { } func showBanner(_ display: Bool) { - SentryManager.breadcrumbAndLog("Changing banner's alwaysShow property to \(display).", category: "engine", sentryLevel: .debug) + let message = "Changing banner's alwaysShow property to \(display)" + os_log("%{public}s", log: KeymanEngineLogger.settings, type: .debug, message) + SentryManager.breadcrumb(message, category: "engine", sentryLevel: .debug) webView?.evaluateJavaScript("showBanner(\(display ? "true" : "false"))", completionHandler: nil) } @@ -416,7 +434,8 @@ extension KeymanWebViewController { } else { logString = path } - log.debug("Banner image path: '\(logString).'") + let message = "Banner image path: '\(logString).'" + os_log("%{public}s", log: KeymanEngineLogger.ui, type: .debug, message) webView?.evaluateJavaScript("setBannerImage(\"\(path)\");", completionHandler: nil) } @@ -519,7 +538,8 @@ extension KeymanWebViewController: WKScriptMessageHandler { subkeyIDs.append(values[0]) subkeyTexts.append(values[1].stringFromUTF16CodeUnits() ?? "") default: - log.warning("Unexpected subkey key: \(key)") + let message = "Unexpected subkey key: \(key)" + os_log("%{public}s", log: KeymanEngineLogger.ui, type: .error, message) } } @@ -552,7 +572,10 @@ extension KeymanWebViewController: WKScriptMessageHandler { // This may need filtering for proper use with Sentry? // Then again, if KMW is logging it... we already have to worry // about it showing up in Web-oriented Sentry logs. - SentryManager.breadcrumbAndLog("KMW Log: \(message)", category: "engine") + + let logMessage = "KMW Log: \(message)" + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, logMessage) + SentryManager.breadcrumb(logMessage) } else if fragment.hasPrefix("#beep-") { beep(self) delegate?.beep(self) @@ -565,9 +588,12 @@ extension KeymanWebViewController: WKScriptMessageHandler { do { let cmd = try decoder.decode(SuggestionPopup.self, from: cmdData!) - log.verbose("Longpress detected on suggestion: \"\(cmd.suggestion.displayAs)\".") - } catch { - SentryManager.captureAndLog(error, message: "Unexpected JSON parse error: \(error).") + let message = "Longpress detected on suggestion: \"\(cmd.suggestion.displayAs)\"." + os_log("%{public}s", log: KeymanEngineLogger.ui, type: .debug, message) + } catch { + let message = "Unexpected JSON parse error: \(error)." + os_log("%{public}s", log:KeymanEngineLogger.engine, type: .error, message) + SentryManager.capture(error, message: message) } // Will need processing upon extraction from the resulting object. @@ -579,7 +605,9 @@ extension KeymanWebViewController: WKScriptMessageHandler { // let frame = KeymanWebViewController.keyFrame(x: x, y: y, w: w, h: h) } else { - SentryManager.captureAndLog("Unexpected KMW event: \(fragment)") + let message = "Unexpected KMW event: \(fragment)" + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.capture(message) } } @@ -636,7 +664,9 @@ extension KeymanWebViewController: KeymanWebDelegate { delegate?.keyboardLoaded(keymanWeb) isLoading = false - SentryManager.breadcrumbAndLog("Loaded keyboard.", sentryLevel: .debug, logLevel: .info) + let message = "Loaded keyboard." + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.breadcrumb(message, sentryLevel: .debug) self.setSentryState() resizeKeyboard() @@ -663,7 +693,9 @@ extension KeymanWebViewController: KeymanWebDelegate { newKb = userKbs[0] } } - SentryManager.breadcrumbAndLog("Setting initial keyboard.", category: "engine") + let message = "Setting initial keyboard." + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.breadcrumb(message) // Compare against resetKeyboard & Manager.setKeyboard; // setting this to `nil` allows us to force keyboard reloads when needed. @@ -688,7 +720,9 @@ extension KeymanWebViewController: KeymanWebDelegate { _ = Manager.shared.setKeyboard(Defaults.keyboard) } } catch { - SentryManager.captureAndLog("Could not load default keyboard as a fallback for keyboard loading failure", sentryLevel: .fatal) + let message = "Could not load default keyboard as a fallback for keyboard loading failure" + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, message) + SentryManager.capture(message, sentryLevel: .fatal) } newKb = Defaults.keyboard } @@ -1066,13 +1100,19 @@ extension KeymanWebViewController { Manager.shared.currentKeyboardID = nil if let keyboard = keyboard { - SentryManager.breadcrumbAndLog("Current keyboard is set.", category: "engine") + let message = "Current keyboard is set." + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.breadcrumb(message) _ = Manager.shared.setKeyboard(keyboard) } else if let keyboard = Storage.active.userDefaults.userKeyboards?[safe: 0] { - SentryManager.breadcrumbAndLog("Using user's default keyboard.", category: "engine") + let message = "Using user's default keyboard." + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.breadcrumb(message) _ = Manager.shared.setKeyboard(keyboard) } else { - SentryManager.breadcrumbAndLog("Using app-default keyboard.", category: "engine") + let message = "Using app-default keyboard." + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .info, message) + SentryManager.breadcrumb(message) _ = Manager.shared.setKeyboard(Defaults.keyboard) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift index 0e08866fb4..e63704e650 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log public class KeyboardKeymanPackage : TypedKeymanPackage { internal var keyboards: [KMPKeyboard]! @@ -22,7 +23,9 @@ public class KeyboardKeymanPackage : TypedKeymanPackage { if(keyboard.isValid && FileManager.default.fileExists(atPath: self.sourceFolder.appendingPathComponent("\(keyboard.keyboardId).js").path)) { keyboards.append(keyboard) } else { - SentryManager.breadcrumbAndLog("\(keyboard.name) not valid / corresponding file not found") + let message = "\(keyboard.name) not valid / corresponding file not found" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift index a13cef142c..2b45462cdc 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift @@ -8,6 +8,7 @@ import Foundation import ZIPFoundation +import os.log // KMPErrors may be passed to UIAlertControllers, so they need localization. public enum KMPError : String, Error { @@ -245,7 +246,7 @@ public class KeymanPackage { try FileManager.default.removeItem(at: self.sourceFolder) } } catch { - log.debug("Could not remove temporary extraction site on package deinit") + os_log("Could not remove temporary extraction site on package deinit", log:KeymanEngineLogger.resources, type: .error) } } } @@ -436,7 +437,8 @@ public class KeymanPackage { let minVersion = Version(minSupportedVersion) if (currentVersion.major < minVersion!.major) { - log.error("package '\(packageName)' could not be installed because it requires version \(minSupportedVersion) of Keyman") + let message = "package '\(packageName)' could not be installed because it requires version \(minSupportedVersion) of Keyman" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) throw KMPError.unsupportedKeymanVersion } } @@ -449,7 +451,9 @@ public class KeymanPackage { let package = try KeymanPackage.parse(destination) complete(package) } catch { - SentryManager.captureAndLog(error, sentryLevel: .info) + let message = "Keyboard installation error: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) complete(nil) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanURLProtocol.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanURLProtocol.swift index 5a9e6f4877..4678c70350 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanURLProtocol.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanURLProtocol.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log class KeymanURLProtocol: URLProtocol, NSURLConnectionDataDelegate { private static let protocolHandledKey = "KMURLProtocolHandledKey" @@ -32,7 +33,7 @@ class KeymanURLProtocol: URLProtocol, NSURLConnectionDataDelegate { override func startLoading() { guard let mutableRequest = request as? NSMutableURLRequest else { - log.error("Bridge to NSMutableURLRequest failed") + os_log("Bridge to NSMutableURLRequest failed", log:KeymanEngineLogger.resources, type: .error) return } URLProtocol.setProperty(true, forKey: KeymanURLProtocol.protocolHandledKey, in: mutableRequest) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguageLMDetailViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguageLMDetailViewController.swift index 6c03696a45..67c878ec4d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguageLMDetailViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguageLMDetailViewController.swift @@ -8,6 +8,7 @@ import UIKit +import os.log private let toolbarButtonTag = 100 private let toolbarLabelTag = 101 @@ -53,19 +54,19 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate forName: Notifications.packageDownloadFailed, observer: self, function: LanguageLMDetailViewController.lexicalModelDownloadFailed) - log.info("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)") + os_log("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)", log:KeymanEngineLogger.ui, type: .info) } override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - log.info("willAppear: LanguageLMDetailViewController") + os_log("viewWillAppear: LanguageLMDetailViewController", log:KeymanEngineLogger.ui, type: .info) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - log.info("didAppear: LanguageLMDetailViewController") - + os_log("viewDidAppear: LanguageLMDetailViewController", log:KeymanEngineLogger.ui, type: .info) + navigationController?.setToolbarHidden(true, animated: true) } @@ -156,7 +157,7 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate } private func lexicalModelDownloadStarted() { - log.info("lexicalModelDownloadStarted: LanguageLMDetailViewController") + os_log("lexicalModelDownloadStarted: LanguageLMDetailViewController", log:KeymanEngineLogger.resources, type: .info) view.isUserInteractionEnabled = false navigationItem.setHidesBackButton(true, animated: true) @@ -164,8 +165,8 @@ class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate } private func lexicalModelDownloadFailed() { - log.info("lexicalModelDownloadFailed: LanguageLMDetailViewController") - view.isUserInteractionEnabled = true + os_log("lexicalModelDownloadFailed: LanguageLMDetailViewController", log:KeymanEngineLogger.resources, type: .info) + view.isUserInteractionEnabled = true navigationItem.setHidesBackButton(false, animated: true) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift index 5dd0d103cf..19dd138849 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LanguagePicker/KeyboardSwitcherViewController.swift @@ -9,6 +9,7 @@ // It replaces the original in every case but one. import UIKit +import os.log private let toolbarButtonTag = 100 @@ -27,7 +28,7 @@ class KeyboardSwitcherViewController: UITableViewController, UIAlertViewDelegate //NEEDED? navigationItem.rightBarButtonItem = nil - log.info("didLoad: KeyboardSwitcherViewController") + os_log("didLoad: KeyboardSwitcherViewController", log: KeymanEngineLogger.ui, type: .info) } override func viewWillAppear(_ animated: Bool) { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift index fe9cd930e6..9188d97c05 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log public class LexicalModelKeymanPackage : TypedKeymanPackage { internal var models : [KMPLexicalModel]! @@ -24,7 +25,9 @@ public class LexicalModelKeymanPackage : TypedKeymanPackage Void @@ -211,7 +212,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { do { try Storage.active.copyKMWFiles(from: Resources.bundle) } catch { - SentryManager.captureAndLog(error, message: "Failed to copy KMW files from bundle: \(error)") + let message = ("Failed to copy KMW files from bundle: \(error)") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message:message) } updateUserKeyboards(with: Defaults.keyboard) @@ -219,7 +222,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { do { try reachability = Reachability(hostname: KeymanHosts.API_KEYMAN_COM.host!) } catch { - SentryManager.captureAndLog(error, message: "Could not start Reachability object: \(error)") + let message = ("Could not start Reachability object: \(error)") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message:message) } if(!Util.isSystemKeyboard) { @@ -228,7 +233,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { do { try reachability.startNotifier() } catch { - SentryManager.captureAndLog(error, message: "failed to start Reachability notifier: \(error)") + let message = ("failed to start Reachability notifier: \(error)") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message:message) } } @@ -294,11 +301,15 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { // We MUST NOT shortcut this method as a result; doing so may (rarely) result in the infamous // blank keyboard bug! if kb.fullID == currentKeyboardID && !self.isSystemKeyboard && !inputViewController.shouldReload { - SentryManager.breadcrumbAndLog("Keyboard unchanged: \(kb.fullID)") + let message = "Keyboard unchanged: \(kb.fullID)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) return false } - SentryManager.breadcrumbAndLog("Setting language: \(kb.fullID)") + let message = "Setting language: \(kb.fullID)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb("Setting language: \(kb.fullID)") currentKeyboardID = kb.fullID @@ -361,7 +372,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if !Migrations.resourceHasPackageMetadata(keyboard) { let wrappedKbds = Migrations.migrateToKMPFormat([keyboard]) guard wrappedKbds.count == 1 else { - SentryManager.captureAndLog("Could not properly import keyboard") + let message = "Could not properly import keyboard" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.capture(message) return } kbdToInstall = wrappedKbds[0] @@ -398,7 +411,8 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { try ResourceFileManager.shared.install(resourceWithID: fullId, from: package) } catch { - log.error("Could not add lexical model for id '\(lexicalModelId)' and languageId '\(languageId)' due to error: \(error)") + let message = "Could not add lexical model for id '\(lexicalModelId)' and languageId '\(languageId)' due to error: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) } if let lexicalModel = Storage.active.userDefaults.userLexicalModel(withFullID: fullId) { @@ -421,7 +435,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { /// Registers a lexical model with KMW. public func registerLexicalModel(_ lm: InstallableLexicalModel) -> Bool { - SentryManager.breadcrumbAndLog("Setting lexical model: \(lm.fullID)") + let message = "Setting lexical model: \(lm.fullID)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) currentLexicalModelID = lm.fullID @@ -462,7 +478,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if !Migrations.resourceHasPackageMetadata(lexicalModel) { let wrappedModels = Migrations.migrateToKMPFormat([lexicalModel]) guard wrappedModels.count == 1 else { - SentryManager.captureAndLog("Could not properly import lexical model") + let message = "Could not properly import lexical model" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.capture(message) return } modelToInstall = wrappedModels[0] @@ -502,7 +520,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { userData.set([Date()], forKey: Key.synchronizeSWKeyboard) userData.synchronize() - SentryManager.breadcrumbAndLog("Removing keyboard with ID \(kb.id) and languageID \(kb.languageID)") + let message = ("Removing keyboard with ID \(kb.id) and languageID \(kb.languageID)") + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) // Set a new keyboard if deleting the current one if kb.fullID == currentKeyboardID { @@ -516,12 +536,18 @@ 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) - SentryManager.breadcrumbAndLog("Deleting directory \(keyboardDir)") + let message = "Deleting directory \(keyboardDir)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) if (try? FileManager.default.removeItem(at: keyboardDir)) == nil { - SentryManager.captureAndLog("Failed to delete \(keyboardDir) when removing keyboard") + let message = "Failed to delete \(keyboardDir) when removing keyboard" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.capture(message) } } else { - SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of keyboard files.") + let message = "User has another language installed. Skipping delete of keyboard files." + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) } NotificationCenter.default.post(name: Notifications.keyboardRemoved, object: self, value: kb) @@ -570,7 +596,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { } let lm = userLexicalModels[index] - SentryManager.breadcrumbAndLog("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from user list of all models") + let message = "Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from user list of all models" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) userLexicalModels.remove(at: index) ud.userLexicalModels = userLexicalModels ud.set([Date()], forKey: Key.synchronizeSWLexicalModel) @@ -580,7 +608,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { /// Removes the lexical model at index from the lexical models list if it exists. public func removeLexicalModelFromLanguagePreference(userDefs ud: UserDefaults, _ lm: InstallableLexicalModel) { - SentryManager.breadcrumbAndLog("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from per-language prefs") + let message = "Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from per-language prefs" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) ud.set(preferredLexicalModelID: nil, forKey: lm.languageID) } @@ -601,7 +631,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { if let first_lm = userLexicalModels.first(where: {$0.languageID == lm.languageID}) { _ = registerLexicalModel(first_lm) } else { - SentryManager.breadcrumbAndLog("no more lexical models available for language \(lm.fullID)") + let message = "no more lexical models available for language \(lm.fullID)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) currentLexicalModelID = nil } } @@ -609,12 +641,18 @@ 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) - SentryManager.breadcrumbAndLog("Deleting directory \(lexicalModelDir)") + let message = "Deleting directory \(lexicalModelDir)" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) if (try? FileManager.default.removeItem(at: lexicalModelDir)) == nil { - SentryManager.captureAndLog("Failed to delete \(lexicalModelDir) when removing lexical model") + let message = "Failed to delete \(lexicalModelDir) when removing lexical model" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.capture(message) } } else { - SentryManager.breadcrumbAndLog("User has another language installed. Skipping delete of lexical model files.") + let message = "User has another language installed. Skipping delete of lexical model files." + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) } NotificationCenter.default.post(name: Notifications.lexicalModelRemoved, object: self, value: lm) @@ -706,7 +744,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { } @objc func reachabilityChanged(_ notification: Notification) { - log.debug { + let message = { [self] in let reachStr: String switch reachability.connection { case Reachability.Connection.wifi: @@ -718,6 +756,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { } return "Reachability changed to '\(reachStr)'" } + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message()) } // MARK: - Loading custom keyboards @@ -793,7 +832,9 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { try shared.copyFiles(to: nonShared) FontManager.shared.registerCustomFonts() } catch { - SentryManager.captureAndLog(error, message: "Failed to copy from shared container: \(error)") + let message = ("Failed to copy from shared container: \(error)") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(error, message:message) } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift b/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift index 65da71edea..8f7a4b9342 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Notification/NotificationCenter+Typed.swift @@ -8,6 +8,7 @@ import Foundation import Sentry +import os.log private let userInfoKey = "value" @@ -42,7 +43,9 @@ public extension NotificationCenter { if let value = notification.userInfo?[userInfoKey] as? T { block(value) } else { - SentryManager.captureAndLog("Unexpected userInfo in notification: \(String(describing: notification))") + let message = "Unexpected userInfo in notification: \(String(describing: notification))" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.capture(message) } } return NotificationObserver(observer: observer, center: self) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+LexicalModel.swift b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+LexicalModel.swift index ed961b8776..7f7156b36f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+LexicalModel.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+LexicalModel.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log extension Queries { public class LexicalModel { @@ -56,7 +57,9 @@ extension Queries { var urlComponents = MODEL_ENDPOINT urlComponents.queryItems = [URLQueryItem(name: "q", value: "bcp47:\(bcp47)")] - SentryManager.breadcrumbAndLog("Querying package versions through API endpoint: \(urlComponents.url!)") + let message = "Querying package versions through API endpoint: \(urlComponents.url!)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) // Step 2: configure the completion closure. let completionClosure = Queries.jsonDataTaskCompletionAdapter(resultType: [Result].self, completionBlock: fetchCompletion) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift index c1c4d89cf5..69ca1e15a9 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries+PackageVersion.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log extension Queries { // Kept internal; the query is 'publicly exposed' through KeymanPackage and ResourceFileManager. @@ -116,7 +117,9 @@ extension Queries { } urlComponents.queryItems = queryItems + [URLQueryItem(name: "platform", value: "ios")] - SentryManager.breadcrumbAndLog("Querying package versions through API endpoint: \(urlComponents.url!)") + let message = "Querying package versions through API endpoint: \(urlComponents.url!)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) // Step 2: configure the completion closure. let completionClosure = Queries.jsonDataTaskCompletionAdapter(resultType: Result.self, completionBlock: fetchCompletion) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/FontManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/FontManager.swift index c8d5715387..d64019cc27 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/FontManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/FontManager.swift @@ -9,6 +9,7 @@ import CoreText import Foundation import UIKit // for UIFont +import os.log public class FontManager { public static let shared = FontManager() @@ -49,13 +50,15 @@ public class FontManager { private func readFontName(at url: URL) -> String? { guard let provider = CGDataProvider(url: url as CFURL) else { - log.error("Failed to open \(url)") + let message = "Failed to open \(url)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) return nil } guard let font = CGFont(provider), let name = font.postScriptName else { - log.error("Failed to read font at \(url)") + let message = "Failed to read font at \(url)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) return nil } return name as String @@ -84,13 +87,16 @@ public class FontManager { didRegister = CTFontManagerRegisterFontsForURL(url as CFURL, .none, &errorRef) let error = errorRef?.takeRetainedValue() // Releases errorRef if !didRegister { - log.error("Failed to register font \(fontName) at \(url) reason: \(String(describing: error))") + let message = "Failed to register font \(fontName) at \(url) reason: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) } else { - log.info("Registered font \(fontName) at \(url)") + let message = "Registered font \(fontName) at \(url)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) } } else { didRegister = false - log.info("Did not register font at \(url) because font name \(fontName) is already registered") + let message = "Did not register font at \(url) because font name \(fontName) is already registered" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) } let font = RegisteredFont(name: fontName, isRegistered: didRegister) fonts[url] = font @@ -111,11 +117,13 @@ public class FontManager { let didUnregister = CTFontManagerUnregisterFontsForURL(url as CFURL, .none, &errorRef) let error = errorRef?.takeRetainedValue() // Releases errorRef if didUnregister { - log.info("Unregistered font \(font.name) at \(url)") + let message = "Unregistered font \(font.name) at \(url)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) font.isRegistered = false fonts[url] = font } else { - log.error("Failed to unregister font \(font.name) at \(url) reason: \(String(describing: error))") + let message = "Failed to unregister font \(font.name) at \(url) reason: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) } } @@ -128,7 +136,8 @@ public class FontManager { public func registerFonts(in directory: URL) { guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else { - log.error("Could not list contents of directory \(directory)") + let message = "Could not list contents of directory \(directory)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) return } for url in urls where url.lastPathComponent.hasFontExtension { @@ -138,7 +147,8 @@ public class FontManager { public func unregisterFonts(in directory: URL, fromSystemOnly: Bool = true) { guard let urls = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else { - log.error("Could not list contents of directory \(directory)") + let message = "Could not list contents of directory \(directory)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) return } for url in urls where url.lastPathComponent.hasFontExtension { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift index 14a71eb542..b28b81abd3 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log /// Mainly differs from the API `Keyboard` by having an associated language. public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource { @@ -107,7 +108,9 @@ public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource { let languageMatches = metadata.languages.compactMap { return $0.languageId == lgCode ? $0.name : nil } if (languageMatches.isEmpty) { - SentryManager.captureAndLog("Could not find languageId '\(lgCode)' for package '\(packageID)'", sentryLevel: .warning) + let message = "Could not find languageId '\(lgCode)' for package '\(packageID)'" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(message) } guard languageMatches.count >= 1 else { return nil diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift index 02579a8edd..30220255f1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift @@ -8,6 +8,7 @@ import Foundation import Sentry +import os.log private enum MigrationLevel { static let initial = 0 @@ -63,13 +64,17 @@ public enum Migrations { migrateUserDefaultsToStructs(storage: storage) storage.userDefaults.migrationLevel = MigrationLevel.migratedUserDefaultsToStructs } else { - SentryManager.breadcrumbAndLog("UserDefaults migration to structs already performed. Skipping.", category: "migration") + let message = "UserDefaults migration to structs already performed. Skipping." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) } if storage.userDefaults.migrationLevel < MigrationLevel.migratedForKMP { migrateForKMP(storage: storage) storage.userDefaults.migrationLevel = MigrationLevel.migratedForKMP } else { - SentryManager.breadcrumbAndLog("KMP directory migration already performed. Skipping.", category: "migration") + let message = "KMP directory migration already performed. Skipping." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) } // Version-based migrations @@ -78,10 +83,14 @@ public enum Migrations { do { try migrateDocumentsFromPreBrowser() } catch { - SentryManager.captureAndLog(error, message: "Could not migrate Documents directory contents: \(error)") + let message = "Could not migrate Documents directory contents: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) } } else { - SentryManager.breadcrumbAndLog("Documents directory structure compatible with \(Version.fileBrowserImplemented)") + let message = "Documents directory structure compatible with \(Version.fileBrowserImplemented)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) } if version < Version.packageBasedFileReorg { @@ -89,12 +98,17 @@ public enum Migrations { try migrateCloudResourcesToKMPFormat() } catch { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "Could not migrate pre-existing resources to KMP-style file organization") + let message = "Could not migrate pre-existing resources to KMP-style file organization" + event.message = SentryMessage(formatted: message) event.extra = [ "priorVersion": version ] - SentryManager.captureAndLog(event) + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) + SentryManager.capture(event) } } else { - SentryManager.breadcrumbAndLog("Resource directories already migrated to package-based format; kmp.jsons already exist.", category: "migration") + let message = "Resource directories already migrated to package-based format; kmp.jsons already exist." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) } } @@ -103,7 +117,9 @@ public enum Migrations { static func detectLegacyKeymanVersion() -> [Version] { // While the 'key' used to track version info existed before v12, it was unused until then. - SentryManager.breadcrumbAndLog("Prior engine version unknown; attepting to auto-detect.", category: "migration") + let message = "Prior engine version unknown; attepting to auto-detect." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) // Detect possible version matches. let userResources = Storage.active.userDefaults.userResources ?? [] @@ -259,7 +275,9 @@ public enum Migrations { do { try Storage.active.installDefaultKeyboard(from: Resources.bundle) } catch { - SentryManager.captureAndLog(error, message: "Failed to copy default keyboard from bundle: \(error)") + let message = "Failed to copy default keyboard from bundle: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -272,7 +290,9 @@ public enum Migrations { do { try Storage.active.installDefaultLexicalModel(from: Resources.bundle) } catch { - SentryManager.captureAndLog(error, message: "Failed to copy default lexical model from bundle: \(error)") + let message = "Failed to copy default lexical model from bundle: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -284,13 +304,17 @@ public enum Migrations { guard let userKeyboardObject = storage.userDefaults.object(forKey: Key.userKeyboardsList), let currentKeyboardObject = storage.userDefaults.object(forKey: Key.userCurrentKeyboard) else { - SentryManager.breadcrumbAndLog("User keyboard list or current keyboard missing. Skipping migration.", category: "migration") + let message = "User keyboard list or current keyboard missing. Skipping migration." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) return } guard let oldUserKeyboards = userKeyboardObject as? [[String: String]], let oldCurrentKeyboard = currentKeyboardObject as? [String: String] else { - SentryManager.captureAndLog("User keyboard list or current keyboard has an unexpected type") + let message = "User keyboard list or current keyboard has an unexpected type" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.capture(message) return } @@ -306,14 +330,18 @@ public enum Migrations { } private static func installableKeyboard(from kbDict: [String: String]) -> InstallableKeyboard? { - SentryManager.breadcrumbAndLog("Migrating keyboard dictionary for '\(String(describing: kbDict["kbId"]))", category: "migration", sentryLevel: .debug) + let message = "Migrating keyboard dictionary for '\(String(describing: kbDict["kbId"]))" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message) + SentryManager.breadcrumb(message) guard let id = kbDict["kbId"], let name = kbDict["kbName"], let languageID = kbDict["langId"], let languageName = kbDict["langName"], let version = kbDict["version"] else { - SentryManager.captureAndLog("Missing required fields in keyboard dictionary: \(kbDict)") + let messageOne = "Missing required fields in keyboard dictionary: \(kbDict)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, messageOne) + SentryManager.capture(message) return nil } let rtl = kbDict["rtl"] == "Y" @@ -329,8 +357,11 @@ public enum Migrations { font: displayFont, oskFont: oskFont, isCustom: isCustom) - SentryManager.breadcrumbAndLog("Migrated keyboard dictionary to keyboard \(kb.id)") - log.debug(kb) + let messageTwo = "Migrated keyboard dictionary to keyboard \(kb.id)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, messageTwo) + SentryManager.breadcrumb(message) + let messageThree = "\(kb)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, messageThree) return kb } @@ -339,13 +370,17 @@ public enum Migrations { let languageID = kbDict["langId"] else { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "Missing required fields in keyboard dictionary for FullKeyboardID") + let message = "Missing required fields in keyboard dictionary for FullKeyboardID" + event.message = SentryMessage(formatted: message) event.extra = ["kbId": kbDict["kbId"] ?? "nil", "langId": kbDict["langId"] ?? "nil"] - SentryManager.captureAndLog(event) + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.capture(event) return nil } let id = FullKeyboardID(keyboardID: keyboardID, languageID: languageID) - SentryManager.breadcrumbAndLog("Migrated keyboard dictionary to \(id)", category: "migration") + let message = "Migrated keyboard dictionary to \(id)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) return id } @@ -354,15 +389,18 @@ public enum Migrations { return nil } guard let data = jsonString.data(using: .utf8) else { - log.error("Failed to encode string: \(jsonString)") + let message = "Failed to encode string: \(jsonString)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) return nil } guard let fontDict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else { - log.error("Error parsing String as JSON: \(jsonString)") - return nil + let message = "Error parsing String as JSON: \(jsonString)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + return nil } guard let family = fontDict["family"] as? String else { - log.error("Missing 'family' String: \(fontDict)") + let message = "Missing 'family' String: \(fontDict)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) return nil } let files: [String] @@ -371,7 +409,8 @@ public enum Migrations { } else if let filesArray = fontDict["files"] as? [String] { files = filesArray } else { - log.error("Missing 'files': \(fontDict)") + let message = "Missing 'files': \(fontDict)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) return nil } return Font(family: family, source: files) @@ -382,10 +421,14 @@ public enum Migrations { let languageDir = storage.baseDir.appendingPathComponent("languages") let fontDir = storage.baseDir.appendingPathComponent("fonts") - SentryManager.breadcrumbAndLog("Migrating from base directory: \(storage.baseDir)", category: "migration") + let message = "Migrating from base directory: \(storage.baseDir)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) guard var userKeyboards = storage.userDefaults.userKeyboards else { - SentryManager.breadcrumbAndLog("No user keyboards to migrate", category: "migration") + let message = "No user keyboards to migrate" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) return } @@ -393,7 +436,8 @@ public enum Migrations { for i in userKeyboards.indices { let keyboard = userKeyboards[i] guard let version = latestKeyboardFileVersion(withID: keyboard.id, dirPath: languageDir.path) else { - log.warning("Could not find JS file for keyboard \(keyboard.id) in \(languageDir.path)") + let message = "Could not find JS file for keyboard \(keyboard.id) in \(languageDir.path)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, message) continue } var urls = urlsForKeyboard[keyboard.id] ?? Set() @@ -402,12 +446,14 @@ public enum Migrations { let fontFiles = (keyboard.font?.source ?? []) + (keyboard.oskFont?.source ?? []) for file in fontFiles { guard file.hasFontExtension else { - log.info("Skipping copy of \(file) for keyboard \(keyboard.id) since it is not a font file.") + let message = "Skipping copy of \(file) for keyboard \(keyboard.id) since it is not a font file." + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) continue } let url = fontDir.appendingPathComponent(file) guard FileManager.default.fileExists(atPath: url.path) else { - log.warning("Font file \(url) for keyboard \(keyboard.id) does not exist") + let message = "Font file \(url) for keyboard \(keyboard.id) does not exist" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .default, message) continue } urls.insert(url) @@ -426,7 +472,8 @@ public enum Migrations { withIntermediateDirectories: true, attributes: nil) } catch { - log.error("Failed to create keyboard directory at \(keyboardDir)") + let message = "Failed to create keyboard directory at \(keyboardDir)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) continue } @@ -436,13 +483,15 @@ public enum Migrations { do { try FileManager.default.copyItem(at: srcURL, to: dstURL) } catch { - log.error("Failed to copy from \(srcURL) to \(dstURL) for keyboard \(keyboardID)") + let message = "Failed to copy from \(srcURL) to \(dstURL) for keyboard \(keyboardID)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) successful = false } } if successful { successfulKeyboards.append(keyboardID) - log.info("Succesfully copied keyboard files for keyboard \(keyboardID)") + let message = "Succesfully copied keyboard files for keyboard \(keyboardID)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) } } @@ -476,7 +525,9 @@ public enum Migrations { } static func migrateDocumentsFromPreBrowser() throws { - SentryManager.breadcrumbAndLog("Cleaning Documents folder due to 12.0 installation artifacts", category: "migration") + let message = "Cleaning Documents folder due to 12.0 installation artifacts" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + SentryManager.breadcrumb(message) // Actually DO it. let documentFolderURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] @@ -491,16 +542,22 @@ public enum Migrations { let destFile = fileURL.lastPathComponent.replacingOccurrences(of: ".kmp.zip", with: ".kmp") let destURL = fileURL.deletingLastPathComponent().appendingPathComponent(destFile) - SentryManager.breadcrumbAndLog("\(fileURL) -> \(destURL)", category: "migration", sentryLevel: .debug) + let message = "\(fileURL) -> \(destURL)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message) + SentryManager.breadcrumb(message) try FileManager.default.moveItem(at: fileURL, to: destURL) } else if fileURL.lastPathComponent == "temp" { // Removes the 'temp' installation directory; that shouldn't be visible to users. - SentryManager.breadcrumbAndLog("Deleting directory: \(fileURL)", category: "migration", sentryLevel: .debug) + let message = "Deleting directory: \(fileURL)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message) + SentryManager.breadcrumb(message) try FileManager.default.removeItem(at: fileURL) } else if fileURL.lastPathComponent.hasSuffix(".kmp") { // Do nothing; this file is fine. } else { - SentryManager.breadcrumbAndLog("Unexpected file found in documents folder during upgrade: \(fileURL)", category: "migration") + let message = "Unexpected file found in documents folder during upgrade: \(fileURL)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .debug, message) + SentryManager.breadcrumb(message) } } } @@ -534,10 +591,12 @@ public enum Migrations { } } catch { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "Could not remove version number from filename") + let message = "Could not remove version number from filename" + event.message = SentryMessage(formatted: message) event.extra = ["package" : resource.packageID ?? "", "id": resource.id, "location": srcLocation ] - SentryManager.captureAndLog(event) + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(event) } } @@ -554,12 +613,16 @@ public enum Migrations { let tuple = try (ResourceFileManager.shared.prepareKMPInstall(from: filePath), filePath) return tuple } catch { - SentryManager.captureAndLog("Could not load kmp.info for local package during migration: \(file)") + let message = "Could not load kmp.info for local package during migration: \(file)" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(message) return nil } } } catch { - SentryManager.captureAndLog("Could not check contents of Documents directory for resource-migration assist") + let message = "Could not check contents of Documents directory for resource-migration assist" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(message) } // Filters out Packages that don't contain the matching resource type. @@ -596,7 +659,9 @@ public enum Migrations { matched.append(package.findResource(withID: resource.typedFullID)!) } } catch { - SentryManager.captureAndLog(error, message: "Could not install resource from locally-cached package: \(String(describing: error))") + let message = "Could not install resource from locally-cached package: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -664,9 +729,11 @@ public enum Migrations { // write to location. } catch { let event = Sentry.Event(level: .error) - event.message = SentryMessage(formatted: "Could not generate kmp.json for legacy resource!") + let message = "Could not generate kmp.json for legacy resource!" + event.message = SentryMessage(formatted: message) + os_log("%{public}s", log: KeymanEngineLogger.engine, type: .error, message) event.extra = [ "resourceId": package.id, "type": package.resourceType()] - SentryManager.captureAndLog(event) + SentryManager.capture(event) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift index 86b068ab29..89833bd5eb 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadManager.swift @@ -7,6 +7,7 @@ // import Foundation +import os.log public enum DownloadError : Error { public enum Cause: Error { @@ -241,26 +242,30 @@ public class ResourceDownloadManager { Queries.LexicalModel.fetchModels(forLanguageCode: languageID, withSession: session) { results, error in if let error = error { // We never quite started downloading the lexical model, so there's no download to have failed. - log.info("Failed to fetch lexical model list for "+languageID+". error: "+error.localizedDescription) + let message = "Failed to fetch lexical model list for \(languageID), error: \(error.localizedDescription)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) try? completionClosure?(nil, error) return } guard let results = results else { // Should not be possible. //TODO: put up an alert instead - log.info("No lexical models available for language \(languageID) (nil)") + let message = "No lexical models available for language \(languageID) (nil)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) try? completionClosure?(nil, nil) return } if results.count == 0 { - log.info("No lexical models available for language \(languageID) (empty)") + let message = "No lexical models available for language \(languageID) (empty)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) try? completionClosure?(nil, nil) // We automatically use the first model in the list. } else { let lexicalModel = results[0].0 let lmFullID = results[0].0.fullID - log.info("Fetched lexical model list for "+languageID+".") + let message = "Fetched lexical model list for \(languageID)." + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) let closure = completionClosure ?? self.standardLexicalModelInstallCompletionBlock(forFullID: lmFullID) let downloadURL = self.defaultDownloadURL(forPackage: lexicalModel.packageKey, @@ -496,7 +501,8 @@ public class ResourceDownloadManager { from: downloadURL) { package, error in guard let package = package, error == nil else { let errString = error != nil ? String(describing: error!) : "" - log.error("Could not successfully download package \(key) for update: \(errString)") + let errorMessage = "Could not successfully download package \(key) for update: \(errString)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) return } @@ -531,11 +537,15 @@ 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 { - SentryManager.captureAndLog("Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)") + let message = "Specified package (at \(packageURL)) does not contain lexical models: \(KMPError.invalidPackage)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.capture(message) return nil } } catch { - SentryManager.captureAndLog(error, message: "Error occurred while attempting to install package from \(packageURL): \(String(describing: error))") + let message = "Error occurred while attempting to install package from \(packageURL): \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message: message) return nil } } @@ -560,7 +570,9 @@ public class ResourceDownloadManager { try handler?(package, error) self.resourceDownloadCompleted(with: package) } catch { - SentryManager.captureAndLog(error, message: "Unhandled error occurred after resource successfully downloaded: \(String(describing: error))") + let message = "Unhandled error occurred after resource successfully downloaded: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message: message) self.resourceDownloadFailed(withKey: packageKey, with: error) } @@ -615,7 +627,9 @@ public class ResourceDownloadManager { if let package = package { do { try ResourceFileManager.shared.install(resourceWithID: fullID, from: package) - SentryManager.breadcrumbAndLog("successfully parsed the keyboard in: \(package.sourceFolder)") + let message = "successfully parsed the keyboard in: \(package.sourceFolder)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) // Maintains legacy behavior; automatically sets the newly-downloaded keyboard as active. if let keyboard = package.findResource(withID: fullID) { @@ -626,13 +640,18 @@ public class ResourceDownloadManager { self.downloadLexicalModelsForLanguageIfExists(languageID: fullID.languageID) } } catch { - SentryManager.captureAndLog(error, message: "Keyboard installation error: \(String(describing: error))") + let message = "Keyboard installation error: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message: message) } } else if let error = error { // Often a download error. - log.error("Installation failed: \(String(describing: error))") + let errorMessage = "Installation failed: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) } else { - SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description))") + let message = "Unknown error when attempting to install \(fullID.description))" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(message) } } } @@ -643,18 +662,25 @@ public class ResourceDownloadManager { do { // A raw port of the queue's old installation method for lexical models. try ResourceFileManager.shared.finalizePackageInstall(package, isCustom: false) - SentryManager.breadcrumbAndLog("successfully parsed the lexical model in: \(package.sourceFolder)") + let message = "successfully parsed the lexical model in: \(package.sourceFolder)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) if let installedLexicalModel = package.findResource(withID: fullID) { _ = Manager.shared.registerLexicalModel(installedLexicalModel) } } catch { - SentryManager.captureAndLog(error, message: "Error installing the lexical model: \(String(describing: error))") + let message = "Error installing the lexical model: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) + SentryManager.capture(error, message: message) } } else if let error = error { - log.error("Error downloading the lexical model \(String(describing: error))") + let errorMessage = "Error downloading the lexical model \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) } else { - SentryManager.captureAndLog("Unknown error when attempting to install \(fullID.description)") + let message = "Unknown error when attempting to install \(fullID.description)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(message) } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift index 55b1809519..cb49d585b6 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceDownloadQueue.swift @@ -8,6 +8,7 @@ import Foundation import Reachability +import os.log enum DownloadNode { case simpleBatch(AnyDownloadBatch) @@ -279,7 +280,9 @@ class ResourceDownloadQueue: HTTPDownloadDelegate { do { try reachability = Reachability(hostname: KeymanHosts.API_KEYMAN_COM.host!) } catch { - SentryManager.captureAndLog("Could not start Reachability object: \(error)") + let message = "Could not start Reachability object: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message: message) } 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 d3831413cb..1715c43d7b 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift @@ -8,6 +8,7 @@ import Foundation import UIKit +import os.log /** * This class stores common methods used for installing language resources, regardless of source. @@ -40,7 +41,9 @@ public class ResourceFileManager { do { try Storage.active.installDefaultKeyboard(from: Resources.bundle) } catch { - SentryManager.captureAndLog(error, message: "Failed to copy default keyboard from bundle: \(error)") + let message = "Failed to copy default keyboard from bundle: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) } } Migrations.engineVersion = Version.latestFeature @@ -154,7 +157,9 @@ public class ResourceFileManager { try copyWithOverwrite(from: url, to: destinationUrl) return destinationUrl } catch { - SentryManager.captureAndLog(error) + let message = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) return nil } } @@ -166,7 +171,9 @@ public class ResourceFileManager { */ public func prepareKMPInstall(from url: URL) throws -> KeymanPackage { // Once selected, start the standard install process. - SentryManager.breadcrumbAndLog("Opening KMP from \(url)") + let message = "Opening KMP from \(url)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) // Step 1: Copy it to a temporary location, making it a .zip in the process let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] @@ -176,7 +183,8 @@ public class ResourceFileManager { do { try copyWithOverwrite(from: url, to: archiveUrl) } catch { - log.error(error) + let errorMessage = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) throw KMPError.copyFiles } @@ -208,7 +216,8 @@ public class ResourceFileManager { let kmp = try prepareKMPInstall(from: url) completionHandler(kmp, nil) } catch { - log.error(error) + let errorMessage = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) completionHandler(nil, error) } } @@ -333,7 +342,8 @@ public class ResourceFileManager { try finalizePackageInstall(package, isCustom: isCustom) completionHandler(nil) } catch { - log.error(error) + let errorMessage = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) completionHandler(error) } } @@ -360,7 +370,8 @@ public class ResourceFileManager { where FullID.Resource.Package: TypedKeymanPackage { if fullIDs.contains(where: { package.findResource(withID: $0) == nil }) { let missingResource = fullIDs.first(where: { package.findResource(withID: $0) == nil })! - log.error("Resource with full ID \(missingResource.description) not in package") + let errorMessage = "Resource with full ID \(missingResource.description) not in package" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) throw KMPError.resourceNotInPackage } @@ -376,7 +387,8 @@ public class ResourceFileManager { try copyWithOverwrite(from: package.sourceFolder, to: Storage.active.packageDir(for: package)!) } catch { - log.error("Could not create installation directory and/or copy resources: \(error)") + let errorMessage = "Could not create installation directory and/or copy resources: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) throw KMPError.fileSystem } @@ -413,7 +425,9 @@ public class ResourceFileManager { if !FileManager.default.fileExists(atPath: path) { // 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") + let message = "Could not add resource of type: \(resource.fullID.type) with ID: \(resource.id) because the resource file does not exist" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(message) return } @@ -445,6 +459,8 @@ public class ResourceFileManager { userDefaults.set([Date()], forKey: Key.synchronizeSWKeyboard) userDefaults.synchronize() - SentryManager.breadcrumbAndLog("Added \(resource.fullID.type) with ID: \(resource.id) and language code: \(resource.languageID)") + let message = "Added \(resource.fullID.type) with ID: \(resource.id) and language code: \(resource.languageID)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Storage.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Storage.swift index a344d445b9..93b2224a3f 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Storage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Storage.swift @@ -8,6 +8,7 @@ import Foundation import Sentry +import os.log // MARK: - Static members extension Storage { @@ -17,7 +18,9 @@ extension Storage { if paths.isEmpty { return nil } - SentryManager.breadcrumbAndLog("Storage.nonShared: using path \(paths[0])") + let message = "Storage.nonShared: using path \(paths[0])" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) return Storage(baseURL: paths[0], userDefaults: UserDefaults.standard) }() @@ -81,7 +84,9 @@ class Storage { attributes: nil) return newDir } catch { - SentryManager.captureAndLog(error, message: "Failed to create subdirectory at \(newDir)") + let message = ("Failed to create subdirectory at \(newDir)") + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) return nil } } @@ -198,7 +203,9 @@ class Storage { contents = try FileManager.default.contentsOfDirectory(at: keyboardDir, includingPropertiesForKeys: [.isDirectoryKey]) } catch { - SentryManager.captureAndLog(error, message: "Failed to list contents at \(keyboardDir) with error \(error)") + let message = ("Failed to list contents at \(keyboardDir) with error \(error)") + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) return nil } return contents.filter { url in @@ -206,7 +213,9 @@ class Storage { let values = try url.resourceValues(forKeys: [.isDirectoryKey]) return values.isDirectory ?? false } catch { - SentryManager.captureAndLog(error) + let message = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error) return false } } @@ -270,7 +279,9 @@ extension Storage { let package = try ResourceFileManager.shared.prepareKMPInstall(from: defaultKMPFile) as! KeyboardKeymanPackage try ResourceFileManager.shared.install(resourceWithID: Defaults.keyboard.fullID, from: package) } catch { - SentryManager.captureAndLog(error, message: "Failed to install the default keyboard from the bundled KMP: \(error)") + let message = "Failed to install the default keyboard from the bundled KMP: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) } } @@ -294,13 +305,17 @@ extension Storage { // Install all languages for the model, not just the default-listed one. try ResourceFileManager.shared.install(resourcesWithIDs: package.installables[0].map { $0.fullID }, from: package) } catch { - SentryManager.captureAndLog(error, message: "Failed to install the default lexical model from the bundled KMP: \(error)") + let message = "Failed to install the default lexical model from the bundled KMP: \(error)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) + SentryManager.capture(error, message:message) } } func copyFiles(to dst: Storage) throws { if FileManager.default.fileExists(atPath: dst.baseDir.path) { - SentryManager.breadcrumbAndLog("Deleting \(dst.baseDir) for copy from \(baseDir) to \(dst.baseDir)") + let message = "Deleting \(dst.baseDir) for copy from \(baseDir) to \(dst.baseDir)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) try FileManager.default.removeItem(at: dst.baseDir) } try FileManager.default.copyItem(at: baseDir, to: dst.baseDir) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/ResourceInfoViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/ResourceInfoViewController.swift index 088af30366..13b7ba9e88 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/ResourceInfoViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/ResourceInfoViewController.swift @@ -9,6 +9,7 @@ import Foundation import UIKit import WebKit +import os.log class ResourceInfoViewController: UIViewController, UIAlertViewDelegate, UITableViewDelegate, UITableViewDataSource { // The data backing our UI text in the UITableView. @@ -84,7 +85,8 @@ class ResourceInfoViewController: UIViewController, if let qrImg = generateQRCode(from: resourceURL) { qrImageView.image = qrImg } else { - log.info("Unable to generate QR code for URL: \(resourceURL)") + let message = "Unable to generate QR code for URL: \(resourceURL)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) } } else { // No resource-sharing link available. Hide the text label! @@ -117,7 +119,7 @@ class ResourceInfoViewController: UIViewController, override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.setToolbarHidden(true, animated: true) - log.info("didAppear: ResourceInfoViewController") + os_log("didAppear: ResourceInfoViewController", log:KeymanEngineLogger.resources, type: .info) } func numberOfSections(in tableView: UITableView) -> Int { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift index 8b67cc0ff8..bf98d9de07 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/InstalledLanguagesViewController.swift @@ -8,6 +8,7 @@ // import QuartzCore import UIKit +import os.log private let activityViewTag = -2 private let toolbarButtonTag = 100 @@ -88,7 +89,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie navigationItem.rightBarButtonItem = addButton } - log.info("viewDidLoad: InstalledLanguagesViewController (registered for keyboardDownloadStarted)") + os_log("viewDidLoad: InstalledLanguagesViewController (registered for keyboardDownloadStarted)", log:KeymanEngineLogger.resources, type: .info) } override public func viewWillAppear(_ animated: Bool) { @@ -104,8 +105,8 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie if numberOfSections(in: tableView) == 0 { showActivityView() } - log.info("didAppear: InstalledLanguagesViewController") - + os_log("didAppear: InstalledLanguagesViewController", log:KeymanEngineLogger.ui, type: .info) + // Are there updates worth doing? if isDidUpdateCheck { // Nope; don't make an 'update' button. @@ -171,11 +172,14 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie for lm in userLexicalModels { let l = lm.languageID if let langName = keyboardLanguages[l]?.name { - log.info("keyboard language \(l) \(langName) has lexical model") + let message = "keyboard language \(l) \(langName) has lexical model" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) } else { // Legacy behavior: we automatically install all MTNT language codes, even without // a matching keyboard for the more specific variant(s). - SentryManager.breadcrumbAndLog("lexical model language \(l) has no keyboard installed!") + let message = "lexical model language \(l) has no keyboard installed!" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.breadcrumb(message) } } @@ -279,7 +283,8 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie } private func packageDownloadStarted(key: KeymanPackage.Key) { - log.info("download started for \(key.type.rawValue): InstalledLanguagesViewController") + let message = "download started for \(key.type.rawValue): InstalledLanguagesViewController" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) view.isUserInteractionEnabled = false navigationItem.setHidesBackButton(true, animated: true) @@ -299,7 +304,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie } private func packageDownloadCompleted(package: KeymanPackage) { - log.info("lexicalModelDownloadCompleted: InstalledLanguagesViewController") + os_log("lexicalModelDownloadCompleted: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info) DispatchQueue.main.async { var msg: String @@ -331,7 +336,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie return } - log.info("keyboardDownloadFailed: InstalledLanguagesViewController") + os_log("keyboardDownloadFailed: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info) DispatchQueue.main.async { var msg: String @@ -353,7 +358,7 @@ public class InstalledLanguagesViewController: UITableViewController, UIAlertVie } private func batchUpdateStarted(_: [AnyLanguageResource]) { - log.info("batchUpdateStarted: InstalledLanguagesViewController") + os_log("batchUpdateStarted: InstalledLanguagesViewController", log:KeymanEngineLogger.resources, type: .info) view.isUserInteractionEnabled = false navigationItem.setHidesBackButton(true, animated: true) @@ -447,7 +452,8 @@ extension InstalledLanguagesViewController { case .error(let error): // Note: Errors may result from network issues. if let error = error { - log.error(String(describing: error)) + let errorMessage = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) } case .success(let package, let fullID): ResourceFileManager.shared.doInstallPrompt(for: package as! KeyboardKeymanPackage, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 24a15da93c..3f0b24d704 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -8,6 +8,7 @@ import UIKit import Sentry +import os.log private let toolbarButtonTag = 100 @@ -38,7 +39,8 @@ class LanguageSettingsViewController: UITableViewController { super.viewDidLoad() let titleFormat = NSLocalizedString("menu-langsettings-title", bundle: engineBundle, comment: "") title = String.localizedStringWithFormat(titleFormat, language.name) - log.info("viewDidLoad: LanguageSettingsViewController title: \(title ?? "")") + let message = "viewDidLoad: LanguageSettingsViewController title: \(title ?? "")" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .info, message) if Manager.shared.canAddNewKeyboards { let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, @@ -50,7 +52,7 @@ class LanguageSettingsViewController: UITableViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - log.info("didAppear: LanguageSettingsViewController") + os_log("viewDidAppear: LanguageSettingsViewController", log:KeymanEngineLogger.ui, type: .info) } @@ -309,7 +311,8 @@ class LanguageSettingsViewController: UITableViewController { case .error(let error): if let error = error { // Note: Errors may result from network issues. - log.error(String(describing: error)) + let errorMessage = "\(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .error, errorMessage) } case .success(let package, let fullID): ResourceFileManager.shared.doInstallPrompt(for: package as! KeyboardKeymanPackage, @@ -346,8 +349,10 @@ class LanguageSettingsViewController: UITableViewController { // If user defaults for keyboards list does not exist, do nothing. guard let globalUserKeyboards = userData.userKeyboards else { - SentryManager.captureAndLog("no keyboards in the global keyboards list!") - return nil + let message = "no keyboards in the global keyboards list!" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + SentryManager.capture(message) + return nil } if let index = globalUserKeyboards.firstIndex(where: { $0.fullID == matchingFullID }) { @@ -361,7 +366,8 @@ class LanguageSettingsViewController: UITableViewController { event.extra = ["id": matchingFullID] SentrySDK.capture(event: event) - log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!") + let errorMessage = "this keyboard \(matchingFullID) not found among user's installed keyboards!" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) return nil } } @@ -373,7 +379,9 @@ class LanguageSettingsViewController: UITableViewController { // If user defaults for keyboards list does not exist, do nothing. guard let globalUserKeyboards = userData.userKeyboards else { - SentryManager.captureAndLog("no keyboards in the global keyboards list!") + let message = "No keyboards in the global keyboards list!" + os_log("%{public}s", log: KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture("no keyboards in the global keyboards list!") return } @@ -392,7 +400,8 @@ class LanguageSettingsViewController: UITableViewController { event.extra = ["id": matchingFullID] SentrySDK.capture(event: event) - log.error("this keyboard \(matchingFullID) not found among user's installed keyboards!") + let errorMessage = "this keyboard \(matchingFullID) not found among user's installed keyboards!" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) return } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/SettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/SettingsViewController.swift index 5b130065cd..ad65e491b4 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/SettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/SettingsViewController.swift @@ -7,6 +7,7 @@ // import UIKit +import os.log open class SettingsViewController: UITableViewController { private var itemsArray = [[String: String]]() @@ -16,7 +17,7 @@ open class SettingsViewController: UITableViewController { super.viewWillAppear(animated) loadUserLanguages() - log.info("willAppear: SettingsViewController") + os_log("viewWillAppear: SettingsViewController", log:KeymanEngineLogger.settings, type: .info) } override open func viewDidLoad() { @@ -40,7 +41,7 @@ open class SettingsViewController: UITableViewController { let sb : UIStoryboard = UIStoryboard(name: "Settings", bundle: nil) if let vc = sb.instantiateInitialViewController() { launchingVC.present(vc, animated: true, completion: { - log.info("presented settings") + os_log("presented settings", log:KeymanEngineLogger.settings, type: .info) }) } } @@ -199,7 +200,9 @@ open class SettingsViewController: UITableViewController { case "systemkeyboardsettings", "installfile", "forcederror", "spacebartext": break default: - SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")") + let message = "unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) cell.accessoryType = .none } @@ -272,7 +275,9 @@ open class SettingsViewController: UITableViewController { case "showbanner", "showgetstarted": cell.detailTextLabel?.isEnabled = false default: - SentryManager.captureAndLog("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")") + let message = "unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) } } @@ -296,7 +301,9 @@ open class SettingsViewController: UITableViewController { case "systemkeyboardsettings": guard let appSettings = URL(string: UIApplication.openSettingsURLString) else { // It is an error if the option is displayed but unusable. That's bad UI. - SentryManager.captureAndLog("Could not launch keyboard settings menu") + let message = "Could not launch keyboard settings menu" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) return } UniversalLinks.externalLinkLauncher?(appSettings) @@ -304,7 +311,9 @@ open class SettingsViewController: UITableViewController { if let block = Manager.shared.fileBrowserLauncher { block(navigationController!) } else { - SentryManager.captureAndLog("Listener for framework signal to launch file browser is missing") + let message = "Listener for framework signal to launch file browser is missing" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) } case "forcederror": SentryManager.forceError() @@ -357,9 +366,9 @@ open class SettingsViewController: UITableViewController { // In a storyboard-based application, you will often want to do a little preparation before navigation override open func prepare(for segue: UIStoryboardSegue, sender: Any?) { - log.info("prepare for segue") - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. + os_log("prepare for segue", log:KeymanEngineLogger.settings, type: .info) + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. } // MARK: - language access - @@ -405,11 +414,14 @@ open class SettingsViewController: UITableViewController { for lm in userLexicalModels { let l = lm.languageID if let langName = keyboardLanguages[l]?.name { - log.info("keyboard language \(l) \(langName) has lexical model") + let message = "keyboard language \(l) \(langName) has lexical model" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) } else { // Legacy behavior: we automatically install all MTNT language codes, even without // a matching keyboard for the more specific variant(s). - SentryManager.breadcrumbAndLog("lexical model language \(l) has no keyboard installed!") + let message = "lexical model language \(l) has no keyboard installed!" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + SentryManager.breadcrumb(message) } } @@ -438,7 +450,9 @@ open class SettingsViewController: UITableViewController { nc.pushViewController(vc, animated: true) setIsDoneButtonEnabled(nc, true) } else { - SentryManager.captureAndLog("no navigation controller for showing languages???") + let message = ("No navigation controller for showing languages???") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) } } @@ -448,7 +462,9 @@ open class SettingsViewController: UITableViewController { nc.pushViewController(vc, animated: true) setIsDoneButtonEnabled(nc, true) } else { - SentryManager.captureAndLog("no navigation controller for showing spacebarText options") + let message = ("No navigation controller for showing spacebarText options") + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) + SentryManager.capture(message) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/TextField.swift b/ios/engine/KMEI/KeymanEngine/Classes/TextField.swift index 42f8ea8297..5b68f0f958 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/TextField.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/TextField.swift @@ -8,6 +8,7 @@ import AudioToolbox import UIKit +import os.log public class TextField: UITextField, KeymanResponder { // viewController should be set to main view controller to enable keyboard picker. @@ -83,7 +84,7 @@ public class TextField: UITextField, KeymanResponder { } if delegate !== delegateProxy { - log.error("Trying to set TextField's delegate directly. Use setKeymanDelegate() instead.") + os_log("Trying to set TextField's delegate directly. Use setKeymanDelegate() instead.", log:KeymanEngineLogger.ui, type: .error) } super.delegate = delegateProxy } @@ -95,7 +96,8 @@ public class TextField: UITextField, KeymanResponder { // All of the normal UITextFieldDelegate methods are supported. public func setKeymanDelegate(_ keymanDelegate: TextFieldDelegate?) { delegateProxy.keymanDelegate = keymanDelegate - log.debug("TextField: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)") + let message = "TextField: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message) } public override var text: String! { @@ -150,7 +152,8 @@ public class TextField: UITextField, KeymanResponder { resignFirstResponder() becomeFirstResponder() } - log.debug("TextField \(self.hashValue) setFont: \(font?.familyName ?? "nil")") + let message = "TextField \(self.hashValue) setFont: \(font?.familyName ?? "nil")" + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .debug, message) } @objc func enableInputClickSound() { @@ -175,7 +178,8 @@ extension KeymanResponder where Self: TextField { // Dismisses the keyboard if this textview is the first responder. // - Use this instead of [resignFirstResponder] as it also resigns the Keyman keyboard's responders. public func dismissKeyboard() { - log.debug("TextField: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)") + let message = "TextField: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message) resignFirstResponder() Manager.shared.inputViewController.endEditing(true) } @@ -262,10 +266,12 @@ extension TextField: UITextFieldDelegate { font = UIFont.systemFont(ofSize: fontSize) } - log.debug("TextField: \(self.hashValue) setFont: \(font?.familyName ?? "nil")") + let messageOne = "TextField: \(self.hashValue) setFont: \(font?.familyName ?? "nil")" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageOne) - log.debug("TextField: \(self.hashValue) Became first responder. Value: \(String(describing: text))") - } + let messageTwo = "TextField: \(self.hashValue) Became first responder. Value: \(String(describing: text))" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageTwo) +} public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { if textField == self { diff --git a/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift b/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift index 8f0ab0c639..e8d970fdd6 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/TextView.swift @@ -8,6 +8,7 @@ import AudioToolbox import UIKit +import os.log public class TextView: UITextView, KeymanResponder { // viewController should be set to main view controller to enable keyboard picker. @@ -80,8 +81,8 @@ public class TextView: UITextView, KeymanResponder { } if delegate !== delegateProxy { - log.error("Trying to set TextView's delegate directly. Use setKeymanDelegate() instead.") - } + os_log("Trying to set TextView's delegate directly. Use setKeymanDelegate() instead.", log:KeymanEngineLogger.ui, type: .error) + } super.delegate = delegateProxy } } @@ -92,7 +93,8 @@ public class TextView: UITextView, KeymanResponder { // - All of the normal UITextViewDelegate methods are supported. public func setKeymanDelegate(_ keymanDelegate: TextViewDelegate?) { delegateProxy.keymanDelegate = keymanDelegate - log.debug("TextView: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)") + let message = "TextView: \(self.hashValue) keymanDelegate set to: \(keymanDelegate.debugDescription)" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message) } public override var text: String! { @@ -138,7 +140,8 @@ public class TextView: UITextView, KeymanResponder { becomeFirstResponder() } - log.debug("TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")") + let message = "TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message) } // MARK: iOS 7 TextView Scroll bug fix @@ -173,7 +176,8 @@ extension KeymanResponder where Self: TextView { // Dismisses the keyboard if this textview is the first responder. // - Use this instead of [resignFirstResponder] as it also resigns the Keyman keyboard's responders. public func dismissKeyboard() { - log.debug("TextView: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)") + let message = "TextView: \(self.hashValue) dismissing keyboard. Was first responder: \(isFirstResponder)" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, message) resignFirstResponder() Manager.shared.inputViewController.endEditing(true) } @@ -256,9 +260,11 @@ extension TextView: UITextViewDelegate { font = UIFont.systemFont(ofSize: fontSize) } - log.debug("TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")") + let messageOne = "TextView: \(self.hashValue) setFont: \(font?.familyName ?? "nil")" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageOne) - log.debug("TextView: \(self.hashValue) Became first responder. Value: \(String(describing: text))") + let messageTwo = "TextView: \(self.hashValue) Became first responder. Value: \(String(describing: text))" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .debug, messageTwo) } public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, diff --git a/ios/engine/KMEI/KeymanEngineLogger.swift b/ios/engine/KMEI/KeymanEngineLogger.swift index 34500609b3..c6caa32e01 100644 --- a/ios/engine/KMEI/KeymanEngineLogger.swift +++ b/ios/engine/KMEI/KeymanEngineLogger.swift @@ -6,7 +6,17 @@ * * Created by Shawn Schantz on 2024-01-16. * - * Basic logging capability provided by wrapping Apple's Unified Logging Framework. + * Basic logging capability provided by wrapping Apple's Unified Logging + * Framework. Works with iOS 10 and later. + * Log statements are written with a call the `os_log` API which takes a + * message, an `OSLog` reference and log level. + * Several static references to `OSLog` objects are defined, each of which + * determines the subsystem and category of subsequent log statements. + * Both subsystem and category are useful for filtering with the Console app. + * Use the same subsystem name for all `OSLog` objects created here, but specify + * a unique category to identify related functionality. + * Add as many static `OSLog` resources as necessary to support new log + * categories. */ import Foundation @@ -15,8 +25,9 @@ import os.log private let subsystem = "org.sil.KeymanEngine4Mac" public struct KeymanEngineLogger { - static let engine = OSLog(subsystem: subsystem, category: "engine") - static let migration = OSLog(subsystem: subsystem, category: "migration") - static let sentry = OSLog(subsystem: subsystem, category: "sentry") - static let settings = OSLog(subsystem: subsystem, category: "settings") + static let engine = OSLog(subsystem: subsystem, category: "Engine") + static let migration = OSLog(subsystem: subsystem, category: "Migration") + static let resources = OSLog(subsystem: subsystem, category: "Resources") + static let settings = OSLog(subsystem: subsystem, category: "Settings") + static let ui = OSLog(subsystem: subsystem, category: "UI") } diff --git a/ios/engine/KMEI/KeymanEngineTests/KeyboardScaleTests.swift b/ios/engine/KMEI/KeymanEngineTests/KeyboardScaleTests.swift index 1907ab1e50..09ead611c0 100644 --- a/ios/engine/KMEI/KeymanEngineTests/KeyboardScaleTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/KeyboardScaleTests.swift @@ -9,6 +9,7 @@ import XCTest @testable import KeymanEngine import DeviceKit +import os.log extension KeyboardSize: Equatable { public static func == (lhs: KeyboardSize, rhs: KeyboardSize) -> Bool { @@ -51,7 +52,8 @@ class KeyboardScaleTests: XCTestCase { func testScaleForUnknown() { let device = Device.unknown("granny smith") - log.info(device.isPad) + let message = "\(device.isPad)" + os_log("%{public}s", log:KeymanEngineLogger.ui, type: .info, message) let size1 = CGSize(width: 375, height: 667) // iPhone 8 device height let mappedScale1 = KeyboardScaleMap.getDeviceDefaultKeyboardScale(forPortrait: true, diff --git a/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift b/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift index baf61ee487..adfb6e45aa 100644 --- a/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift @@ -127,13 +127,11 @@ class KeymanPackageTests: XCTestCase { do { if let kmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) { - log.info("*** first unzip of \(kmp.id)") do { // clear directory before second extract try KeymanPackage.clearDirectory(destination: destinationFolderURL) if let secondKmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) { - log.info("*** second unzip of \(secondKmp.id)") } else { XCTAssert(false, "*** second unzip failed") } diff --git a/ios/engine/KMEI/KeymanEngineTests/ResourceUpdateTests.swift b/ios/engine/KMEI/KeymanEngineTests/ResourceUpdateTests.swift index 3945f9212e..fd9af29b6f 100644 --- a/ios/engine/KMEI/KeymanEngineTests/ResourceUpdateTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/ResourceUpdateTests.swift @@ -7,6 +7,7 @@ // import XCTest +import os.log @testable import KeymanEngine class ResourceUpdateTests: XCTestCase { @@ -60,7 +61,7 @@ class ResourceUpdateTests: XCTestCase { // Uses the XCTest 'attachment' system to retrieve the desired file. self.add(try TestUtils.EngineStateBundler.createBundle(withName: "khmer_angkor update base")) - log.info("Bundle archived and attached to test's report.") + os_log("Bundle archived and attached to test's report.", log:KeymanEngineLogger.resources, type: .info) } func testCacheCurrent() { diff --git a/ios/engine/KMEI/KeymanEngineTests/TestUtils/EngineStateBundler.swift b/ios/engine/KMEI/KeymanEngineTests/TestUtils/EngineStateBundler.swift index b8950dc9c2..fa566d9012 100644 --- a/ios/engine/KMEI/KeymanEngineTests/TestUtils/EngineStateBundler.swift +++ b/ios/engine/KMEI/KeymanEngineTests/TestUtils/EngineStateBundler.swift @@ -9,6 +9,7 @@ import Foundation import XCTest import ZIPFoundation +import os.log @testable import KeymanEngine @@ -41,13 +42,16 @@ extension TestUtils { try FileManager.default.createDirectory(at: bundleConstructionURL.appendingPathComponent("Library"), withIntermediateDirectories: true, attributes: nil) - log.info("Documents directory source: \(documentsDirectory)") + let messageOne = "Documents directory source: \(documentsDirectory)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageOne) try FileManager.default.copyItem(at: documentsDirectory, to: bundleConstructionURL.appendingPathComponent("Documents")) - log.info("Library source: \(storageDirectory)") + let messageTwo = "Library source: \(storageDirectory)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageTwo) try FileManager.default.copyItem(at: storageDirectory, to: bundleConstructionURL.appendingPathComponent("Library").appendingPathComponent("keyman")) - log.info("Preferences directory source: \(preferencesDirectory)") + let messageThree = "Preferences directory source: \(preferencesDirectory)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, messageThree) let pListPath = bundleConstructionURL.appendingPathComponent("Library").appendingPathComponent("Preferences") let testEngineFilename = "com.keyman.testing.KeymanEngineTestHost.plist" @@ -68,7 +72,8 @@ extension TestUtils { let archiveURL = bundleConstructionURL.appendingPathComponent("bundleArchive.zip") do { _ = try Archive(url: archiveURL, accessMode: .create) - log.info("archiveURL: \(archiveURL)") + let message = "archiveURL: \(archiveURL)" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) let attachment = XCTAttachment(contentsOfFile: archiveURL) attachment.lifetime = .keepAlways return attachment diff --git a/ios/engine/KMEI/KeymanEngineTests/TestUtils/TestUtils.swift b/ios/engine/KMEI/KeymanEngineTests/TestUtils/TestUtils.swift index aa2e09db74..57da0383b1 100644 --- a/ios/engine/KMEI/KeymanEngineTests/TestUtils/TestUtils.swift +++ b/ios/engine/KMEI/KeymanEngineTests/TestUtils/TestUtils.swift @@ -7,8 +7,9 @@ // import XCTest - import Foundation +import os.log + @testable import KeymanEngine /** @@ -40,8 +41,9 @@ enum TestUtils { try FileManager.default.removeItem(at: item) } } catch { - log.error(error) - XCTFail("Could not clear a directory because of error: \(error)") + let message = "Could not clear a directory because of error: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .debug, message) + XCTFail("Could not clear a directory because of error: \(error)") } } @@ -57,7 +59,8 @@ enum TestUtils { withIntermediateDirectories: true, attributes: nil) } catch { - log.error(error) + let message = "Could not properly erase & reinit KeymanEngine Storage because of error: \(String(describing: error))" + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .debug, message) XCTFail("Could not properly erase & reinit KeymanEngine Storage because of error: \(error)") } } diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index 9c03987338..d70bb38e94 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -10,6 +10,7 @@ import KeymanEngine import UIKit import WebKit import Sentry +import os.log @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -49,7 +50,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { vc.present(nvc, animated: true, completion: nil) } } else { - SentryManager.captureAndLog("Cannot find app's root UIViewController") + let message = "Cannot find app's root UIViewController" + os_log("%{public}s", log: KeymanLogger.ui, type: .info, message) + SentryManager.capture(message) } return true @@ -138,7 +141,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ResourceFileManager.shared.promptPackageInstall(of: package, in: nvc, isCustom: true) vc.present(nvc, animated: true, completion: nil) } else { - SentryManager.captureAndLog("Cannot find app's root UIViewController") + let message = "Cannot find app's root UIViewController" + os_log("%{public}s", log: KeymanLogger.ui, type: .error, message) + SentryManager.capture(message) } } return true diff --git a/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift b/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift index 923bcab84a..c82c8f904d 100644 --- a/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/InfoViewController/InfoViewController.swift @@ -42,7 +42,9 @@ class InfoViewController: UIViewController, WKNavigationDelegate { try networkReachable = Reachability(hostname: "keyman.com") try networkReachable?.startNotifier() } catch { - SentryManager.captureAndLog(error, message: "error starting Reachability notifier: \(error)") + let message = "error starting Reachability notifier: \(error)" + os_log("%{public}s", log:KeymanLogger.ui, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -68,7 +70,7 @@ class InfoViewController: UIViewController, WKNavigationDelegate { func webView(_ webView: WKWebView, didFailNavigation error: Error) { UIApplication.shared.isNetworkActivityIndicatorVisible = false updateButtons() - os_log("%s", log: KeymanLogger.ui, type: .error, error.localizedDescription) + os_log("%{public}s", log: KeymanLogger.ui, type: .error, error.localizedDescription) } @IBAction func back(_ sender: Any) { @@ -101,6 +103,6 @@ class InfoViewController: UIViewController, WKNavigationDelegate { let appVersion = Version.current.majorMinor let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)/" webView.load(URLRequest(url: URL(string: url)!)) - os_log("Info page URL: %s", log: KeymanLogger.ui, type: .debug, url) + os_log("Info page URL: %{public}s", log: KeymanLogger.ui, type: .debug, url) } } diff --git a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift index 2bb26f0863..b127da8319 100644 --- a/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/MainViewController/MainViewController.swift @@ -192,7 +192,7 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg userData.set(self.textView?.text, forKey: userTextKey) userData.set(self.textSize.description, forKey: userTextSizeKey) userData.synchronize() - os_log("saving text size: %s", log: KeymanLogger.settings, type: .debug, textSize.description) + os_log("saving text size: %{public}s", log: KeymanLogger.settings, type: .debug, textSize.description) } private func calculateDefaultTextSize() -> CGFloat { diff --git a/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift b/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift index 9ce55088dd..697095e7af 100644 --- a/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift +++ b/ios/keyman/Keyman/Keyman/Classes/SetUpViewController/SetUpViewController.swift @@ -36,7 +36,9 @@ class SetUpViewController: UIViewController, WKNavigationDelegate { try networkReachable = Reachability(hostname: "keyman.com") try networkReachable?.startNotifier() } catch { - SentryManager.captureAndLog(error, message: "error thrown starting Reachability notifier: \(error)") + let message = "error thrown starting Reachability notifier: \(error)" + os_log("%{public}s", log:KeymanLogger.ui, type: .error, message) + SentryManager.capture(error, message: message) } } @@ -75,6 +77,6 @@ class SetUpViewController: UIViewController, WKNavigationDelegate { let url = "\(KeymanHosts.HELP_KEYMAN_COM)/products/iphone-and-ipad/\(appVersion.plainString)" + "/start/installing-system-keyboard?embed=ios" webView.load(URLRequest(url: URL(string: url)!)) - os_log("Set up page URL: %s", log: KeymanLogger.resources, type: .debug, url) + os_log("Set up page URL: %{public}s", log: KeymanLogger.resources, type: .debug, url) } } diff --git a/ios/keyman/Keyman/Keyman/KeymanLogger.swift b/ios/keyman/Keyman/Keyman/KeymanLogger.swift index b1ff40e8d6..e3a656bc05 100644 --- a/ios/keyman/Keyman/Keyman/KeymanLogger.swift +++ b/ios/keyman/Keyman/Keyman/KeymanLogger.swift @@ -6,9 +6,17 @@ * * Created by Shawn Schantz on 2024-01-11. * - * Basic logging capability provided by wrapping Apple's Unified Logging Framework. - * Works with iOS 10 and later. - * Each time we call the `os_log` API + * Basic logging capability provided by wrapping Apple's Unified Logging + * Framework. Works with iOS 10 and later. + * Log statements are written with a call the `os_log` API which takes a + * message, an `OSLog` reference and log level. + * Several static references to `OSLog` objects are defined, each of which + * determines the subsystem and category of subsequent log statements. + * Both subsystem and category are useful for filtering with the Console app. + * Use the same subsystem name for all `OSLog` objects created here, but specify + * a unique category to identify related functionality. + * Add as many static `OSLog` resources as necessary to support new log + * categories. */ import Foundation @@ -17,7 +25,7 @@ import os.log private let subsystem = "keyman.inputmethod.Keyman" public struct KeymanLogger { - static let ui = OSLog(subsystem: subsystem, category: "ui") - static let resources = OSLog(subsystem: subsystem, category: "resources") - static let settings = OSLog(subsystem: subsystem, category: "settings") + static let ui = OSLog(subsystem: subsystem, category: "UI") + static let resources = OSLog(subsystem: subsystem, category: "Resources") + static let settings = OSLog(subsystem: subsystem, category: "Settings") } diff --git a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift index ae6a0ac385..9f4aeb6344 100644 --- a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift @@ -50,7 +50,7 @@ class ImageBannerViewController: UIViewController { let frame = CGRect(origin: self.view.frame.origin, size: size) - os_log("Rendering banner image of size %s", log: KeymanLogger.ui, type: .info, NSCoder.string(for: size)) + os_log("Rendering banner image of size %{public}s", log: KeymanLogger.ui, type: .info, NSCoder.string(for: size)) self.view.frame = frame widthConstraint?.constant = size.width diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index 1a97abc1d1..fc15184fbe 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -9,6 +9,7 @@ import KeymanEngine import UIKit import Sentry +import os.log class KeyboardViewController: InputViewController { var topBarImageSource: ImageBannerViewController! @@ -69,7 +70,9 @@ class KeyboardViewController: InputViewController { let imgPath = getTopBarImage(size: size) guard let path = imgPath else { - SentryManager.captureAndLog("No image specified for the image banner!") + let message = "No image specified for the image banner!" + os_log("%{public}s", log: KeymanLogger.ui, type: .info, message) + SentryManager.capture(message) return } From d7a2451a0fd6071912528a27c01d389f0a7db46f Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 11:29:25 +0700 Subject: [PATCH 03/11] remove XCGlogger --- ios/Cartfile | 1 - ios/Cartfile.resolved | 1 - .../KeymanEngine.xcodeproj/project.pbxproj | 8 ----- .../KMEI/KeymanEngine/Classes/Log.swift | 34 ------------------- .../KMEI/KeymanEngine/Classes/Manager.swift | 1 - .../Downloading/URLSessionMock.swift | 1 - .../Keyman/Keyman.xcodeproj/project.pbxproj | 6 ---- ios/keyman/Keyman/Keyman/AppDelegate.swift | 1 - .../SWKeyboard/KeyboardViewController.swift | 1 - 9 files changed, 54 deletions(-) delete mode 100644 ios/engine/KMEI/KeymanEngine/Classes/Log.swift diff --git a/ios/Cartfile b/ios/Cartfile index 57ec6de1e2..0dae92f36b 100644 --- a/ios/Cartfile +++ b/ios/Cartfile @@ -1,5 +1,4 @@ github "weichsel/ZIPFoundation" ~> 0.9 -github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/ios/Cartfile.resolved b/ios/Cartfile.resolved index 5245b0dfa7..95e608b932 100644 --- a/ios/Cartfile.resolved +++ b/ios/Cartfile.resolved @@ -1,5 +1,4 @@ github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.1.0" github "getsentry/sentry-cocoa" "8.15.2" -github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" github "weichsel/ZIPFoundation" "0.9.17" diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 68e21af380..16d68a8350 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -71,7 +71,6 @@ C0452BAD1F9F21270064431A /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0452BAC1F9F21270064431A /* Keyboard.swift */; }; C0452BAF1F9F22A80064431A /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0452BAE1F9F22A80064431A /* Font.swift */; }; C055E6EB1F99ED090035C2DD /* RegisteredFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = C055E6EA1F99ED090035C2DD /* RegisteredFont.swift */; }; - C05B14331FD914870082A316 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05B14321FD914870082A316 /* Log.swift */; }; C05F43311FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05F43301FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift */; }; C06085B41F9485E40057E5B9 /* UIButton+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06085B31F9485E40057E5B9 /* UIButton+Helpers.swift */; }; C06D37341F81F5C300F61AE0 /* HTTPDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED71B41F6BBFAF002A2FD6 /* HTTPDownloader.swift */; }; @@ -122,7 +121,6 @@ CE46D65D247B93C9005FD506 /* (null) in Resources */ = {isa = PBXBuildFile; }; CE5C8BE324B5B3BA00FAFB7F /* Queries+LexicalModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE224B5B3BA00FAFB7F /* Queries+LexicalModel.swift */; }; CE5C8BE524B5BD1C00FAFB7F /* QueryModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */; }; - CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */; }; CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC226522EAA001733AC /* Sentry.xcframework */; }; CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC326522EAA001733AC /* Reachability.xcframework */; }; CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */; }; @@ -393,7 +391,6 @@ C0452BAC1F9F21270064431A /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = ""; }; C0452BAE1F9F22A80064431A /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; C055E6EA1F99ED090035C2DD /* RegisteredFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisteredFont.swift; sourceTree = ""; }; - C05B14321FD914870082A316 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; C05F43301FBD62550058CBD4 /* JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder.DateDecodingStrategy+ISO8601Fallback.swift"; sourceTree = ""; }; C06085B31F9485E40057E5B9 /* UIButton+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Helpers.swift"; sourceTree = ""; }; C06D372B1F81F4E100F61AE0 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -438,7 +435,6 @@ CE5C8BE224B5B3BA00FAFB7F /* Queries+LexicalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Queries+LexicalModel.swift"; sourceTree = ""; }; CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryModelTests.swift; sourceTree = ""; }; CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; - CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; CE5EDDC226522EAA001733AC /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; CE5EDDC326522EAA001733AC /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; @@ -563,7 +559,6 @@ buildActionMask = 2147483647; files = ( 296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */, - CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */, CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */, CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */, CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */, @@ -956,7 +951,6 @@ CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */, CE5EDDC326522EAA001733AC /* Reachability.xcframework */, CE5EDDC226522EAA001733AC /* Sentry.xcframework */, - CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */, CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */, 9A079DE52231A69D00581263 /* Foundation.framework */, ); @@ -1006,7 +1000,6 @@ CEE0321224C58C90005BFC73 /* KeymanHosts.swift */, C0324B921F87689B00AF3785 /* KeymanURLProtocol.swift */, C0A93A531F8B21240079948B /* Manager.swift */, - C05B14321FD914870082A316 /* Log.swift */, CEA70CCE24CAC3AE001C12E6 /* Obsoletions.swift */, CE9E95CC24CE786900F6DD78 /* UniversalLinks.swift */, ); @@ -1442,7 +1435,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C05B14331FD914870082A316 /* Log.swift in Sources */, 9A079E43223B61AE00581263 /* FullLexicalModelID.swift in Sources */, C06D37341F81F5C300F61AE0 /* HTTPDownloader.swift in Sources */, C0452BAF1F9F22A80064431A /* Font.swift in Sources */, diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift b/ios/engine/KMEI/KeymanEngine/Classes/Log.swift deleted file mode 100644 index 7f8e47e413..0000000000 --- a/ios/engine/KMEI/KeymanEngine/Classes/Log.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// Log.swift -// KeymanEngine -// -// Created by Gabriel Wong on 2017-12-07. -// Copyright © 2017 SIL International. All rights reserved. -// - -import XCGLogger - -// From XCGLogger docs: -// Note: This creates the log object lazily, which means it's not created until it's actually needed. -public let log: XCGLogger = { - // Default: the 'console', which is read by Xcode but doesn't reach the system logs. - let mainLog = XCGLogger(identifier: "KeymanEngine", includeDefaultDestinations: false) - - // Ensures our log messages go out to the device's system log as well as the console. - let systemLogDest = AppleSystemLogDestination(identifier: "") - systemLogDest.showLogIdentifier = true - - mainLog.add(destination: systemLogDest) - - // Temporary logging level to ensure that app details are reported properly. - mainLog.outputLevel = .info - mainLog.logAppDetails() - -#if DEBUG - mainLog.outputLevel = .debug -#else - mainLog.outputLevel = .warning -#endif - - return mainLog -}() diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index 324e8cead6..f77e6407e1 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -8,7 +8,6 @@ import UIKit import WebKit -import XCGLogger import DeviceKit import Reachability import os.log diff --git a/ios/engine/KMEI/KeymanEngineTests/TestUtils/Downloading/URLSessionMock.swift b/ios/engine/KMEI/KeymanEngineTests/TestUtils/Downloading/URLSessionMock.swift index 9e63bd06d0..3fec058477 100644 --- a/ios/engine/KMEI/KeymanEngineTests/TestUtils/Downloading/URLSessionMock.swift +++ b/ios/engine/KMEI/KeymanEngineTests/TestUtils/Downloading/URLSessionMock.swift @@ -7,7 +7,6 @@ // import Foundation -import XCGLogger import XCTest extension TestUtils.Downloading { diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index 96e8cf4b62..d0d3f04083 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -156,8 +156,6 @@ CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; }; CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; }; - CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEF4E55623E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; }; CEF4E55723E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; }; CEF4E55923E967140065B9C7 /* ImageBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF4E55823E967140065B9C7 /* ImageBannerViewController.swift */; }; @@ -187,7 +185,6 @@ 296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */, CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */, CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */, - CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */, CE80AD34257F2B4B008D2150 /* KeymanEngine.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -345,7 +342,6 @@ CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; - CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; CEBD3458265511B700EB2EA8 /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = am; path = am.lproj/Localizable.strings; sourceTree = ""; }; CEEC468226F2F7BA009A5B7D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; CEEF81B92673019600EE6A07 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = ""; }; @@ -363,7 +359,6 @@ 296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */, CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */, CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */, - CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */, CE80AD33257F2B4A008D2150 /* KeymanEngine.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -550,7 +545,6 @@ CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */, CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */, CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */, - CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */, CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */, ); name = Frameworks; diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index d70bb38e94..be6667ac4a 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -63,7 +63,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { SentryManager.start() // Forces the logs to initialize, as their definitions result in lazy init. // These references have been configured to also log app details. - _ = KeymanEngine.log // In iOS 15, navigation bars become transparent by default when the edge // of the scrollable content aligns with the edge of the navigation bar. diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index fc15184fbe..9502919749 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -22,7 +22,6 @@ class KeyboardViewController: InputViewController { // is enabled. They seem to get blocked otherwise, except in the Simulator. SentryManager.start(sendingEnabled: true) } - _ = KeymanEngine.log Manager.applicationGroupIdentifier = "group.KM4I" From 15609864a5b1b6c8eb3cfaefe8aad907b3c517da Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 11:32:06 +0700 Subject: [PATCH 04/11] remove xcglogger from cartfile --- oem/firstvoices/ios/Cartfile | 1 - oem/firstvoices/ios/Cartfile.resolved | 1 - 2 files changed, 2 deletions(-) diff --git a/oem/firstvoices/ios/Cartfile b/oem/firstvoices/ios/Cartfile index 57ec6de1e2..0dae92f36b 100644 --- a/oem/firstvoices/ios/Cartfile +++ b/oem/firstvoices/ios/Cartfile @@ -1,5 +1,4 @@ github "weichsel/ZIPFoundation" ~> 0.9 -github "keymanapp/dependency-XCGLogger" "master" github "devicekit/DeviceKit" ~> 5.0 github "ashleymills/Reachability.swift" github "getsentry/sentry-cocoa" ~> 8.7.0 diff --git a/oem/firstvoices/ios/Cartfile.resolved b/oem/firstvoices/ios/Cartfile.resolved index 5245b0dfa7..95e608b932 100644 --- a/oem/firstvoices/ios/Cartfile.resolved +++ b/oem/firstvoices/ios/Cartfile.resolved @@ -1,5 +1,4 @@ github "ashleymills/Reachability.swift" "v5.1.0" github "devicekit/DeviceKit" "5.1.0" github "getsentry/sentry-cocoa" "8.15.2" -github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113" github "weichsel/ZIPFoundation" "0.9.17" From 2604058212980b0c90b12a2f3a9de265af94a211 Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 13:30:24 +0700 Subject: [PATCH 05/11] remove ObjcExceptionBridging.xcframework from project --- ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj | 4 ---- ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj | 6 ------ 2 files changed, 10 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 16d68a8350..78edc183c8 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -123,7 +123,6 @@ CE5C8BE524B5BD1C00FAFB7F /* QueryModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */; }; CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC226522EAA001733AC /* Sentry.xcframework */; }; CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC326522EAA001733AC /* Reachability.xcframework */; }; - CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */; }; CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */; }; CE67D961228A6F190029F2B5 /* KeyboardCommandStructs.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE67D960228A6F190029F2B5 /* KeyboardCommandStructs.swift */; }; CE71705823A9C14D00A924A1 /* ResourceFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE71705723A9C14D00A924A1 /* ResourceFileManager.swift */; }; @@ -437,7 +436,6 @@ CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; CE5EDDC226522EAA001733AC /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; CE5EDDC326522EAA001733AC /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; - CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; CE67D960228A6F190029F2B5 /* KeyboardCommandStructs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardCommandStructs.swift; sourceTree = ""; }; CE71705723A9C14D00A924A1 /* ResourceFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceFileManager.swift; sourceTree = ""; }; CE71705E23A9C97F00A924A1 /* PackageInstallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageInstallViewController.swift; sourceTree = ""; }; @@ -562,7 +560,6 @@ CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */, CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */, CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */, - CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -948,7 +945,6 @@ isa = PBXGroup; children = ( 296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */, - CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */, CE5EDDC326522EAA001733AC /* Reachability.xcframework */, CE5EDDC226522EAA001733AC /* Sentry.xcframework */, CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */, diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index d0d3f04083..d1c3f929be 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -150,8 +150,6 @@ CEBD34322654FE3500EB2EA8 /* KeymanEngine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */; }; CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */; }; CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD343D2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */; }; - CEBD343E2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD343F2654FEB400EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */; }; CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; }; @@ -181,7 +179,6 @@ dstSubfolderSpec = 10; files = ( CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */, - CEBD343E2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */, 296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */, CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */, CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */, @@ -339,7 +336,6 @@ CEACC90F25F07D5A006EAB45 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/Localizable.strings; sourceTree = ""; }; CEACC91225F07D77006EAB45 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; - CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; CEBD3458265511B700EB2EA8 /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = am; path = am.lproj/Localizable.strings; sourceTree = ""; }; @@ -355,7 +351,6 @@ buildActionMask = 2147483647; files = ( CEBD343F2654FEB400EB2EA8 /* Reachability.xcframework in Frameworks */, - CEBD343D2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */, 296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */, CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */, CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */, @@ -542,7 +537,6 @@ children = ( 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */, CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */, - CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */, CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */, CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */, CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */, From cdd0820ce96f2b786ac63d675d5b163bdd4c9fa8 Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 15:18:37 +0700 Subject: [PATCH 06/11] fix compile error in conditionally compiled non-debug code --- ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index 69bb9d4662..bb825f4d90 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -29,7 +29,7 @@ public class SentryManager { os_log("Sentry error logging disabled for development mode.", log: KeymanEngineLogger.settings, type: .debug) #else let allowEnabled = true - os_log("Sentry error logging enabled.", log: KeymanEngineLogger.sentry, type: .debug) + os_log("Sentry error logging enabled.", log: KeymanEngineLogger.settings, type: .debug) #endif let infoDict = Bundle(for: SentryManager.self).infoDictionary From 494a2403831d7be951d0e726fbb7aba536e9508e Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 18:10:17 +0700 Subject: [PATCH 07/11] remove XCGLogger from FV --- .../ios/FirstVoices.xcodeproj/project.pbxproj | 22 ++++--------- .../ios/FirstVoices/AppDelegate.swift | 7 ----- .../ios/FirstVoices/FVLogger.swift | 31 +++++++++++++++++++ .../SWKeyboard/FVInputViewController.swift | 9 ++---- 4 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 oem/firstvoices/ios/FirstVoices/FVLogger.swift diff --git a/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj b/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj index dcb81b684f..7925b00543 100644 --- a/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj +++ b/oem/firstvoices/ios/FirstVoices.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 2990208E278FF402004F18CF /* KeyboardTableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2990208D278FF402004F18CF /* KeyboardTableController.swift */; }; 2990209127901DD7004F18CF /* KeyboardDetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2990209027901DD7004F18CF /* KeyboardDetailCell.swift */; }; 2993F6E62791621D009E1352 /* KeyboardDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2993F6E52791621D009E1352 /* KeyboardDetailController.swift */; }; + 29B30C252B5A8A4800C342A4 /* FVLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C242B5A8A4800C342A4 /* FVLogger.swift */; }; + 29B30C262B5A8A4800C342A4 /* FVLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B30C242B5A8A4800C342A4 /* FVLogger.swift */; }; 37C183B922932AD2009F9EFD /* FVKeyboardList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C183B822932AD2009F9EFD /* FVKeyboardList.swift */; }; 37C183BD2293321C009F9EFD /* Instructions in Resources */ = {isa = PBXBuildFile; fileRef = 37C183BC2293321C009F9EFD /* Instructions */; }; 37EA1F23228A1F57003E710C /* FVInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37EA1F22228A1F57003E710C /* FVInputViewController.swift */; }; @@ -52,20 +54,14 @@ CEBD34032654D41200EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; }; CEBD341B2654D76600EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD341D2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; }; - CEBD341E2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD341F2654D76900EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; }; CEBD34202654D76900EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD34222654D76B00EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; }; CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; }; - CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */; }; CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */; }; - CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */; }; CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */; }; CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */; }; - CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -76,11 +72,9 @@ dstSubfolderSpec = 10; files = ( CEBD34202654D76900EB2EA8 /* Reachability.xcframework in Embed Frameworks */, - CEBD341E2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */, 290FB4642AFB358F00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */, CEBD341B2654D76600EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */, CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */, - CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */, CEBD34032654D41200EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -108,6 +102,7 @@ 2990208D278FF402004F18CF /* KeyboardTableController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTableController.swift; sourceTree = ""; }; 2990209027901DD7004F18CF /* KeyboardDetailCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDetailCell.swift; sourceTree = ""; }; 2993F6E52791621D009E1352 /* KeyboardDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDetailController.swift; sourceTree = ""; }; + 29B30C242B5A8A4800C342A4 /* FVLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVLogger.swift; sourceTree = ""; }; 37C183B822932AD2009F9EFD /* FVKeyboardList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVKeyboardList.swift; sourceTree = ""; }; 37C183BC2293321C009F9EFD /* Instructions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Instructions; path = FirstVoices/Instructions; sourceTree = ""; }; 37EA1F22228A1F57003E710C /* FVInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FVInputViewController.swift; sourceTree = ""; }; @@ -145,9 +140,7 @@ 98C9A9FA1BFC1111009E9A4F /* SWKeyboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = SWKeyboard.entitlements; sourceTree = ""; }; CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = ""; }; CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; - CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = Carthage/Build/Sentry.xcframework; sourceTree = ""; }; - CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = Carthage/Build/Reachability.xcframework; sourceTree = ""; }; /* End PBXFileReference section */ @@ -156,13 +149,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CEBD341D2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */, CEBD341F2654D76900EB2EA8 /* Reachability.xcframework in Frameworks */, 290FB4632AFB358F00249D58 /* ZIPFoundation.xcframework in Frameworks */, CEBD34222654D76B00EB2EA8 /* Sentry.xcframework in Frameworks */, CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */, CEBD34022654D41200EB2EA8 /* KeymanEngine.xcframework in Frameworks */, - CEBD34252654D76C00EB2EA8 /* XCGLogger.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -171,11 +162,9 @@ buildActionMask = 2147483647; files = ( CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */, - CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */, 290FB4652AFB359A00249D58 /* ZIPFoundation.xcframework in Frameworks */, CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */, CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */, - CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */, CEBD34472654FF3300EB2EA8 /* KeymanEngine.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -240,6 +229,7 @@ 9889E8C71BFA9CEC00019560 /* LaunchScreen.storyboard */, 9889E8C21BFA9CEC00019560 /* Main.storyboard */, 9889E8B91BFA9CEC00019560 /* Supporting Files */, + 29B30C242B5A8A4800C342A4 /* FVLogger.swift */, ); path = FirstVoices; sourceTree = ""; @@ -292,10 +282,8 @@ isa = PBXGroup; children = ( 290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */, - CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */, CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */, CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */, - CEBD340A2654D5AA00EB2EA8 /* XCGLogger.xcframework */, CEBD34052654D4EB00EB2EA8 /* DeviceKit.xcframework */, CEBD34012654D41200EB2EA8 /* KeymanEngine.xcframework */, ); @@ -501,6 +489,7 @@ 2990209127901DD7004F18CF /* KeyboardDetailCell.swift in Sources */, 37EA1F4622921BAD003E710C /* KeyboardInfo.swift in Sources */, 296E22E527ED8FA4007BDA12 /* DownloadStatusToolbar.swift in Sources */, + 29B30C252B5A8A4800C342A4 /* FVLogger.swift in Sources */, 294D40E9279FE62600DB37F6 /* KeyboardRepository.swift in Sources */, 2990208E278FF402004F18CF /* KeyboardTableController.swift in Sources */, 37EA1F31228B7661003E710C /* FVShared.swift in Sources */, @@ -513,6 +502,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 29B30C262B5A8A4800C342A4 /* FVLogger.swift in Sources */, 37EA1F32228BCDA7003E710C /* FVShared.swift in Sources */, 37EA1F23228A1F57003E710C /* FVInputViewController.swift in Sources */, ); diff --git a/oem/firstvoices/ios/FirstVoices/AppDelegate.swift b/oem/firstvoices/ios/FirstVoices/AppDelegate.swift index 325f32b134..d024bd21ec 100644 --- a/oem/firstvoices/ios/FirstVoices/AppDelegate.swift +++ b/oem/firstvoices/ios/FirstVoices/AppDelegate.swift @@ -22,13 +22,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { FVRegionStorage.upgrade() - #if DEBUG - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - #else - KeymanEngine.log.outputLevel = .warning - #endif - // Replace with your application group id Manager.applicationGroupIdentifier = FVConstants.groupID Manager.shared.spacebarText = .KEYBOARD diff --git a/oem/firstvoices/ios/FirstVoices/FVLogger.swift b/oem/firstvoices/ios/FirstVoices/FVLogger.swift new file mode 100644 index 0000000000..f72734ad1e --- /dev/null +++ b/oem/firstvoices/ios/FirstVoices/FVLogger.swift @@ -0,0 +1,31 @@ +/** + * Keyman is copyright (C) SIL International. MIT License. + * + * FVLogger.swift + * FirstVoices + * + * Created by Shawn Schantz on 2024-01-19. + * + * Basic logging capability provided by wrapping Apple's Unified Logging + * Framework. Works with iOS 10 and later. + * Log statements are written with a call the `os_log` API which takes a + * message, an `OSLog` reference and log level. + * Several static references to `OSLog` objects are defined, each of which + * determines the subsystem and category of subsequent log statements. + * Both subsystem and category are useful for filtering with the Console app. + * Use the same subsystem name for all `OSLog` objects created here, but specify + * a unique category to identify related functionality. + * Add as many static `OSLog` resources as necessary to support new log + * categories. + */ + +import Foundation +import os.log + +private let subsystem = "keyman.inputmethod.Keyman" + +public struct FVLogger { + static let ui = OSLog(subsystem: subsystem, category: "UI") + static let resources = OSLog(subsystem: subsystem, category: "Resources") + static let settings = OSLog(subsystem: subsystem, category: "Settings") +} diff --git a/oem/firstvoices/ios/SWKeyboard/FVInputViewController.swift b/oem/firstvoices/ios/SWKeyboard/FVInputViewController.swift index a9fadbe807..7513a5c025 100644 --- a/oem/firstvoices/ios/SWKeyboard/FVInputViewController.swift +++ b/oem/firstvoices/ios/SWKeyboard/FVInputViewController.swift @@ -11,16 +11,11 @@ import KeymanEngine import UIKit +import os.log @objc(FVInputViewController) class FVInputViewController: InputViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - #if DEBUG - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - #else - KeymanEngine.log.outputLevel = .warning - #endif Manager.applicationGroupIdentifier = FVConstants.groupID @@ -77,7 +72,7 @@ class FVInputViewController: InputViewController { func setupTopBarImage(isPortrait: Bool) { let imgPath = getTopBarImage(isPortrait: isPortrait) guard let path = imgPath else { - log.error("No image specified for the image banner!") + os_log("No image specified for the image banner!", log: FVLogger.ui, type: .error) return } From a61fb788608a1160fc17038c737432a1889f94cd Mon Sep 17 00:00:00 2001 From: sgschantz Date: Fri, 19 Jan 2024 22:51:20 +0700 Subject: [PATCH 08/11] remove XCGLogger from sample projects, correct subsystem names --- .../KMEI/KeymanEngine.xcodeproj/project.pbxproj | 2 +- .../Classes}/KeymanEngineLogger.swift | 2 +- ios/keyman/Keyman/Keyman/KeymanLogger.swift | 2 +- .../KMSample1.xcodeproj/project.pbxproj | 12 ------------ .../KMSample2.xcodeproj/project.pbxproj | 16 ---------------- oem/firstvoices/ios/FirstVoices/FVLogger.swift | 2 +- 6 files changed, 4 insertions(+), 32 deletions(-) rename ios/engine/KMEI/{ => KeymanEngine/Classes}/KeymanEngineLogger.swift (96%) diff --git a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj index 78edc183c8..0490f26550 100644 --- a/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj +++ b/ios/engine/KMEI/KeymanEngine.xcodeproj/project.pbxproj @@ -919,7 +919,6 @@ F243887314BBD43000A3E055 = { isa = PBXGroup; children = ( - 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */, C06D372C1F81F4E100F61AE0 /* KeymanEngine */, 9A079DD0223194B100581263 /* KeymanEngineTests */, CE1679B8265F315C008D6FCE /* Demos */, @@ -993,6 +992,7 @@ CE7A26DA23CEEF640005955C /* Colors.swift */, CE7A26D723CEEC630005955C /* Colors+Extension.swift */, C0959CD31F99C44E00B616BC /* Constants.swift */, + 29B30C222B564F9900C342A4 /* KeymanEngineLogger.swift */, CEE0321224C58C90005BFC73 /* KeymanHosts.swift */, C0324B921F87689B00AF3785 /* KeymanURLProtocol.swift */, C0A93A531F8B21240079948B /* Manager.swift */, diff --git a/ios/engine/KMEI/KeymanEngineLogger.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift similarity index 96% rename from ios/engine/KMEI/KeymanEngineLogger.swift rename to ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift index c6caa32e01..f4ad65cbc1 100644 --- a/ios/engine/KMEI/KeymanEngineLogger.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift @@ -22,7 +22,7 @@ import Foundation import os.log -private let subsystem = "org.sil.KeymanEngine4Mac" +private let subsystem = "org.sil.Keyman.ios.Engine" public struct KeymanEngineLogger { static let engine = OSLog(subsystem: subsystem, category: "Engine") diff --git a/ios/keyman/Keyman/Keyman/KeymanLogger.swift b/ios/keyman/Keyman/Keyman/KeymanLogger.swift index e3a656bc05..749a2054de 100644 --- a/ios/keyman/Keyman/Keyman/KeymanLogger.swift +++ b/ios/keyman/Keyman/Keyman/KeymanLogger.swift @@ -22,7 +22,7 @@ import Foundation import os.log -private let subsystem = "keyman.inputmethod.Keyman" +private let subsystem = "Tavultesoft.Keyman" public struct KeymanLogger { static let ui = OSLog(subsystem: subsystem, category: "UI") diff --git a/ios/samples/KMSample1/KMSample1.xcodeproj/project.pbxproj b/ios/samples/KMSample1/KMSample1.xcodeproj/project.pbxproj index 86e6bc7d2d..e0fee90879 100644 --- a/ios/samples/KMSample1/KMSample1.xcodeproj/project.pbxproj +++ b/ios/samples/KMSample1/KMSample1.xcodeproj/project.pbxproj @@ -18,14 +18,10 @@ CEBD337E26549EC100EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD33C62654CB0E00EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */; }; CEBD33C72654CB0F00EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD33C82654CB1000EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */; }; - CEBD33C92654CB1100EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD33CA2654CB1300EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */; }; CEBD33CB2654CB1300EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD33CC2654CB1400EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */; }; CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; }; - CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CED2AC85260AFADC00A408D2 /* ekwtamil99uni.kmp in Resources */ = {isa = PBXBuildFile; fileRef = CED2AC84260AFADC00A408D2 /* ekwtamil99uni.kmp */; }; /* End PBXBuildFile section */ @@ -37,11 +33,9 @@ dstSubfolderSpec = 10; files = ( CEBD33CB2654CB1300EB2EA8 /* Reachability.xcframework in Embed Frameworks */, - CEBD33C92654CB1100EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */, 290FB45C2AFB234900249D58 /* ZIPFoundation.xcframework in Embed Frameworks */, CEBD33C72654CB0F00EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */, CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */, - CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */, CEBD337E26549EC100EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -60,8 +54,6 @@ C0324B831F87172900AF3785 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = ""; }; CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; - CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; - CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; CED2AC84260AFADC00A408D2 /* ekwtamil99uni.kmp */ = {isa = PBXFileReference; lastKnownFileType = file; path = ekwtamil99uni.kmp; sourceTree = ""; }; @@ -72,13 +64,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CEBD33C82654CB1000EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */, CEBD33CA2654CB1300EB2EA8 /* Reachability.xcframework in Frameworks */, 290FB45B2AFB234900249D58 /* ZIPFoundation.xcframework in Frameworks */, CEBD33CC2654CB1400EB2EA8 /* Sentry.xcframework in Frameworks */, CEBD33C62654CB0E00EB2EA8 /* DeviceKit.xcframework in Frameworks */, CEBD337D26549EC100EB2EA8 /* KeymanEngine.xcframework in Frameworks */, - CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -128,10 +118,8 @@ isa = PBXGroup; children = ( 290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */, - CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */, CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */, CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */, - CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */, CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */, CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */, ); diff --git a/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj b/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj index 9b1620b2ad..29443db1c7 100644 --- a/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj +++ b/ios/samples/KMSample2/KMSample2.xcodeproj/project.pbxproj @@ -23,22 +23,16 @@ CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; }; CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; }; - CEBD33E52654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBD33E62654CB8800EB2EA8 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; }; CEBD33E72654CB8800EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEBD33E82654CB8800EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; }; - CEBD33E92654CB8800EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEBEFB94231FB4E000F8F67C /* banner-portrait-text.png in Resources */ = {isa = PBXBuildFile; fileRef = CEBEFB92231FB4DF00F8F67C /* banner-portrait-text.png */; }; CEBEFB95231FB4E000F8F67C /* banner-landscape-text.png in Resources */ = {isa = PBXBuildFile; fileRef = CEBEFB93231FB4DF00F8F67C /* banner-landscape-text.png */; }; CED2AC8D260B07CF00A408D2 /* ekwtamil99uni.kmp in Resources */ = {isa = PBXBuildFile; fileRef = CED2AC8C260B07CF00A408D2 /* ekwtamil99uni.kmp */; }; CEDB3276265C953A000A2009 /* KeymanEngine.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */; }; CEDB3277265C953A000A2009 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; CEDB3279265C9B15000A2009 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; }; - CEDB327A265C9B33000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; }; CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; }; CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; }; - CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -70,12 +64,10 @@ dstSubfolderSpec = 10; files = ( CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */, - CEBD33E52654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */, 290FB45F2AFB270D00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */, CEBD33E72654CB8800EB2EA8 /* Sentry.xcframework in Embed Frameworks */, CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */, CEBD33D42654CB6A00EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */, - CEBD33E92654CB8800EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -110,9 +102,7 @@ CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = ""; }; CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = ""; }; CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = ""; }; - CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = ""; }; CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = ""; }; - CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = ""; }; CEBEFB92231FB4DF00F8F67C /* banner-portrait-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "banner-portrait-text.png"; sourceTree = ""; }; CEBEFB93231FB4DF00F8F67C /* banner-landscape-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "banner-landscape-text.png"; sourceTree = ""; }; CED2AC8C260B07CF00A408D2 /* ekwtamil99uni.kmp */ = {isa = PBXFileReference; lastKnownFileType = file; path = ekwtamil99uni.kmp; sourceTree = ""; }; @@ -124,11 +114,9 @@ buildActionMask = 2147483647; files = ( CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */, - CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */, 290FB45E2AFB270D00249D58 /* ZIPFoundation.xcframework in Frameworks */, CEBD33E62654CB8800EB2EA8 /* Sentry.xcframework in Frameworks */, CEBD33D82654CB7800EB2EA8 /* DeviceKit.xcframework in Frameworks */, - CEBD33E82654CB8800EB2EA8 /* XCGLogger.xcframework in Frameworks */, CEBD33D32654CB6A00EB2EA8 /* KeymanEngine.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -137,11 +125,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CEDB327A265C9B33000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */, CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */, 290FB4602AFB271C00249D58 /* ZIPFoundation.xcframework in Frameworks */, CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */, - CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */, CEDB3279265C9B15000A2009 /* DeviceKit.xcframework in Frameworks */, CEDB3276265C953A000A2009 /* KeymanEngine.xcframework in Frameworks */, ); @@ -196,10 +182,8 @@ isa = PBXGroup; children = ( 290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */, - CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */, CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */, CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */, - CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */, CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */, CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */, ); diff --git a/oem/firstvoices/ios/FirstVoices/FVLogger.swift b/oem/firstvoices/ios/FirstVoices/FVLogger.swift index f72734ad1e..543100f6ac 100644 --- a/oem/firstvoices/ios/FirstVoices/FVLogger.swift +++ b/oem/firstvoices/ios/FirstVoices/FVLogger.swift @@ -22,7 +22,7 @@ import Foundation import os.log -private let subsystem = "keyman.inputmethod.Keyman" +private let subsystem = "com.firstvoices.keyboards" public struct FVLogger { static let ui = OSLog(subsystem: subsystem, category: "UI") From 1f88c6f438ab0fa0c420cae8b94aecff2fa50c9c Mon Sep 17 00:00:00 2001 From: sgschantz Date: Sat, 20 Jan 2024 08:41:00 +0700 Subject: [PATCH 09/11] remove obsolete log reference --- ios/samples/KMSample1/KMSample1/AppDelegate.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ios/samples/KMSample1/KMSample1/AppDelegate.swift b/ios/samples/KMSample1/KMSample1/AppDelegate.swift index ee5a0b7352..fcd919a6db 100644 --- a/ios/samples/KMSample1/KMSample1/AppDelegate.swift +++ b/ios/samples/KMSample1/KMSample1/AppDelegate.swift @@ -1,4 +1,4 @@ -// +.// // AppDelegate.swift // KMSample1 // @@ -15,8 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool { - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() return true } } From a23644c0d8431cc429293de7675dd3ae3f8ebd71 Mon Sep 17 00:00:00 2001 From: sgschantz Date: Sat, 20 Jan 2024 09:32:04 +0700 Subject: [PATCH 10/11] remove more logging remnants --- ios/samples/KMSample1/KMSample1/AppDelegate.swift | 2 +- ios/samples/KMSample2/KMSample2/AppDelegate.swift | 4 ---- .../KMSample2/SWKeyboard/KeyboardViewController.swift | 6 +----- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/ios/samples/KMSample1/KMSample1/AppDelegate.swift b/ios/samples/KMSample1/KMSample1/AppDelegate.swift index fcd919a6db..dc43cb4285 100644 --- a/ios/samples/KMSample1/KMSample1/AppDelegate.swift +++ b/ios/samples/KMSample1/KMSample1/AppDelegate.swift @@ -1,4 +1,4 @@ -.// +// // AppDelegate.swift // KMSample1 // diff --git a/ios/samples/KMSample2/KMSample2/AppDelegate.swift b/ios/samples/KMSample2/KMSample2/AppDelegate.swift index bff8a7e0a4..433816ce5c 100644 --- a/ios/samples/KMSample2/KMSample2/AppDelegate.swift +++ b/ios/samples/KMSample2/KMSample2/AppDelegate.swift @@ -15,10 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool { - // Set desired level before release - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - // Replace with your application group id // Ensure this happens before installing any keyboards or models within the engine // whenever using App Group Identifiers. diff --git a/ios/samples/KMSample2/SWKeyboard/KeyboardViewController.swift b/ios/samples/KMSample2/SWKeyboard/KeyboardViewController.swift index 7af728c78b..60a86452dc 100644 --- a/ios/samples/KMSample2/SWKeyboard/KeyboardViewController.swift +++ b/ios/samples/KMSample2/SWKeyboard/KeyboardViewController.swift @@ -11,10 +11,6 @@ import UIKit class KeyboardViewController: InputViewController { override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { - // Set desired level before release - KeymanEngine.log.outputLevel = .debug - KeymanEngine.log.logAppDetails() - // Replace with your application group id Manager.applicationGroupIdentifier = "group.KMSample" @@ -56,7 +52,7 @@ class KeyboardViewController: InputViewController { func setupTopBarImage(isPortrait: Bool) { let imgPath = getTopBarImage(isPortrait: isPortrait) guard let path = imgPath else { - log.error("No image specified for the image banner!") + print("No image specified for the image banner!") return } From 323f60b5f27962a7967e7f2d4e40a9d9f4a17c36 Mon Sep 17 00:00:00 2001 From: sgschantz Date: Tue, 23 Jan 2024 14:26:24 +0700 Subject: [PATCH 11/11] respond to review comments, change subsystem names --- .../Classes/Errors/SentryManager.swift | 32 ++++--------------- .../Extension/UserDefaults+Types.swift | 2 +- .../Classes/Keyboard/KeyboardScaleMap.swift | 3 +- .../Classes/KeymanEngineLogger.swift | 4 ++- .../KeymanEngine/Classes/KeymanPackage.swift | 2 +- .../LexicalModelPickerViewController.swift | 2 +- .../KMEI/KeymanEngine/Classes/Manager.swift | 2 +- .../Resource Data/InstallableKeyboard.swift | 4 +-- .../Resource Management/Migrations.swift | 5 ++- .../LanguageSettingsViewController.swift | 2 +- ios/keyman/Keyman/Keyman/KeymanLogger.swift | 4 ++- .../ImageBannerViewController.swift | 2 +- .../SWKeyboard/KeyboardViewController.swift | 2 +- 13 files changed, 25 insertions(+), 41 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift index bb825f4d90..441997ce23 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Errors/SentryManager.swift @@ -91,11 +91,7 @@ public class SentryManager { } /** - * Captures a Sentry event and copies its message to the engine's logging mechanism. - * If the logging level is not specified, the Sentry event's log-level will be used as a default. - * - * Will safely bypass the Sentry component if not activated by the app, only logging the - * message in such scenarios. + * Captures a Sentry event and safely bypass the Sentry component if not activated by the app. */ public static func capture(_ event: Sentry.Event) { // Guarded in case a library consumer decides against initializing Sentry. @@ -105,11 +101,8 @@ public class SentryManager { } /** - * Captures a Sentry event and copies its message to the engine's logging mechanism. - * If the logging level is not specified, the Sentry event's log-level will be used as a default. - * - * Will safely bypass the Sentry component if not activated by the app, only logging the - * message in such scenarios. + * Captures a Sentry event for the specified error. If the logging level is not specified, it will default to .error. + * Will safely bypass the Sentry component if not activated by the app. */ public static func capture(_ error: Error, message: String? = nil, sentryLevel: Sentry.SentryLevel = .error) { let event = Sentry.Event(error: error) @@ -122,11 +115,8 @@ public class SentryManager { } /** - * Constructs a SentryEvent around a message and also passes it to the engine's logging mechanism. - * If the logging level is not specified, it will default to .error. - * - * Will safely bypass the Sentry component if not activated by the app, only logging the - * message in such scenarios. + * Constructs a SentryEvent around a message. If the logging level is not specified, it will default to .error. + * Will safely bypass the Sentry component if not activated by the app. */ public static func capture(_ message: String, sentryLevel: Sentry.SentryLevel = .error) { let event = Sentry.Event(level: sentryLevel) @@ -136,11 +126,7 @@ public class SentryManager { } /** - * Adds a Sentry breadcrumb and copies its message to the engine's logging mechanism. - * If the logging level is not specified, the Sentry event's log-level will be used as a default. - * - * Will safely bypass the Sentry component if not activated by the app, only logging the - * message in such scenarios. + * Adds a Sentry breadcrumb. Will safely bypass the Sentry component if not activated by the app. */ public static func breadcrumb(crumb: Sentry.Breadcrumb) { // Guarded in case a library consumer decides against initializing Sentry. @@ -150,11 +136,7 @@ public class SentryManager { } /** - * Adds a Sentry breadcrumb and copies its message to the engine's logging mechanism. - * If the logging level is not specified, the Sentry event's log-level will be used as a default. - * - * Will safely bypass the Sentry component if not activated by the app, only logging the - * message in such scenarios. + * Adds a Sentry breadcrumb. Will safely bypass the Sentry component if not activated by the app. */ public static func breadcrumb(_ message: String, category: String? = nil, sentryLevel: Sentry.SentryLevel = .info) { let crumb = Sentry.Breadcrumb() diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift index d4912069b3..5cfed55ea2 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Extension/UserDefaults+Types.swift @@ -19,7 +19,7 @@ public extension UserDefaults { do { return try array.map { try decoder.decode(InstallableKeyboard.self, from: $0) } } catch { - let message = "\(String(describing: error))" + let message = "Error decoding keyboards: \(error))" os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) SentryManager.capture(error, message: message) return nil diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift index 520af29cc5..c18fb8c7ad 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/KeyboardScaleMap.swift @@ -172,10 +172,9 @@ class KeyboardScaleMap { // Shouldn't happen, but just in case. if _screenSize == CGSize.zero { // This would notify us whenever new devices are out that we haven't build a mapping for. - // We haven't actually updated things here in a while, so we'll just breadcrumb for now. let message = "Cannot detect device dimensions; defaulting to smallest device for form factor." os_log("%{public}s", log: KeymanEngineLogger.ui, type: .error, message) - SentryManager.breadcrumb(message, sentryLevel: .error) + SentryManager.capture(message, sentryLevel: .error) } // Convert to CGSize in portrait orientation. diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift index f4ad65cbc1..6d34adae7c 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanEngineLogger.swift @@ -22,7 +22,9 @@ import Foundation import os.log -private let subsystem = "org.sil.Keyman.ios.Engine" +// bundle for engine is "org.sil.Keyman.ios.Engine" +// but selecting this subsystem name for consistency across apps +private let subsystem = "com.keyman.ios.engine" public struct KeymanEngineLogger { static let engine = OSLog(subsystem: subsystem, category: "Engine") diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift index 2b45462cdc..29fb984e59 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift @@ -453,7 +453,7 @@ public class KeymanPackage { } catch { let message = "Keyboard installation error: \(String(describing: error))" os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) - SentryManager.capture(error, message: message) + SentryManager.capture(error, message: message, sentryLevel: .info) complete(nil) } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelPickerViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelPickerViewController.swift index b82f682044..2febc8bac0 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelPickerViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelPickerViewController.swift @@ -288,7 +288,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega Queries.LexicalModel.fetch(forLanguageCode: language.id) { result, error in if let error = error { let errorMessage = "Failed to fetch lexical model list for \(self.language.id). Error: \(error.localizedDescription)" - os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, errorMessage) + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, errorMessage) DispatchQueue.main.async { self.lexicalModelDownloadFailed(PackageDownloadFailedNotification(packageKey: nil, error: error)) } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index f77e6407e1..6419dcd669 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -372,7 +372,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { let wrappedKbds = Migrations.migrateToKMPFormat([keyboard]) guard wrappedKbds.count == 1 else { let message = "Could not properly import keyboard" - os_log("%{public}s", log:KeymanEngineLogger.settings, type: .info, message) + os_log("%{public}s", log:KeymanEngineLogger.settings, type: .error, message) SentryManager.capture(message) return } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift index b28b81abd3..452f5dc729 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/InstallableKeyboard.swift @@ -109,8 +109,8 @@ public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource { let languageMatches = metadata.languages.compactMap { return $0.languageId == lgCode ? $0.name : nil } if (languageMatches.isEmpty) { let message = "Could not find languageId '\(lgCode)' for package '\(packageID)'" - os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) - SentryManager.capture(message) + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .default, message) + SentryManager.capture(message, sentryLevel: .warning) } guard languageMatches.count >= 1 else { return nil diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift index 30220255f1..e8715c321d 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/Migrations.swift @@ -101,8 +101,7 @@ public enum Migrations { let message = "Could not migrate pre-existing resources to KMP-style file organization" event.message = SentryMessage(formatted: message) event.extra = [ "priorVersion": version ] - os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) - SentryManager.breadcrumb(message) + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) SentryManager.capture(event) } } else { @@ -373,7 +372,7 @@ public enum Migrations { let message = "Missing required fields in keyboard dictionary for FullKeyboardID" event.message = SentryMessage(formatted: message) event.extra = ["kbId": kbDict["kbId"] ?? "nil", "langId": kbDict["langId"] ?? "nil"] - os_log("%{public}s", log:KeymanEngineLogger.migration, type: .info, message) + os_log("%{public}s", log:KeymanEngineLogger.migration, type: .error, message) SentryManager.capture(event) return nil } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift index 3f0b24d704..0c98a1cacc 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Settings/LanguageSettingsViewController.swift @@ -350,7 +350,7 @@ class LanguageSettingsViewController: UITableViewController { // If user defaults for keyboards list does not exist, do nothing. guard let globalUserKeyboards = userData.userKeyboards else { let message = "no keyboards in the global keyboards list!" - os_log("%{public}s", log:KeymanEngineLogger.resources, type: .info, message) + os_log("%{public}s", log:KeymanEngineLogger.resources, type: .error, message) SentryManager.capture(message) return nil } diff --git a/ios/keyman/Keyman/Keyman/KeymanLogger.swift b/ios/keyman/Keyman/Keyman/KeymanLogger.swift index 749a2054de..cbf18826f5 100644 --- a/ios/keyman/Keyman/Keyman/KeymanLogger.swift +++ b/ios/keyman/Keyman/Keyman/KeymanLogger.swift @@ -22,7 +22,9 @@ import Foundation import os.log -private let subsystem = "Tavultesoft.Keyman" +// bundle for engine is "Tavultesoft.Keyman" +// but selecting this subsystem name for consistency across apps +private let subsystem = "com.keyman.ios.app" public struct KeymanLogger { static let ui = OSLog(subsystem: subsystem, category: "UI") diff --git a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift index 9f4aeb6344..ca24083ca0 100644 --- a/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/ImageBannerViewController.swift @@ -50,7 +50,7 @@ class ImageBannerViewController: UIViewController { let frame = CGRect(origin: self.view.frame.origin, size: size) - os_log("Rendering banner image of size %{public}s", log: KeymanLogger.ui, type: .info, NSCoder.string(for: size)) + os_log("Rendering banner image of size %{public}s", log: KeymanLogger.ui, type: .debug, NSCoder.string(for: size)) self.view.frame = frame widthConstraint?.constant = size.width diff --git a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift index 9502919749..be5ffb4e39 100644 --- a/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift +++ b/ios/keyman/Keyman/SWKeyboard/KeyboardViewController.swift @@ -70,7 +70,7 @@ class KeyboardViewController: InputViewController { let imgPath = getTopBarImage(size: size) guard let path = imgPath else { let message = "No image specified for the image banner!" - os_log("%{public}s", log: KeymanLogger.ui, type: .info, message) + os_log("%{public}s", log: KeymanLogger.ui, type: .error, message) SentryManager.capture(message) return }