Skip to content

Commit

Permalink
VPN-6041: Add Swift logs from app (#9192)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mcleinman authored Mar 19, 2024
1 parent 7e94b06 commit 162e1b9
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 52 deletions.
1 change: 0 additions & 1 deletion src/platforms/ios/iosconstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions src/platforms/ios/ioscontroller.mm
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ emit statusUpdated(QString::fromNSString(serverIpv4Gateway),
void IOSController::getBackendLogs(std::function<void(const QString&)>&& a_callback) {
std::function<void(const QString&)> 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]; }
12 changes: 9 additions & 3 deletions src/platforms/ios/ioslogger.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
130 changes: 84 additions & 46 deletions src/platforms/ios/ioslogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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(
Expand All @@ -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)")
}
}
}
Expand Down

0 comments on commit 162e1b9

Please sign in to comment.