From 162e1b947fcd83252d73dbbe4340f72010d160ca Mon Sep 17 00:00:00 2001 From: MCleinman <9295855+mcleinman@users.noreply.github.com> Date: Tue, 19 Mar 2024 08:30:44 -0700 Subject: [PATCH] VPN-6041: Add Swift logs from app (#9192) * VPN-6041 add swift logs * testing xcode taskcluster upgrade * VPN-5101 clean up iOS logs * flush on other platforms * pr feedback * remove test code - ok doesn't confirm Android share sheet was used, just presented * PR feedback to rename, remove some duplicated code * improve code --- src/platforms/ios/iosconstants.swift | 1 - src/platforms/ios/ioscontroller.mm | 4 +- src/platforms/ios/ioslogger.mm | 12 ++- src/platforms/ios/ioslogger.swift | 130 +++++++++++++++++---------- 4 files changed, 95 insertions(+), 52 deletions(-) diff --git a/src/platforms/ios/iosconstants.swift b/src/platforms/ios/iosconstants.swift index 7ac250a66a..676ed903a6 100644 --- a/src/platforms/ios/iosconstants.swift +++ b/src/platforms/ios/iosconstants.swift @@ -4,7 +4,6 @@ struct Constants { static let appGroupIdentifier = "group.org.mozilla.ios.Guardian" - static let networkExtensionLogFileName = "networkextension.log" struct UserDefaultKeys { static let telemetryEnabled = "TELEMETRY_ENABLED" diff --git a/src/platforms/ios/ioscontroller.mm b/src/platforms/ios/ioscontroller.mm index ebcbb694d4..1cc5986ef8 100644 --- a/src/platforms/ios/ioscontroller.mm +++ b/src/platforms/ios/ioscontroller.mm @@ -256,9 +256,9 @@ emit statusUpdated(QString::fromNSString(serverIpv4Gateway), void IOSController::getBackendLogs(std::function&& a_callback) { std::function callback = std::move(a_callback); - [IOSLoggerImpl getAppexLogsWithCallback:^(NSString* logs) { + [IOSLoggerImpl getLogsWithCallback:^(NSString* logs) { callback(QString::fromNSString(logs)); }]; } -void IOSController::cleanupBackendLogs() { [IOSLoggerImpl clearAppexLogs]; } +void IOSController::cleanupBackendLogs() { [IOSLoggerImpl clearLogs]; } diff --git a/src/platforms/ios/ioslogger.mm b/src/platforms/ios/ioslogger.mm index c43404c6c0..c7743930c3 100644 --- a/src/platforms/ios/ioslogger.mm +++ b/src/platforms/ios/ioslogger.mm @@ -32,10 +32,16 @@ } // static -void IOSLogger::debug(const QString& message) { [impl debugWithMessage:message.toNSString()]; } +void IOSLogger::debug(const QString& message) { + [impl logToConsoleWithMessage:message.toNSString() level:MzLogLevelDebug]; +} // static -void IOSLogger::info(const QString& message) { [impl infoWithMessage:message.toNSString()]; } +void IOSLogger::info(const QString& message) { + [impl logToConsoleWithMessage:message.toNSString() level:MzLogLevelInfo]; +} // static -void IOSLogger::error(const QString& message) { [impl errorWithMessage:message.toNSString()]; } +void IOSLogger::error(const QString& message) { + [impl logToConsoleWithMessage:message.toNSString() level:MzLogLevelError]; +} diff --git a/src/platforms/ios/ioslogger.swift b/src/platforms/ios/ioslogger.swift index f1b4943b47..773d029ebf 100644 --- a/src/platforms/ios/ioslogger.swift +++ b/src/platforms/ios/ioslogger.swift @@ -7,6 +7,42 @@ import os.log import OSLog public class IOSLoggerImpl : NSObject { + enum LogType { + case swift + case networkExtension + + var filename: String { + switch self { + case .swift: return "mozillavpnswift.log" + case .networkExtension: return "networkextension.log" + } + } + + var fileUrl: URL? { + let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier) + return containerURL?.appendingPathComponent(self.filename, isDirectory: false) + } + + var newFileText: String { + switch self { + case .swift: return "\n\nApp Swift Logs\n==============\n" + case .networkExtension: return "" + } + } + } + + @objc enum MzLogLevel: Int { + case debug, info, error + + var swiftVersion: OSLogType { + switch self { + case .debug: return .debug + case .info: return .info + case .error: return .error + } + } + } + private let log: OSLog private lazy var dateFormatter: DateFormatter = { @@ -16,17 +52,6 @@ public class IOSLoggerImpl : NSObject { }() private static let logger = IOSLoggerImpl(tag: "IOSLoggerImpl") - private static var appexLogFileURL: URL? { - get { - guard let containerURL = FileManager.default.containerURL( - forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier - ) else { - return nil - } - - return containerURL.appendingPathComponent(Constants.networkExtensionLogFileName, isDirectory: false) - } - } @objc init(tag: String) { self.log = OSLog( @@ -35,71 +60,84 @@ public class IOSLoggerImpl : NSObject { ) } - @objc func debug(message: String) { + @objc func logToConsole(message: String, level: MzLogLevel) { + os_log("%{public}@", log: self.log, type: level.swiftVersion, message) + } + + func debug(message: String) { log(message, type: .debug) } - @objc func info(message: String) { + func info(message: String) { log(message, type: .info) } - @objc func error(message: String) { + func error(message: String) { log(message, type: .error) } func log(_ message: String, type: OSLogType) { os_log("%{public}@", log: self.log, type: type, message) - - if (Bundle.main.bundlePath.hasSuffix(".appex")) { - let currentDate = Date() - let formattedDateString = dateFormatter.string(from: currentDate) - - if let data = "[\(formattedDateString)] \(message)\n".data(using: .utf8) { - let _ = IOSLoggerImpl.withAppexLogFile { logFileHandle in - logFileHandle.seekToEndOfFile() - logFileHandle.write(data) - } + + let currentDate = Date() + let formattedDateString = dateFormatter.string(from: currentDate) + + if let data = "[\(formattedDateString)] \(message)\n".data(using: .utf8) { + let fileType: LogType = Bundle.main.bundlePath.hasSuffix(".appex") ? .networkExtension : .swift + let _ = IOSLoggerImpl.withLogFile(for: fileType) { logFileHandle in + logFileHandle.seekToEndOfFile() + logFileHandle.write(data) } } } - - @objc static func getAppexLogs(callback: @escaping (String) -> Void) { - withAppexLogFile { logFileHandle in - if let contents = String(data: logFileHandle.readDataToEndOfFile(), encoding: .utf8) { - callback(contents); + + @objc static func getLogs(callback: @escaping (String) -> Void) { + var returnLogs: String = "" + [LogType.networkExtension, .swift].forEach { + IOSLoggerImpl.withLogFile(for: $0) { logFileHandle in + if let contents = String(data: logFileHandle.readDataToEndOfFile(), encoding: .utf8) { + returnLogs.append(contents) + } } } + callback(returnLogs) } - - @objc static func clearAppexLogs() { - withAppexLogFile { logFileHandle in - logFileHandle.truncateFile(atOffset: 0) + + @objc static func clearLogs() { + [LogType.networkExtension, .swift].forEach { logType in + IOSLoggerImpl.withLogFile(for: logType) { logFileHandle in + logFileHandle.truncateFile(atOffset: 0) + if let data = logType.newFileText.data(using: .utf8) { + logFileHandle.write(data) + } else { + logger.error(message: "Unable to write new log header") + } + } } } - - private static func withAppexLogFile(_ f: (_ handle: FileHandle) throws -> Void) { - guard let appexLogFileURL = IOSLoggerImpl.appexLogFileURL else { - logger.error(message: "IMPOSSIBLE: No known app extension log file.") + + private static func withLogFile(for type: LogType, _ f: (_ handle: FileHandle) throws -> Void) { + guard let logFileURL = type.fileUrl else { + logger.error(message: "IMPOSSIBLE: No known log file.") return } - do { - if !FileManager.default.fileExists(atPath: appexLogFileURL.path) { - // Create an empty file - if let data = "".data(using: .utf8) { - try data.write(to: appexLogFileURL) + if !FileManager.default.fileExists(atPath: logFileURL.path) { + // Create an empty file with appropriate headers + if let data = type.newFileText.data(using: .utf8) { + try data.write(to: logFileURL) } else { - logger.error(message: "Unable to create log file at \(appexLogFileURL)") + logger.error(message: "Unable to create log file at \(logFileURL)") return } } - - let fileHandle = try FileHandle(forUpdating: appexLogFileURL) + + let fileHandle = try FileHandle(forUpdating: logFileURL) try f(fileHandle) fileHandle.closeFile() } catch { - logger.error(message: "Unable to access log file at \(appexLogFileURL): \(error)") + logger.error(message: "Unable to access log file at \(logFileURL): \(error)") } } }