Skip to content

Commit 7674d8e

Browse files
authored
FEAT: HIPMC-2293, Expand logger ability to record events from App Extensions (#23)
1 parent f9251dc commit 7674d8e

19 files changed

+119
-85
lines changed

QuantiLogger/common/ApiFactory.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ extension ApiFactory {
146146
/// - session: Session in which the request should be sent.
147147
/// - Returns: .completed if request success, ApiError otherwise.
148148
static func noData(`for` request: URLRequest?, `in` session: URLSession) -> Observable<Void> {
149-
return data(for: request, in: session).flatMap { _ in Observable.just(()) }
149+
data(for: request, in: session).flatMap { _ in Observable.just(()) }
150150
}
151151

152152
/// Method to receive a JSON response based on a provided URL request.
@@ -156,7 +156,7 @@ extension ApiFactory {
156156
/// - session: Session in which the request should be sent.
157157
/// - Returns: JSON data if received and parsed successfully, ApiError instance otherwise.
158158
static func json<T: JSONParseable>(`for` request: URLRequest?, `in` session: URLSession) -> Observable<T> {
159-
return data(for: request, in: session)
159+
data(for: request, in: session)
160160
.flatMap { data -> Observable<T> in
161161
Observable.create { observer in
162162
do {

QuantiLogger/common/ApplicationCallbackLogger.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class ApplicationCallbackLogger {
151151
/// - newCallbacks: new array of callbacks
152152
/// - Returns: array of callbacks to be removed
153153
private func getCallbacksToRemove(from oldCallbacks: [ApplicationCallbackType]?, basedOn newCallbacks: [ApplicationCallbackType]?) -> [ApplicationCallbackType] {
154-
return oldCallbacks?.filter { !(newCallbacks?.contains($0) ?? false) } ?? []
154+
oldCallbacks?.filter { !(newCallbacks?.contains($0) ?? false) } ?? []
155155
}
156156

157157
/// Method to get array of callbacks to add (thus those, who are in newCallbacks but not in oldCallbacks).
@@ -162,7 +162,7 @@ class ApplicationCallbackLogger {
162162
/// - oldCallbacks: old array of callbacks
163163
/// - Returns: array of callbacks to be added
164164
private func getCallbacksToAdd(from newCallbacks: [ApplicationCallbackType]?, basedOn oldCallbacks: [ApplicationCallbackType]?) -> [ApplicationCallbackType] {
165-
return newCallbacks?.filter { !(oldCallbacks?.contains($0) ?? false) } ?? []
165+
newCallbacks?.filter { !(oldCallbacks?.contains($0) ?? false) } ?? []
166166
}
167167

168168
/// Method to remove specific Application's notification callbacks

QuantiLogger/common/Date+.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ extension Date {
1414
///
1515
/// - Returns: String
1616
public func toFullDateTimeString() -> String {
17-
return DateHelper.toFullDateTimeString(from: self)
17+
DateHelper.toFullDateTimeString(from: self)
1818
}
1919

2020
/// Method to return String in format: "yyyy-MM-dd" from Date instance.
2121
///
2222
/// - Returns: String
2323
public func toFullDateString() -> String {
24-
return DateHelper.toFullDateString(from: self)
24+
DateHelper.toFullDateString(from: self)
2525
}
2626
}

QuantiLogger/common/DateHelper.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ struct DateHelper {
2727
}()
2828

2929
static func toFullDateTimeString(from date: Date) -> String {
30-
return DateHelper.dateTimeFormatter.string(from: date)
30+
DateHelper.dateTimeFormatter.string(from: date)
3131
}
3232

3333
static func toFullDateString(from date: Date) -> String {
34-
return DateHelper.dateFormatter.string(from: date)
34+
DateHelper.dateFormatter.string(from: date)
3535
}
3636
}

QuantiLogger/common/FileLogger.swift

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
/// Pre-built logger that logs to a single or multiple files within dedicated log dir.
1212
public class FileLogger: Logging {
13-
private let fileLoggerManager = FileLoggerManager.shared
13+
private let fileLoggerManager: FileLoggerManager
1414

1515
/// Property to set a number of log files that can be used for loging.
1616
public var numOfLogFiles: Int = 4 {
@@ -19,25 +19,35 @@ public class FileLogger: Logging {
1919
}
2020
}
2121

22-
// Url of the zip file containing all log files.
23-
public var archivedLogFilesUrl: URL? {
24-
return fileLoggerManager.archivedLogFilesUrl
25-
}
26-
27-
public var archivedLogFiles: Archive? {
28-
return fileLoggerManager.archivedLogFiles
29-
}
30-
3122
public var levels: [Level] = [.info]
3223

33-
public init() {}
24+
/// FileLogger initializer
25+
///
26+
/// - Parameters:
27+
/// - subsystem: suit name of the application. Must be passed to create logs from app extensions.
28+
public init(suiteName: String? = nil) {
29+
fileLoggerManager = FileLoggerManager(suiteName: suiteName)
30+
}
3431

3532
public func log(_ message: String, onLevel level: Level) {
3633
fileLoggerManager.writeToLogFile(message: message, withMessageHeader: messageHeader(forLevel: level), onLevel: level)
3734
}
3835

39-
public func deleteAllLogFiles() {
40-
fileLoggerManager.deleteAllLogFiles()
36+
/// Delete all logs
37+
///
38+
/// - Parameters:
39+
/// - subsystem: suit name of the application. Must be passed to also delete logs from app extensions.
40+
public func deleteAllLogFiles(suiteName: String? = nil) {
41+
fileLoggerManager.deleteAllLogFiles(suiteName: suiteName)
4142
}
4243

44+
/// Get archive that contains logs
45+
///
46+
/// - Parameters:
47+
/// - subsystem: suit name of the application. Must be passed to also add logs from app extensions to archive.
48+
///
49+
/// - Returns: compressed archive with logs
50+
public func getArchivedLogFiles(suiteName: String? = nil) -> Archive? {
51+
fileLoggerManager.getArchivedLogFiles(suiteName: suiteName)
52+
}
4353
}

QuantiLogger/common/FileLoggerManager.swift

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ class FileLoggerManager {
1313
/// The class is used as a Singleton, thus should be accesed via instance property !!!
1414
static let shared = FileLoggerManager()
1515

16-
let logDirUrl: URL? = {
16+
lazy var logDirUrl: URL? = {
1717
do {
1818
let fileManager = FileManager.default
19-
let documentDirUrl = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
20-
let _logDirUrl = documentDirUrl.appendingPathComponent("logs")
19+
var dirUrl: URL
20+
if let suiteName = suiteName,
21+
let url = fileManager.containerURL(forSecurityApplicationGroupIdentifier: suiteName) {
22+
dirUrl = url
23+
} else {
24+
dirUrl = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
25+
}
26+
let _logDirUrl = dirUrl.appendingPathComponent("logs")
2127
if !fileManager.fileExists(atPath: _logDirUrl.path) {
2228
try fileManager.createDirectory(at: _logDirUrl, withIntermediateDirectories: true, attributes: nil)
2329
}
@@ -30,6 +36,8 @@ class FileLoggerManager {
3036
}
3137
}()
3238

39+
private let suiteName: String?
40+
3341
private(set) var currentLogFileNumber: Int = 0 {
3442
didSet {
3543
// Check if currentLogFileNumber got updated, if not - do nothing, thus keep the currentWritableFileHandle opened
@@ -56,7 +64,9 @@ class FileLoggerManager {
5664
}
5765

5866
var currentLogFileUrl: URL? {
59-
return logDirUrl?.appendingPathComponent("\(currentLogFileNumber)").appendingPathExtension("log")
67+
suiteName == nil ?
68+
logDirUrl?.appendingPathComponent("\(currentLogFileNumber)").appendingPathExtension("log") :
69+
logDirUrl?.appendingPathComponent("extension-\(currentLogFileNumber)").appendingPathExtension("log")
6070
}
6171

6272
private var currentWritableFileHandle: FileHandle? {
@@ -86,21 +96,42 @@ class FileLoggerManager {
8696
}
8797
}
8898

89-
// Url of the zip file containing all log files.
90-
var archivedLogFilesUrl: URL? {
91-
return archivedLogFiles?.url
99+
init(suiteName: String? = nil) {
100+
self.suiteName = suiteName
101+
if let _dateOfLastLog = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.dateOfLastLog) as? Date {
102+
dateOfLastLog = _dateOfLastLog
103+
} else {
104+
UserDefaults.standard.set(dateOfLastLog, forKey: QuantiLoggerConstants.UserDefaultsKeys.dateOfLastLog)
105+
}
106+
107+
if let _currentLogFileNumber = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.currentLogFileNumber) as? Int {
108+
currentLogFileNumber = _currentLogFileNumber
109+
} else {
110+
UserDefaults.standard.set(currentLogFileNumber, forKey: QuantiLoggerConstants.UserDefaultsKeys.currentLogFileNumber)
111+
}
112+
113+
if let _numOfLogFiles = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.numOfLogFiles) as? Int {
114+
numOfLogFiles = _numOfLogFiles
115+
} else {
116+
UserDefaults.standard.set(numOfLogFiles, forKey: QuantiLoggerConstants.UserDefaultsKeys.numOfLogFiles)
117+
}
92118
}
93119

94-
// Zip file containing log files
95-
var archivedLogFiles: Archive? {
120+
/// Get archive that contains logs
121+
///
122+
/// - Parameters:
123+
/// - subsystem: suit name of the application. Must be passed to add logs from app extensions to archive.
124+
///
125+
/// - Returns: compressed archive with logs
126+
func getArchivedLogFiles(suiteName: String?) -> Archive? {
96127
guard let _logDirUrl = logDirUrl else {
97128
print("\(#function) - logDirUrl is nil.")
98129
return nil
99130
}
100131

101132
let archiveUrl = _logDirUrl.appendingPathComponent("log_files_archive.zip")
102133

103-
guard let allLogFiles = gettingAllLogFiles(), allLogFiles.count > 0 else {
134+
guard let allLogFiles = gettingAllLogFiles(suiteName: suiteName), allLogFiles.count > 0 else {
104135
print("\(#function) - no log files.")
105136
return nil
106137
}
@@ -139,26 +170,6 @@ class FileLoggerManager {
139170
return archive
140171
}
141172

142-
private init() {
143-
if let _dateOfLastLog = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.dateOfLastLog) as? Date {
144-
dateOfLastLog = _dateOfLastLog
145-
} else {
146-
UserDefaults.standard.set(dateOfLastLog, forKey: QuantiLoggerConstants.UserDefaultsKeys.dateOfLastLog)
147-
}
148-
149-
if let _currentLogFileNumber = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.currentLogFileNumber) as? Int {
150-
currentLogFileNumber = _currentLogFileNumber
151-
} else {
152-
UserDefaults.standard.set(currentLogFileNumber, forKey: QuantiLoggerConstants.UserDefaultsKeys.currentLogFileNumber)
153-
}
154-
155-
if let _numOfLogFiles = UserDefaults.standard.object(forKey: QuantiLoggerConstants.UserDefaultsKeys.numOfLogFiles) as? Int {
156-
numOfLogFiles = _numOfLogFiles
157-
} else {
158-
UserDefaults.standard.set(numOfLogFiles, forKey: QuantiLoggerConstants.UserDefaultsKeys.numOfLogFiles)
159-
}
160-
}
161-
162173
/// Method to reset properties that control the correct flow of storing log files.
163174
/// - "currentLogFileNumber" represents the current logging file number
164175
/// - "dateTimeOfLastLog" represents the last date the logger was used
@@ -171,8 +182,8 @@ class FileLoggerManager {
171182
}
172183

173184
/// Method to remove all log files from dedicated log folder. These files are detected by its ".log" suffix.
174-
func deleteAllLogFiles() {
175-
guard let aLogFiles = gettingAllLogFiles() else { return }
185+
func deleteAllLogFiles(suiteName: String? = nil) {
186+
guard let aLogFiles = gettingAllLogFiles(suiteName: suiteName) else { return }
176187

177188
aLogFiles.forEach { (aLogFileUrl) in
178189
deleteLogFile(at: aLogFileUrl)
@@ -231,12 +242,22 @@ class FileLoggerManager {
231242

232243
/// Method to get all log file names from dedicated log folder. These files are detected by its ".log" suffix.
233244
///
245+
/// - Parameters:
246+
/// - subsystem: suit name of the application. Must be passed to also get logs from app extensions.
247+
///
234248
/// - Returns: Array of log file names
235-
func gettingAllLogFiles() -> [URL]? {
236-
guard let _logDirUrl = logDirUrl else { return nil }
237-
249+
func gettingAllLogFiles(suiteName: String? = nil) -> [URL]? {
250+
var logDirectories = [logDirUrl]
251+
if let suiteName = suiteName,
252+
let extensionDirectory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName)?.appendingPathComponent("logs"), FileManager.default.fileExists(atPath: extensionDirectory.path) {
253+
logDirectories.append(extensionDirectory)
254+
}
238255
do {
239-
let directoryContent = try FileManager.default.contentsOfDirectory(at: _logDirUrl, includingPropertiesForKeys: nil, options: [])
256+
let directoryContent = try logDirectories
257+
.compactMap { $0 }
258+
.flatMap {
259+
try FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil, options: [])
260+
}
240261
let logFiles = directoryContent.filter({ (file) -> Bool in
241262
file.pathExtension == "log"
242263
})

QuantiLogger/common/HTTPURLResponse+logDescription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ import Foundation
1010

1111
extension HTTPURLResponse {
1212
var logDescription: String {
13-
return "Response: {url: \(url?.description ?? "nil"), statusCode: \(statusCode), headersFields: \(allHeaderFields)}"
13+
"Response: {url: \(url?.description ?? "nil"), statusCode: \(statusCode), headersFields: \(allHeaderFields)}"
1414
}
1515
}

QuantiLogger/common/JSONSerializable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ extension JSONSerializable {
7777
// enables to serialize [JSONSerializable]. This method does so!
7878
extension Array: JSONSerializable {
7979
var jsonRepresentation: AnyObject {
80-
return self.map { (element) -> AnyObject in
80+
self.map { (element) -> AnyObject in
8181
if let _element = element as? JSONRepresentable {
8282
return _element.jsonRepresentation
8383
}

QuantiLogger/common/LogManager.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public class LogManager {
5959
///
6060
/// - Returns: the logger if exists, nil otherwise
6161
public func logger<T: Logging>() -> T? {
62-
return loggers.compactMap { $0 as? T }.first
62+
loggers.compactMap { $0 as? T }.first
6363
}
6464

6565
/// Method to register a new custom or pre-build logger.
@@ -108,12 +108,15 @@ public class LogManager {
108108
}
109109

110110
/// Method to delete all log files if there are any.
111-
public func deleteAllLogFiles() {
111+
///
112+
/// - Parameters:
113+
/// - subsystem: suit name of the application. Must be passed to also delete logs from app extensions.
114+
public func deleteAllLogFiles(suiteName: String? = nil) {
112115
serialLoggingQueue.async {
113116
dispatchPrecondition(condition: .onQueue(self.serialLoggingQueue))
114117

115118
self.loggers.compactMap { $0 as? FileLogger }
116-
.forEach { $0.deleteAllLogFiles() }
119+
.forEach { $0.deleteAllLogFiles(suiteName: suiteName) }
117120
}
118121
}
119122

QuantiLogger/common/Logging.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ extension Logging {
1818
public func configure() {}
1919

2020
public func messageHeader(forLevel level: Level) -> String {
21-
return "[\(level.rawValue) \(Date().toFullDateTimeString())]"
21+
"[\(level.rawValue) \(Date().toFullDateTimeString())]"
2222
}
2323

2424
func doesLog(forLevel level: Level) -> Bool {
25-
return levels.contains(level)
25+
levels.contains(level)
2626
}
2727
}

0 commit comments

Comments
 (0)