mirror of
https://github.com/writeas/writefreely-swiftui-multiplatform.git
synced 2024-11-15 01:11:02 +00:00
105 lines
3.0 KiB
Swift
105 lines
3.0 KiB
Swift
// Credit for much of this class: https://steipete.com/posts/logging-in-swift/
|
|
|
|
import Foundation
|
|
import os
|
|
import OSLog
|
|
|
|
protocol LogWriter {
|
|
func log(_ message: String, withSensitiveInfo privateInfo: String?, level: OSLogType)
|
|
func logCrashAndSetFlag(error: Error)
|
|
}
|
|
|
|
@available(iOS 15, *)
|
|
protocol LogReader {
|
|
func fetchLogs() -> [String]
|
|
}
|
|
|
|
final class Logging {
|
|
|
|
private let logger: Logger
|
|
private let subsystem = Bundle.main.bundleIdentifier!
|
|
|
|
init(for category: String = "") {
|
|
self.logger = Logger(subsystem: subsystem, category: category)
|
|
}
|
|
|
|
}
|
|
|
|
extension Logging: LogWriter {
|
|
|
|
func log(
|
|
_ message: String,
|
|
withSensitiveInfo privateInfo: String? = nil,
|
|
level: OSLogType = .default
|
|
) {
|
|
if let privateInfo = privateInfo {
|
|
logger.log(level: level, "\(message): \(privateInfo, privacy: .sensitive)")
|
|
} else {
|
|
logger.log(level: level, "\(message)")
|
|
}
|
|
}
|
|
|
|
func logCrashAndSetFlag(error: Error) {
|
|
let errorDescription = error.localizedDescription
|
|
UserDefaults.shared.set(true, forKey: WFDefaults.didHaveFatalError)
|
|
UserDefaults.shared.set(errorDescription, forKey: WFDefaults.fatalErrorDescription)
|
|
logger.log(level: .error, "\(errorDescription)")
|
|
fatalError(errorDescription)
|
|
}
|
|
|
|
}
|
|
|
|
extension Logging: LogReader {
|
|
|
|
@available(iOS 15, *)
|
|
func fetchLogs() -> [String] {
|
|
var logs: [String] = []
|
|
|
|
do {
|
|
let osLog = try getLogEntries()
|
|
for logEntry in osLog {
|
|
let formattedEntry = formatEntry(logEntry)
|
|
logs.append(formattedEntry)
|
|
}
|
|
} catch {
|
|
logs.append("Could not fetch logs")
|
|
}
|
|
|
|
return logs
|
|
}
|
|
|
|
@available(iOS 15, *)
|
|
private func getLogEntries() throws -> [OSLogEntryLog] {
|
|
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
|
|
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
|
|
let allEntries = try Array(logStore.__entriesEnumerator(position: oneHourAgo, predicate: nil))
|
|
return allEntries
|
|
.compactMap { $0 as? OSLogEntryLog }
|
|
.filter { $0.subsystem == subsystem }
|
|
}
|
|
|
|
@available(iOS 15, *)
|
|
private func formatEntry(_ logEntry: OSLogEntryLog) -> String {
|
|
/// The desired format is:
|
|
/// `date [process/category] LEVEL: composedMessage (threadIdentifier)`
|
|
var level: String = ""
|
|
switch logEntry.level {
|
|
case .debug:
|
|
level = "DEBUG"
|
|
case .info:
|
|
level = "INFO"
|
|
case .notice:
|
|
level = "NOTICE"
|
|
case .error:
|
|
level = "ERROR"
|
|
case .fault:
|
|
level = "FAULT"
|
|
default:
|
|
level = "UNDEFINED"
|
|
}
|
|
// swiftlint:disable:next line_length
|
|
return "\(logEntry.date) [\(logEntry.process)/\(logEntry.category)] \(level): \(logEntry.composedMessage) (\(logEntry.threadIdentifier))"
|
|
}
|
|
|
|
}
|