diff --git a/mac/Config/Config/DownloadCoordinator.swift b/mac/Config/Config/DownloadCoordinator.swift index 63d7a7e50f..735887d737 100644 --- a/mac/Config/Config/DownloadCoordinator.swift +++ b/mac/Config/Config/DownloadCoordinator.swift @@ -58,15 +58,15 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega // get the package id (though it appears to be identifying a keyboard in the URL) let matchPackageId = String(match.4) if let downloadUrl = self.settings?.buildDownloadPackageUrl(for: matchPackageId) { - Logger.download.info("package install, download url = \(downloadUrl.absoluteString, privacy: .public)") - LogUtil.infoBreadcrumb("package install, download url = \(downloadUrl.absoluteString)", category: .download) + Logger.download.info("package install, download url = \(downloadUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("package install, download url = \(downloadUrl.cleanUrlPath())", category: .download) let newRequest = URLRequest(url: downloadUrl) DispatchQueue.main.async { webView.startDownload(using: newRequest) { download in - Logger.download.info("download initiated to \(newRequest.url?.absoluteString ?? "nil", privacy: .public)") - LogUtil.infoBreadcrumb("download initiated to \(newRequest.url?.absoluteString ?? "nil")", category: .download) + Logger.download.info("download initiated to \(newRequest.url?.cleanUrlPath() ?? "nil", privacy: .public)") + LogUtil.infoBreadcrumb("download initiated to \(newRequest.url?.cleanUrlPath() ?? "nil")", category: .download) download.delegate = self self.setupDownloadTracking(download) } @@ -165,8 +165,8 @@ public class DownloadCoordinator: NSObject, ObservableObject, WKNavigationDelega self.progressObserver = nil if let downloadDestination = installHelper?.temporaryKmpFileLocation { - Logger.download.info("download of \(downloadDestination.path, privacy: .public) was successful.") - LogUtil.infoBreadcrumb("download of \(downloadDestination.path) was successful.", category: .download) + Logger.download.info("download of \(downloadDestination.cleanUrlPath(), privacy: .public) was successful.") + LogUtil.infoBreadcrumb("download of \(downloadDestination.cleanUrlPath()) was successful.", category: .download) if let settings { do { diff --git a/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift b/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift index 521b7bd9bd..0f5f51cd55 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/InputMethodUtil.swift @@ -56,7 +56,7 @@ public class InputMethodUtil { return false } - return FileManager.default.fileExists(atPath: inputMethodUrl.path) + return FileManager.default.fileExists(atPath: inputMethodUrl.path(percentEncoded: false)) } /** @@ -214,8 +214,8 @@ public class InputMethodUtil { let process = Process() if let executableUrl = self.pathUtil.buildInputMethodExecutableUrl(fileName: self.keymanInputMethodApplicationName) { process.executableURL = executableUrl - Logger.setup.info("invoking Keyman at: \(String(describing: process.executableURL), privacy: .public)") - LogUtil.infoBreadcrumb("invoking Keyman at: \(String(describing: process.executableURL))", category: .setup) + Logger.setup.info("invoking Keyman at: \(String(describing: process.executableURL?.cleanUrlPath()), privacy: .public)") + LogUtil.infoBreadcrumb("invoking Keyman at: \(String(describing: process.executableURL?.cleanUrlPath()))", category: .setup) process.arguments = [argument] } @@ -253,8 +253,8 @@ public class InputMethodUtil { NSWorkspace.shared.openApplication(at: inputMethodUrl, configuration: openConfig) { (app, error) in if let error = error { - Logger.setup.error("Could not launch Keyman input method at \(inputMethodUrl), due to error: \(error as NSError, privacy: .public)") - LogUtil.errorBreadcrumb("Could not launch Keyman input method at \(inputMethodUrl), due to error: \(error as NSError)", category: .setup) + Logger.setup.error("Could not launch Keyman input method at \(inputMethodUrl.cleanUrlPath()), due to error: \(error as NSError, privacy: .public)") + LogUtil.errorBreadcrumb("Could not launch Keyman input method at \(inputMethodUrl.cleanUrlPath()), due to error: \(error as NSError)", category: .setup) } } } diff --git a/mac/KeymanSettings/Sources/KeymanSettings/LogUtil.swift b/mac/KeymanSettings/Sources/KeymanSettings/LogUtil.swift index 8b8fb4d9fb..027c9fb190 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/LogUtil.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/LogUtil.swift @@ -8,6 +8,31 @@ import Sentry +import Foundation + +/** + * Extend URL to add function that cleans path strings, removing the home directory from the path. + */ +extension URL { + /** + * If the path contains the user's home, replace it with ~ as the home directory name + * may contain the user's name and should not be written to the logs. + */ + public func cleanUrlPath() -> String { + guard self.isFileURL else { return self.absoluteString } + + let unescapedPath = self.path(percentEncoded: false) + let homeDirectory = NSHomeDirectory() + + if unescapedPath.hasPrefix(homeDirectory) { + let relativeComponent = unescapedPath.dropFirst(homeDirectory.count) + return "~\(relativeComponent)" + } + + return unescapedPath + } +} + public struct LogUtil { public enum LogCategory: String { case setup // related to start of app and installation diff --git a/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift b/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift index bc57d4fac9..c8b53fa050 100644 --- a/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift +++ b/mac/KeymanSettings/Sources/KeymanSettings/SettingsContainer.swift @@ -153,7 +153,6 @@ public class SettingsContainer : ObservableObject { Logger.data.log("Found documents group container") } catch KeymanPathError.groupContainerNotFound { Logger.data.error("Document group container not found") - let message = "Document group container not found." fatalError("Document group container not found.") } catch { Logger.data.error("Unable to access documents in group container, error \(error as NSError, privacy: .public)") @@ -483,8 +482,8 @@ public class SettingsContainer : ObservableObject { * Delegates to the PackageInstallHelper instance to decide whether the package should be installed. */ public func packageDownloadComplete(kmpFileUrl: URL) throws { - Logger.setup.info("packageDownloadComplete \(kmpFileUrl, privacy: .public)") - LogUtil.infoBreadcrumb("packageDownloadComplete \(kmpFileUrl)", category: .setup) + Logger.setup.info("packageDownloadComplete \(kmpFileUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("packageDownloadComplete \(kmpFileUrl.cleanUrlPath())", category: .setup) do { try self.packageInstall?.prepareToInstall(for: kmpFileUrl) diff --git a/mac/KeymanSettings/Sources/Model/Keyboard.swift b/mac/KeymanSettings/Sources/Model/Keyboard.swift index 2fe500f94e..12a83023ce 100644 --- a/mac/KeymanSettings/Sources/Model/Keyboard.swift +++ b/mac/KeymanSettings/Sources/Model/Keyboard.swift @@ -85,9 +85,9 @@ public class Keyboard: Identifiable, Hashable, Equatable { * validate whether a corresponding kmx file exists for this keyboard */ public func validateKmxFile(in packageDirectory: URL) throws { - let kmxFilePath = self.deriveKmxFileUrl(from: packageDirectory).path - if !FileManager.default.fileExists(atPath: kmxFilePath) { - Logger.data.error("error: could not find kmx file \(kmxFilePath, privacy: .public)") + let kmxFilePath = self.deriveKmxFileUrl(from: packageDirectory) + if !FileManager.default.fileExists(atPath: kmxFilePath.path(percentEncoded: false)) { + Logger.data.error("error: could not find kmx file \(kmxFilePath.cleanUrlPath(), privacy: .public)") throw LoadPackageError.missingKmxFile } } diff --git a/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift b/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift index 15b8b968e0..96a9cc8780 100644 --- a/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift +++ b/mac/KeymanSettings/Sources/Persistence/KeymanPaths.swift @@ -49,7 +49,7 @@ public struct KeymanPaths { // if for some reason it doesn't exist, create it let fileManager = FileManager.default - if !fileManager.fileExists(atPath: fontsDirectory.path) { + if !fileManager.fileExists(atPath: fontsDirectory.path(percentEncoded: false)) { do { try fileManager.createDirectory(at: fontsDirectory, withIntermediateDirectories: true, attributes: nil) } catch { @@ -113,16 +113,16 @@ public struct KeymanPaths { } fileprivate func logPaths() { - Logger.setup.debug("documents: \(self.keyman17DocumentsDirectory!.absoluteString)") - Logger.setup.debug("keyman 17 packages: \(self.keyman17PackagesDirectory!.absoluteString)") + Logger.setup.debug("documents: \(self.keyman17DocumentsDirectory!.cleanUrlPath())") + Logger.setup.debug("keyman 17 packages: \(self.keyman17PackagesDirectory!.cleanUrlPath())") - Logger.setup.debug("support directory: \(self.keyman18SupportDirectory!.absoluteString)") - Logger.setup.debug("support keyman directory: \(self.keyman18DataDirectory!.absoluteString)") - Logger.setup.debug("keyman 18 packages: \(self.keyman18PackagesDirectory!.absoluteString)") + Logger.setup.debug("support directory: \(self.keyman18SupportDirectory!.cleanUrlPath())") + Logger.setup.debug("support keyman directory: \(self.keyman18DataDirectory!.cleanUrlPath())") + Logger.setup.debug("keyman 18 packages: \(self.keyman18PackagesDirectory!.cleanUrlPath())") - Logger.setup.debug("container: \(self.keyman19ContainerDirectory.absoluteString)") - Logger.setup.debug("preferences: \(self.keyman19PreferencesDirectory.absoluteString)") - Logger.setup.debug("keyman 19 packages: \(self.keyman19PackagesDirectory.absoluteString)") + Logger.setup.debug("container: \(self.keyman19ContainerDirectory.cleanUrlPath())") + Logger.setup.debug("preferences: \(self.keyman19PreferencesDirectory.cleanUrlPath())") + Logger.setup.debug("keyman 19 packages: \(self.keyman19PackagesDirectory.cleanUrlPath())") } /** diff --git a/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift b/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift index 744a43e914..b5d7db38ca 100644 --- a/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift +++ b/mac/KeymanSettings/Sources/Persistence/PackageInstallHelper.swift @@ -70,7 +70,7 @@ public class PackageInstallHelper: Identifiable { * Indicates that a package has been downloaded and can be prepared for installation */ public func packageDownloadComplete(for kmpFileUrl: URL) throws { - Logger.data.log("packageDownloadComplete \(kmpFileUrl.path, privacy: .public)") + Logger.data.log("packageDownloadComplete \(kmpFileUrl.cleanUrlPath(), privacy: .public)") try self.prepareToInstall(for: kmpFileUrl) } @@ -80,7 +80,7 @@ public class PackageInstallHelper: Identifiable { * */ public func prepareToInstall(for kmpFileUrl: URL) throws { - Logger.data.log("prepareToInstall \(kmpFileUrl.path, privacy: .public)") + Logger.data.log("prepareToInstall \(kmpFileUrl.cleanUrlPath(), privacy: .public)") do { // unzip to the temp directory @@ -104,8 +104,8 @@ public class PackageInstallHelper: Identifiable { self.packageInstallationType = self.determinePackageInstallationType(newPackage: package) } catch { self.cleanupFailedInstallation() - Logger.data.error("package installation failed for \(kmpFileUrl) with error: \(error as NSError, privacy: .public)") - LogUtil.errorBreadcrumb("package installation failed for \(kmpFileUrl) with error: \(error as NSError)", category: .data) + Logger.data.error("package installation failed for \(kmpFileUrl.cleanUrlPath(), privacy: .public) with error: \(error as NSError, privacy: .public)") + LogUtil.errorBreadcrumb("package installation failed for \(kmpFileUrl.cleanUrlPath()) with error: \(error as NSError)", category: .data) throw error } } @@ -185,7 +185,7 @@ public class PackageInstallHelper: Identifiable { includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles]) } catch { - Logger.data.error("error: unable to get contents of package fonts directory at \(installLocation.path, privacy: .public) with error: \(error as NSError, privacy: .public)") + Logger.data.error("error: unable to get contents of package fonts directory at \(installLocation.cleanUrlPath(), privacy: .public) with error: \(error as NSError, privacy: .public)") } for fontUrl in fileUrls { @@ -228,7 +228,7 @@ public class PackageInstallHelper: Identifiable { let fileManager = FileManager.default // remove the font from the fonts directory just in case it is an old one - if fileManager.fileExists(atPath: fontDestinationUrl.path) { + if fileManager.fileExists(atPath: fontDestinationUrl.path(percentEncoded: false)) { Logger.data.info("removed existing font: \(fontDestinationUrl.lastPathComponent, privacy: .public)") LogUtil.infoBreadcrumb("removed existing font: \(fontDestinationUrl.lastPathComponent)", category: .data) try? fileManager.removeItem(at: fontDestinationUrl) diff --git a/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift b/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift index c2cd335456..7d73104c10 100644 --- a/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift +++ b/mac/KeymanSettings/Sources/Persistence/PackageRepository.swift @@ -80,8 +80,8 @@ public class PackageRepository: PackageRepo { * */ public func loadSinglePackage(packageUrl: URL) throws -> KeymanPackage { - Logger.data.info("loadSinglePackage from url: \(packageUrl, privacy: .public)") - LogUtil.infoBreadcrumb("loadSinglePackage from url: \(packageUrl)", category: .data) + Logger.data.info("loadSinglePackage from url: \(packageUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("loadSinglePackage from url: \(packageUrl.cleanUrlPath())", category: .data) guard let source = try readPackageFromDirectory(packageDirectoryUrl: packageUrl) else { throw LoadPackageError.invalidUrl } @@ -94,12 +94,12 @@ public class PackageRepository: PackageRepo { * delete the package from disk */ public func deletePackage(package: KeymanPackage) { - Logger.data.info("deleting package: \(package.sourceDirectoryUrl, privacy: .public)") - LogUtil.infoBreadcrumb("deleting package: \(package.sourceDirectoryUrl)", category: .data) + Logger.data.info("deleting package: \(package.sourceDirectoryUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("deleting package: \(package.sourceDirectoryUrl.cleanUrlPath())", category: .data) do { try FileManager.default.removeItem(at: package.sourceDirectoryUrl) - Logger.data.info("deleted package: \(package.sourceDirectoryUrl, privacy: .public)") - LogUtil.infoBreadcrumb("deleted package: \(package.sourceDirectoryUrl)", category: .data) + Logger.data.info("deleted package: \(package.sourceDirectoryUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("deleted package: \(package.sourceDirectoryUrl.cleanUrlPath())", category: .data) } catch { Logger.data.error("could not delete directory: \(error as NSError, privacy: .public)") LogUtil.errorBreadcrumb("could not delete directory: \(error as NSError)", category: .data) @@ -115,23 +115,23 @@ public class PackageRepository: PackageRepo { let packageTempDirectory = pathUtil.keyman19TempDirectory // create the keyman-packages directory if it doesn't already exist - if !FileManager.default.fileExists(atPath: packageDirectory.path) { + if !FileManager.default.fileExists(atPath: packageDirectory.path(percentEncoded: false)) { try FileManager.default.createDirectory(at: packageDirectory, withIntermediateDirectories: true, attributes: nil) - Logger.data.info("Created directory: \(packageDirectory.path, privacy: .public)") - LogUtil.infoBreadcrumb("Created directory: \(packageDirectory.path)", category: .data) + Logger.data.info("Created directory: \(packageDirectory.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("Created directory: \(packageDirectory.cleanUrlPath())", category: .data) } else { - Logger.data.info("Directory already exists: \(packageDirectory.path, privacy: .public)") - LogUtil.infoBreadcrumb("Directory already exists: \(packageDirectory.path)", category: .data) + Logger.data.info("Directory already exists: \(packageDirectory.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("Directory already exists: \(packageDirectory.cleanUrlPath())", category: .data) } // create the temp directory if it doesn't already exist - if !FileManager.default.fileExists(atPath: packageTempDirectory.path) { + if !FileManager.default.fileExists(atPath: packageTempDirectory.path(percentEncoded: false)) { try FileManager.default.createDirectory(at: packageTempDirectory, withIntermediateDirectories: true, attributes: nil) - Logger.data.info("Created directory: \(packageTempDirectory.path, privacy: .public)") - LogUtil.infoBreadcrumb("Created directory: \(packageTempDirectory.path)", category: .data) + Logger.data.info("Created directory: \(packageTempDirectory.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("Created directory: \(packageTempDirectory.cleanUrlPath())", category: .data) } else { - Logger.data.info("Directory already exists: \(packageTempDirectory.path, privacy: .public)") - LogUtil.infoBreadcrumb("Directory already exists: \(packageTempDirectory.path)", category: .data) + Logger.data.info("Directory already exists: \(packageTempDirectory.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("Directory already exists: \(packageTempDirectory.cleanUrlPath())", category: .data) } } @@ -208,7 +208,7 @@ public class PackageRepository: PackageRepo { */ func directoryExistsAtPath(directoryUrl: URL) -> Bool { var isDirectory: ObjCBool = false - let exists = FileManager.default.fileExists(atPath: directoryUrl.path, isDirectory: &isDirectory) + let exists = FileManager.default.fileExists(atPath: directoryUrl.path(percentEncoded: false), isDirectory: &isDirectory) return exists && isDirectory.boolValue } @@ -241,8 +241,8 @@ public class PackageRepository: PackageRepo { packageMap[itemUrl] = packageSource } } catch let error as LoadPackageError { - Logger.data.error("package at \(itemUrl) could not be loaded: \(error as NSError, privacy: .public)") - LogUtil.errorBreadcrumb("package at \(itemUrl) could not be loaded: \(error as NSError)", category: .data) + Logger.data.error("package at \(itemUrl.cleanUrlPath(), privacy: .public) could not be loaded: \(error as NSError, privacy: .public)") + LogUtil.errorBreadcrumb("package at \(itemUrl.cleanUrlPath()) could not be loaded: \(error as NSError)", category: .data) } } } @@ -260,12 +260,12 @@ public class PackageRepository: PackageRepo { * check the specified directory for the kmp.json file and read it if it exists */ func readPackageFromDirectory(packageDirectoryUrl: URL) throws -> PackageSource? { - Logger.data.info("readPackageFromDirectory from url: \(packageDirectoryUrl, privacy: .public)") - LogUtil.infoBreadcrumb("readPackageFromDirectory from url: \(packageDirectoryUrl)", category: .data) + Logger.data.info("readPackageFromDirectory from url: \(packageDirectoryUrl.cleanUrlPath(), privacy: .public)") + LogUtil.infoBreadcrumb("readPackageFromDirectory from url: \(packageDirectoryUrl.cleanUrlPath())", category: .data) var packageSource: PackageSource? = nil let kmpJsonFileUrl = packageDirectoryUrl.appendingPathComponent(packageFileName) - if !FileManager.default.fileExists(atPath: kmpJsonFileUrl.path) { + if !FileManager.default.fileExists(atPath: kmpJsonFileUrl.path(percentEncoded: false)) { throw LoadPackageError.kmpJsonFileNotFound }