Add local logging to file.
PiperOrigin-RevId: 184161119
diff --git a/src/Tulsi/EventLogger.swift b/src/Tulsi/EventLogger.swift
index ebd53d5..266f39f 100644
--- a/src/Tulsi/EventLogger.swift
+++ b/src/Tulsi/EventLogger.swift
@@ -21,8 +21,16 @@
private let verboseLevel: TulsiMessageLevel
private var observer: NSObjectProtocol? = nil
- init(verboseLevel: TulsiMessageLevel) {
+ private let logFile: FileHandle? = nil
+
+ init(verboseLevel: TulsiMessageLevel, logToFile: Bool=false) {
self.verboseLevel = verboseLevel
+
+ if logToFile, let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
+ self.logFile = EventLogger.createLogFile(appName)
+ } else {
+ self.logFile = nil
+ }
}
deinit {
@@ -36,9 +44,7 @@
object: nil,
queue: nil) {
[weak self] (notification: Notification) in
- guard let item = LogMessage(notification: notification),
- let verboseLevel = self?.verboseLevel,
- verboseLevel.logRank.rawValue >= item.level.logRank.rawValue else {
+ guard let item = LogMessage(notification: notification) else {
return
}
self?.logItem(item)
@@ -47,8 +53,56 @@
// MARK: - Private methods
+ private static func createLogFile(_ appName: String) -> FileHandle? {
+ let fileManager = FileManager.default
+ guard let folder = fileManager.urls(for: .applicationSupportDirectory,
+ in: .userDomainMask).first else {
+ return
+ }
+
+ let tulsiFolder = folder.appendingPathComponent(appName)
+ var isDirectory: ObjCBool = false
+ let fileExists = fileManager.fileExists(atPath: tulsiFolder.path, isDirectory: &isDirectory)
+
+ if !fileExists || !isDirectory.boolValue {
+ do {
+ try fileManager.createDirectory(at: tulsiFolder,
+ withIntermediateDirectories: true,
+ attributes: nil)
+ } catch {
+ print("failed to create logging folder at \"\(tulsiFolder)\".")
+ return
+ }
+ }
+
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "yyyyMMdd_HHmmss"
+ let date = Date()
+ let dateString = dateFormatter.string(from: date)
+
+ let logFileUrl = tulsiFolder.appendingPathComponent("log_\(dateString).txt")
+ do {
+ fileManager.createFile(atPath: logFileUrl.path, contents: nil, attributes: nil)
+ try self.logFile = FileHandle(forWritingTo: logFileUrl)
+ } catch {
+ print("error creating log file at \"\(logFileUrl.path)\".")
+ return
+ }
+ }
+
private func logItem(_ item: LogMessage) {
let level: String = item.level.rawValue
+
+ let logString = "[\(level)] \(item.message)\n"
+ if let logFile = self.logFile, let logData = logString.data(using: .utf8) {
+ logFile.seekToEndOfFile()
+ logFile.write(logData)
+ }
+
+ guard self.verboseLevel.logRank.rawValue >= item.level.logRank.rawValue else {
+ return
+ }
+
let details: String
if let itemDetails = item.details {
details = " \(itemDetails)"
diff --git a/src/Tulsi/TulsiCommandlineParser.swift b/src/Tulsi/TulsiCommandlineParser.swift
index 80b7710..f654ea3 100644
--- a/src/Tulsi/TulsiCommandlineParser.swift
+++ b/src/Tulsi/TulsiCommandlineParser.swift
@@ -42,6 +42,7 @@
let buildStartupOptions: String?
let buildOptions: String?
let buildTargets: [String]?
+ let logToFile: Bool
init() {
bazel = nil
@@ -56,6 +57,7 @@
buildStartupOptions = nil
buildOptions = nil
buildTargets = nil
+ logToFile = false
}
init(dict: [String: Any]) {
@@ -87,6 +89,7 @@
buildStartupOptions = dict[TulsiCommandlineParser.ParamBuildStartupOptions] as? String
buildOptions = dict[TulsiCommandlineParser.ParamBuildOptions] as? String
buildTargets = dict[TulsiCommandlineParser.ParamBuildTargetLong] as? [String]
+ logToFile = dict[TulsiCommandlineParser.ParamLogToFile] as? Bool == true
}
}
@@ -122,6 +125,8 @@
static let ParamBuildTargetShort = "-t"
static let ParamBuildTargetLong = "--target"
+ static let ParamLogToFile = "--log-to-file"
+
let arguments: Arguments
let commandlineSentinalFound: Bool
@@ -254,6 +259,9 @@
storeValueAt(i, forArgument: TulsiCommandlineParser.ParamBuildTargetLong, append: true)
i += 1
+ case TulsiCommandlineParser.ParamLogToFile:
+ parsedArguments[TulsiCommandlineParser.ParamLogToFile] = true as AnyObject?
+
default:
print("Ignoring unknown option \"\(arg)\"")
}
@@ -302,6 +310,8 @@
" Overrides \(ParamQuietLong) if both are present. ",
" \(ParamQuietLong): Hide all debug info messages (warning: may also hide some error details).",
" \(ParamAdditionalPathFilters) \"<paths>\": Space-delimited source filters to be included in the generated project.",
+ " \(ParamLogToFile): Enable logging to ~/Library/Application Support/Tulsi/*.txt.",
+ " Ignores specified verbosity; everything will be logged to file.",
""
]
print(usage.joined(separator: "\n") + "\n")
diff --git a/src/Tulsi/main.swift b/src/Tulsi/main.swift
index 8958310..557c66a 100644
--- a/src/Tulsi/main.swift
+++ b/src/Tulsi/main.swift
@@ -19,7 +19,8 @@
// Parse the commandline parameters to see if the app should operate in headless mode or not.
let commandlineParser = TulsiCommandlineParser()
- let consoleLogger = EventLogger(verboseLevel: commandlineParser.arguments.verboseLevel)
+ let consoleLogger = EventLogger(verboseLevel: commandlineParser.arguments.verboseLevel,
+ logToFile: commandlineParser.arguments.logToFile)
consoleLogger.startLogging()
if !commandlineParser.commandlineSentinalFound {