From a87a547886831a81bbfec64c40edb10cc428e5af Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Fri, 6 Sep 2024 17:35:28 +1000 Subject: [PATCH] libSession attachment upload/download improvements, logging tweaks --- LibSession-Util | 2 +- Session/Utilities/IP2Country.swift | 8 +- SessionUtilitiesKit/General/Logging.swift | 76 ++++++++++++++++--- .../LibSession/LibSession.swift | 70 ++++++++--------- 4 files changed, 104 insertions(+), 52 deletions(-) diff --git a/LibSession-Util b/LibSession-Util index c3aa3b99e..1d20f5e39 160000 --- a/LibSession-Util +++ b/LibSession-Util @@ -1 +1 @@ -Subproject commit c3aa3b99e05d7e7568cdd1dc2e0f1200e3ec01f1 +Subproject commit 1d20f5e392ba55847a30431f196d638834a4feb0 diff --git a/Session/Utilities/IP2Country.swift b/Session/Utilities/IP2Country.swift index 59fdf72d5..3fa8f382d 100644 --- a/Session/Utilities/IP2Country.swift +++ b/Session/Utilities/IP2Country.swift @@ -8,10 +8,14 @@ import GRDB import SessionSnodeKit import SessionUtilitiesKit -private extension Log.Category { - static var ip2Country: Log.Category = "IP2Country" +// MARK: - Log.Level + +public extension Log.Category { + static let ip2Country: Log.Category = .create("IP2Country", defaultLevel: .info) } +// MARK: - IP2Country + public enum IP2Country { public static var isInitialized: Atomic = Atomic(false) private static var countryNamesCache: Atomic<[String: String]> = Atomic([:]) diff --git a/SessionUtilitiesKit/General/Logging.swift b/SessionUtilitiesKit/General/Logging.swift index 23cbb1320..8aea7d733 100644 --- a/SessionUtilitiesKit/General/Logging.swift +++ b/SessionUtilitiesKit/General/Logging.swift @@ -25,19 +25,42 @@ public enum Log { case error case critical case off + + case `default` } - public struct Category: ExpressibleByStringLiteral, ExpressibleByExtendedGraphemeClusterLiteral, ExpressibleByUnicodeScalarLiteral { - public typealias StringLiteralType = String + public struct Category: Hashable { + public let rawValue: String + fileprivate let customPrefix: String + public let defaultLevel: Log.Level + + fileprivate static let identifierPrefix: String = "logLevel-" + fileprivate var identifier: String { "\(Category.identifierPrefix)\(rawValue)" } + + private init(rawValue: String, customPrefix: String, defaultLevel: Log.Level) { + self.rawValue = rawValue + self.customPrefix = customPrefix + self.defaultLevel = defaultLevel + + AllLoggingCategories.register(category: self) + } - fileprivate let rawValue: String + fileprivate init?(identifier: String) { + guard identifier.hasPrefix(Category.identifierPrefix) else { return nil } + + self.init( + rawValue: identifier.substring(from: Category.identifierPrefix.count), + customPrefix: "", + defaultLevel: .default + ) + } - public init(stringLiteral value: String) { - self.rawValue = value + public init(rawValue: String, customPrefix: String = "") { + self.init(rawValue: rawValue, customPrefix: customPrefix, defaultLevel: .default) } - public init(unicodeScalarLiteral value: Character) { - self.rawValue = String(value) + @discardableResult public static func create(_ rawValue: String, customPrefix: String = "", defaultLevel: Log.Level) -> Log.Category { + return Log.Category(rawValue: rawValue, customPrefix: customPrefix, defaultLevel: defaultLevel) } } @@ -501,7 +524,7 @@ public class Logger { (DispatchQueue.isDBWriteQueue ? "DBWrite" : nil) ] .compactMap { $0 } - .appending(contentsOf: categories.map { $0.rawValue }) + .appending(contentsOf: categories.map { "\($0.customPrefix)\($0.rawValue)" }) .joined(separator: ", ") return "[\(prefixes)] " @@ -516,7 +539,7 @@ public class Logger { .trimmingCharacters(in: .whitespacesAndNewlines) switch level { - case .off: return + case .off, .default: return case .verbose: DDLogVerbose("💙 \(logMessage)", file: file, function: function, line: line) case .debug: DDLogDebug("💚 \(logMessage)", file: file, function: function, line: line) case .info: DDLogInfo("💛 \(logMessage)", file: file, function: function, line: line) @@ -525,7 +548,7 @@ public class Logger { case .critical: DDLogError("🔥 \(logMessage)", file: file, function: function, line: line) } - let mainCategory: String = (categories.first?.rawValue ?? "[General]") + let mainCategory: String = (categories.first?.rawValue ?? "General") var systemLogger: SystemLoggerType? = systemLoggers.wrappedValue[mainCategory] if systemLogger == nil { @@ -577,7 +600,7 @@ private class SystemLogger: SystemLoggerType { public func log(_ level: Log.Level, _ log: String) { switch level { - case .off: return + case .off, .default: return case .verbose: logger.trace("\(log)") case .debug: logger.debug("\(log)") case .info: logger.info("\(log)") @@ -623,3 +646,34 @@ private extension DispatchQueue { public func SNLog(_ message: String, forceNSLog: Bool = false) { Log.info(message) } + +// MARK: - AllLoggingCategories + +public struct AllLoggingCategories { + public static var defaultLevels: [Log.Category: Log.Level] { + return AllLoggingCategories.registeredCategoryDefaults.wrappedValue + .reduce(into: [:]) { result, cat in result[cat] = cat.defaultLevel } + } + private static let registeredCategoryDefaults: Atomic> = Atomic([]) + + // MARK: - Initialization + + public let rawValue: Int + + public init(rawValue: Int) { + self.rawValue = -1 // `0` is a protected value so can't use it + } + + fileprivate static func register(category: Log.Category) { + guard + !registeredCategoryDefaults.wrappedValue.contains(where: { cat in + /// **Note:** We only want to use the `rawValue` to distinguish between logging categories + /// as the `defaultLevel` can change via the dev settings and any additional metadata could + /// be file/class specific + category.rawValue != cat.rawValue + }) + else { return } + + registeredCategoryDefaults.mutate { $0.insert(category) } + } +} diff --git a/SessionUtilitiesKit/LibSession/LibSession.swift b/SessionUtilitiesKit/LibSession/LibSession.swift index 585fedee6..07591480b 100644 --- a/SessionUtilitiesKit/LibSession/LibSession.swift +++ b/SessionUtilitiesKit/LibSession/LibSession.swift @@ -8,12 +8,6 @@ import SessionUtil // MARK: - LibSession public enum LibSession { - private static let logLevels: [LogCategory: LOG_LEVEL] = [ - .config: LOG_LEVEL_INFO, - .network: LOG_LEVEL_INFO, - .manual: LOG_LEVEL_INFO, - ] - public static var version: String { String(cString: LIBSESSION_UTIL_VERSION_STR) } } @@ -21,19 +15,29 @@ public enum LibSession { extension LibSession { public static func addLogger() { + /// Setup any custom category defaul log levels for libSession + Log.Category.create("config", defaultLevel: .info) + Log.Category.create("network", defaultLevel: .info) + /// Set the default log level first (unless specified we only care about semi-dangerous logs) session_logger_set_level_default(LOG_LEVEL_WARN) /// Then set any explicit category log levels we have - logLevels.forEach { cat, level in - guard let cCat: [CChar] = cat.rawValue.cString(using: .utf8) else { return } + AllLoggingCategories.defaultLevels.forEach { category, level in + guard + let cCat: [CChar] = category.rawValue.cString(using: .utf8), + let cLogLevel: LOG_LEVEL = level.libSession + else { return } - session_logger_set_level(cCat, level) + session_logger_set_level(cCat, cLogLevel) } /// Finally register the actual logger callback session_add_logger_full({ msgPtr, msgLen, catPtr, catLen, lvl in - guard let msg: String = String(pointer: msgPtr, length: msgLen, encoding: .utf8) else { return } + guard + let msg: String = String(pointer: msgPtr, length: msgLen, encoding: .utf8), + let cat: String = String(pointer: catPtr, length: catLen, encoding: .utf8) + else { return } /// Logs from libSession come through in the format: /// `[yyyy-MM-dd hh:mm:ss] [+{lifetime}s] [{cat}:{lvl}|log.hpp:{line}] {message}` @@ -48,45 +52,35 @@ extension LibSession { return "\(logParts[1])] \(message)" }() - Log.custom(Log.Level(lvl), [LogCategory(catPtr, catLen).logCat], processedMessage) + Log.custom( + Log.Level(lvl), + [Log.Category(rawValue: cat, customPrefix: "libSession:")], + processedMessage + ) }) } public static func clearLoggers() { session_clear_loggers() } - - // MARK: - Internal - - fileprivate enum LogCategory: String { - case libSession - case config - case network - case quic - case manual - - var logCat: Log.Category { - switch self { - case .libSession: return "libSession" - case .config: return "libSession:config" - case .network: return "libSession:network" - case .quic: return "libSession:quic" - case .manual: return "libSession:manual" - } - } - - init(_ catPtr: UnsafePointer?, _ catLen: Int) { - switch String(pointer: catPtr, length: catLen, encoding: .utf8).map({ LogCategory(rawValue: $0) }) { - case .some(let cat): self = cat - case .none: self = .libSession - } - } - } } // MARK: - Convenience fileprivate extension Log.Level { + var libSession: LOG_LEVEL? { + switch self { + case .verbose: return LOG_LEVEL_TRACE + case .debug: return LOG_LEVEL_DEBUG + case .info: return LOG_LEVEL_INFO + case .warn: return LOG_LEVEL_WARN + case .error: return LOG_LEVEL_ERROR + case .critical: return LOG_LEVEL_CRITICAL + case .off: return LOG_LEVEL_OFF + case .default: return nil // It'll use the default value by default so just return nil + } + } + init(_ level: LOG_LEVEL) { switch level { case LOG_LEVEL_TRACE: self = .verbose