Structured logging (#831)

* Structured logging support

* Bump the SDK, fix breaking changes

* Enabled more logging of timeline diffs

* Keep only source file last path components in logs

* Bump the SDK, tweaks following code review
This commit is contained in:
Stefan Ceriu
2023-04-25 14:48:20 +03:00
committed by GitHub
parent 0d3e823b69
commit be8adb19b5
20 changed files with 286 additions and 252 deletions
@@ -111,8 +111,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift", "location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : { "state" : {
"revision" : "8ce08eb804c449f4e7472e66edd7e80b4e8e9ded", "revision" : "1febfb02005fca9cf99264be72c6383c103085c5",
"version" : "1.0.54-alpha" "version" : "1.0.56-alpha"
} }
}, },
{ {
@@ -214,15 +214,6 @@
"version" : "0.2.3" "version" : "0.2.3"
} }
}, },
{
"identity" : "swiftybeaver",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SwiftyBeaver/SwiftyBeaver",
"state" : {
"revision" : "1080914828ef1c9ca9cd2bad50667b3d847dabff",
"version" : "2.0.0"
}
},
{ {
"identity" : "version", "identity" : "version",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@@ -51,10 +51,11 @@ class AppCoordinator: AppCoordinatorProtocol {
@Consumable private var storedAppRoute: AppRoute? @Consumable private var storedAppRoute: AppRoute?
init() { init() {
MXLog.configure()
navigationRootCoordinator = NavigationRootCoordinator() navigationRootCoordinator = NavigationRootCoordinator()
Self.setupServiceLocator(navigationRootCoordinator: navigationRootCoordinator) Self.setupServiceLocator(navigationRootCoordinator: navigationRootCoordinator)
Self.setupLogging()
ServiceLocator.shared.analytics.startIfEnabled() ServiceLocator.shared.analytics.startIfEnabled()
@@ -118,26 +119,6 @@ class AppCoordinator: AppCoordinatorProtocol {
ServiceLocator.shared.register(analytics: Analytics(client: PostHogAnalyticsClient())) ServiceLocator.shared.register(analytics: Analytics(client: PostHogAnalyticsClient()))
} }
private static func setupLogging() {
let loggerConfiguration = MXLogConfiguration()
loggerConfiguration.maxLogFilesCount = 10
#if DEBUG
setupTracing(configuration: .debug)
loggerConfiguration.logLevel = .debug
#else
setupTracing(configuration: .release)
loggerConfiguration.logLevel = .info
#endif
// Avoid redirecting NSLogs to files if we are attached to a debugger.
if isatty(STDERR_FILENO) == 0 {
loggerConfiguration.redirectLogsToFiles = true
}
MXLog.configure(loggerConfiguration)
}
/// Perform any required migrations for the app to function correctly. /// Perform any required migrations for the app to function correctly.
private func performMigrationsIfNecessary(from oldVersion: Version, to newVersion: Version) { private func performMigrationsIfNecessary(from oldVersion: Version, to newVersion: Version) {
guard oldVersion != newVersion else { return } guard oldVersion != newVersion else { return }
@@ -19,13 +19,15 @@ import SwiftUI
@main @main
struct Application: App { struct Application: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var applicationDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) private var applicationDelegate
private let applicationCoordinator: AppCoordinatorProtocol private let appCoordinator: AppCoordinatorProtocol!
init() { init() {
if Tests.isRunningUITests { if Tests.isRunningUITests {
applicationCoordinator = UITestsAppCoordinator() appCoordinator = UITestsAppCoordinator()
} else if Tests.isRunningUnitTests {
appCoordinator = nil
} else { } else {
applicationCoordinator = AppCoordinator() appCoordinator = AppCoordinator()
} }
} }
@@ -34,9 +36,9 @@ struct Application: App {
if Tests.isRunningUnitTests { if Tests.isRunningUnitTests {
EmptyView() EmptyView()
} else { } else {
applicationCoordinator.toPresentable() appCoordinator.toPresentable()
.task { .task {
applicationCoordinator.start() appCoordinator.start()
} }
.statusBarHidden(shouldHideStatusBar) .statusBarHidden(shouldHideStatusBar)
} }
+141 -114
View File
@@ -15,87 +15,119 @@
// //
import Foundation import Foundation
import SwiftyBeaver import MatrixRustSDK
/// Various MXLog configuration options. Used in conjunction with `MXLog.configure()`
public class MXLogConfiguration: NSObject {
/// the desired log level. `.verbose` by default.
public var logLevel = MXLogLevel.verbose
/// whether logs should be written directly to files. `false` by default.
public var redirectLogsToFiles = false
/// the maximum total space to use for log files in bytes. `100MB` by default.
public var logFilesSizeLimit: UInt = 100 * 1024 * 1024 // 100MB
/// the maximum number of log files to use before rolling. `50` by default.
public var maxLogFilesCount: UInt = 50
/// the subname for log files. Files will be named as 'console-[subLogName].log'. `nil` by default
public var subLogName: String?
}
/// MXLog logging levels. Use .none to disable logging entirely.
public enum MXLogLevel: UInt {
case none
case verbose
case debug
case info
case warning
case error
}
private var logger: SwiftyBeaver.Type = {
let logger = SwiftyBeaver.self
MXLog.configureLogger(logger, withConfiguration: MXLogConfiguration())
return logger
}()
/** /**
Logging utility that provies multiple logging levels as well as file output and rolling. Logging utility that provies multiple logging levels as well as file output and rolling.
Its purpose is to provide a common entry for customizing logging and should be used throughout the code. Its purpose is to provide a common entry for customizing logging and should be used throughout the code.
Please see `MXLog.h` for Objective-C options.
*/ */
public class MXLog: NSObject { enum MXLog {
/// Method used to customize MXLog's behavior. private enum Constants {
/// Called automatically when first accessing the logger with the default values. static let target = "elementx"
/// Please see `MXLogConfiguration` for all available options.
/// - Parameters: // Avoid redirecting NSLogs to files if we are attached to a debugger.
/// - configuration: the `MXLogConfiguration` instance to use static let redirectToFiles = isatty(STDERR_FILENO) == 0
public static func configure(_ configuration: MXLogConfiguration) {
configureLogger(logger, withConfiguration: configuration) /// the maximum number of log files to use before rolling. `10` by default.
static let maxLogFileCount: UInt = 10
/// the maximum total space to use for log files in bytes. `100MB` by default.
static let logFilesSizeLimit: UInt = 100 * 1024 * 1024 // 100MB
} }
public static func verbose(_ message: @autoclosure () -> Any, // Rust side crashes if invoking setupTracing multiple times
file: String = #file, private static var didConfigureOnce = false
function: String = #function,
line: Int = #line, private static var rootSpan: Span!
context: Any? = nil) { private static var target: String!
logger.verbose(message(), file: file, function: function, line: line, context: context)
static func configure(target: String? = nil,
logLevel: TracingConfiguration.LogLevel? = nil,
redirectToFiles: Bool = Constants.redirectToFiles,
maxLogFileCount: UInt = Constants.maxLogFileCount,
logFileSizeLimit: UInt = Constants.logFilesSizeLimit) {
guard didConfigureOnce == false else {
if let target {
MXLogger.setSubLogName(target)
}
// SubLogName needs to be set before calling configure in order to be applied
MXLogger.configure(redirectToFiles: redirectToFiles,
maxLogFileCount: maxLogFileCount,
logFileSizeLimit: logFileSizeLimit)
return
}
if let logLevel {
setupTracing(configuration: .custom(logLevel: logLevel))
} else {
#if DEBUG
setupTracing(configuration: .debug)
#else
setupTracing(configuration: .release)
#endif
}
if let target {
self.target = target
MXLogger.setSubLogName(target)
} else {
self.target = Constants.target
}
rootSpan = Span(file: #file, line: #line, column: #column, level: .info, target: self.target, name: "root")
rootSpan.enter()
MXLogger.configure(redirectToFiles: redirectToFiles,
maxLogFileCount: maxLogFileCount,
logFileSizeLimit: logFileSizeLimit)
didConfigureOnce = true
} }
public static func debug(_ message: @autoclosure () -> Any, static func createSpan(_ name: String,
file: String = #file, file: String = #file,
function: String = #function, function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { column: Int = #column) -> Span {
logger.debug(message(), file: file, function: function, line: line, context: context) createSpan(name, level: .info, file: file, function: function, line: line, column: column)
} }
public static func info(_ message: @autoclosure () -> Any, static func verbose(_ message: Any,
file: String = #file, file: String = #file,
function: String = #function, function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { column: Int = #column,
logger.info(message(), file: file, function: function, line: line, context: context) context: Any? = nil) {
log(message, level: .trace, file: file, function: function, line: line, column: column, context: context)
} }
public static func warning(_ message: @autoclosure () -> Any, static func debug(_ message: Any,
file: String = #file, file: String = #file,
function: String = #function, function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { column: Int = #column,
logger.warning(message(), file: file, function: function, line: line, context: context) context: Any? = nil) {
log(message, level: .debug, file: file, function: function, line: line, column: column, context: context)
}
static func info(_ message: Any,
file: String = #file,
function: String = #function,
line: Int = #line,
column: Int = #column,
context: Any? = nil) {
log(message, level: .info, file: file, function: function, line: line, column: column, context: context)
}
static func warning(_ message: Any,
file: String = #file,
function: String = #function,
line: Int = #line,
column: Int = #column,
context: Any? = nil) {
log(message, level: .warn, file: file, function: function, line: line, column: column, context: context)
} }
/// Log error with additional details /// Log error with additional details
@@ -103,12 +135,13 @@ public class MXLog: NSObject {
/// - Parameters: /// - Parameters:
/// - message: Description of the error without any variables (this is to improve error aggregations by type) /// - message: Description of the error without any variables (this is to improve error aggregations by type)
/// - context: Additional context-dependent details about the issue /// - context: Additional context-dependent details about the issue
public static func error(_ message: @autoclosure () -> Any, static func error(_ message: Any,
file: String = #file, file: String = #file,
function: String = #function, function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { column: Int = #column,
logger.error(message(), file: file, function: function, line: line, context: context) context: Any? = nil) {
log(message, level: .error, file: file, function: function, line: line, column: column, context: context)
} }
/// Log failure with additional details /// Log failure with additional details
@@ -119,12 +152,14 @@ public class MXLog: NSObject {
/// - Parameters: /// - Parameters:
/// - message: Description of the error without any variables (this is to improve error aggregations by type) /// - message: Description of the error without any variables (this is to improve error aggregations by type)
/// - context: Additional context-dependent details about the issue /// - context: Additional context-dependent details about the issue
public static func failure(_ message: String, static func failure(_ message: Any,
file: String = #file, file: String = #file,
function: String = #function, function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { column: Int = #column,
logger.error(message, file: file, function: function, line: line, context: context) context: Any? = nil) {
log(message, level: .error, file: file, function: function, line: line, column: column, context: context)
#if DEBUG #if DEBUG
assertionFailure("\(message)") assertionFailure("\(message)")
#endif #endif
@@ -132,46 +167,38 @@ public class MXLog: NSObject {
// MARK: - Private // MARK: - Private
fileprivate static func configureLogger(_ logger: SwiftyBeaver.Type, withConfiguration configuration: MXLogConfiguration) { private static func createSpan(_ name: String,
if let subLogName = configuration.subLogName { level: LogLevel,
MXLogger.setSubLogName(subLogName) file: String = #file,
function: String = #function,
line: Int = #line,
column: Int = #column) -> Span {
guard didConfigureOnce else {
fatalError()
} }
MXLogger.redirectNSLog(toFiles: configuration.redirectLogsToFiles, if Span.current().isNone() {
numberOfFiles: configuration.maxLogFilesCount, rootSpan.enter()
sizeLimit: configuration.logFilesSizeLimit) }
guard configuration.logLevel != .none else { return Span(file: file, line: UInt32(line), column: UInt32(column), level: level, target: target, name: name)
logger.removeAllDestinations() }
private static func log(_ message: Any,
level: LogLevel,
file: String = #file,
function: String = #function,
line: Int = #line,
column: Int = #column,
context: Any? = nil) {
guard didConfigureOnce else {
return return
} }
logger.removeAllDestinations() if Span.current().isNone() {
rootSpan.enter()
let consoleDestination = ConsoleDestination()
consoleDestination.useNSLog = true
consoleDestination.asynchronously = false
consoleDestination.format = "$C$N.$F:$l $M $X$c" // See https://docs.swiftybeaver.com/article/20-custom-format
consoleDestination.levelColor.verbose = ""
consoleDestination.levelColor.debug = ""
consoleDestination.levelColor.info = ""
consoleDestination.levelColor.warning = "⚠️ "
consoleDestination.levelColor.error = "🚨 "
switch configuration.logLevel {
case .verbose:
consoleDestination.minLevel = .verbose
case .debug:
consoleDestination.minLevel = .debug
case .info:
consoleDestination.minLevel = .info
case .warning:
consoleDestination.minLevel = .warning
case .error:
consoleDestination.minLevel = .error
case .none:
break
} }
logger.addDestination(consoleDestination)
logEvent(file: (file as NSString).lastPathComponent, line: UInt32(line), column: UInt32(column), level: level, target: target, message: "\(message)")
} }
} }
@@ -37,14 +37,16 @@ class MXLogger {
/// ///
/// - Parameters: /// - Parameters:
/// - redirectToFiles: `true` to enable the redirection. /// - redirectToFiles: `true` to enable the redirection.
/// - numberOfFiles: number of files to keep (default is 10). /// - maxLogFileCount: number of files to keep (default is 10).
/// - sizeLimit: size limit of log files in bytes. 0 means no limitation, the default value for other methods /// - logFileSizeLimit: size limit of log files in bytes. 0 means no limitation, the default value for other methods
static func redirectNSLog(toFiles redirectToFiles: Bool, numberOfFiles: UInt = 10, sizeLimit: UInt = 0) { static func configure(redirectToFiles: Bool,
maxLogFileCount: UInt,
logFileSizeLimit: UInt) {
if redirectToFiles { if redirectToFiles {
var tempLog = "" var tempLog = ""
// Do a circular buffer based on X files // Do a circular buffer based on X files
for index in (0...(numberOfFiles - 2)).reversed() { for index in (0...(maxLogFileCount - 2)).reversed() {
rotateLog(at: index, tempLog: &tempLog) rotateLog(at: index, tempLog: &tempLog)
} }
@@ -60,10 +62,10 @@ class MXLogger {
MXLog.info(tempLog) MXLog.info(tempLog)
} }
removeExtraFiles(from: numberOfFiles) removeExtraFiles(from: maxLogFileCount)
if sizeLimit > 0 { if logFileSizeLimit > 0 {
removeFiles(after: sizeLimit) removeFiles(after: logFileSizeLimit)
} }
} else if stderrSave > 0 { } else if stderrSave > 0 {
// Flush before restoring stderr // Flush before restoring stderr
@@ -22,11 +22,30 @@ import MatrixRustSDK
// https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples // https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples
struct TracingConfiguration { struct TracingConfiguration {
static var release = TracingConfiguration(overrides: [.common: .info]) static var release = TracingConfiguration(overrides: [.common: .info])
static var debug = TracingConfiguration(overrides: [.common: .info])
static var debug = TracingConfiguration(overrides: [.matrix_sdk_crypto: .info,
.matrix_sdk_http_client: .info,
.matrix_sdk_sliding_sync: .info,
.matrix_sdk_base_sliding_sync: .info])
/// Configure tracing with certain overrides in place
/// - Parameter overrides: the desired overrides
/// - Returns: a custom tracing configuration
static func custom(overrides: [Target: LogLevel]) -> TracingConfiguration { static func custom(overrides: [Target: LogLevel]) -> TracingConfiguration {
TracingConfiguration(overrides: overrides) TracingConfiguration(overrides: overrides)
} }
/// Sets the same log level for all Targets
/// - Parameter logLevel: the desired log level
/// - Returns: a custom tracing configuration
static func custom(logLevel: LogLevel) -> TracingConfiguration {
let overrides = targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in
partialResult[target] = logLevel
}
return TracingConfiguration(overrides: overrides)
}
enum LogLevel: String { case error, warn, info, debug, trace } enum LogLevel: String { case error, warn, info, debug, trace }
enum Target: String { enum Target: String {
@@ -41,8 +60,8 @@ struct TracingConfiguration {
case matrix_sdk_room_timeline = "matrix_sdk::room::timeline" case matrix_sdk_room_timeline = "matrix_sdk::room::timeline"
} }
let targets: OrderedDictionary<Target, LogLevel> = [ static let targets: OrderedDictionary<Target, LogLevel> = [
.common: .warn, .common: .info,
.hyper: .warn, .hyper: .warn,
.sled: .warn, .sled: .warn,
.matrix_sdk_sled: .warn, .matrix_sdk_sled: .warn,
@@ -56,7 +75,7 @@ struct TracingConfiguration {
var overrides = [Target: LogLevel]() var overrides = [Target: LogLevel]()
var filter: String { var filter: String {
var newTargets = targets var newTargets = Self.targets
for (target, logLevel) in overrides { for (target, logLevel) in overrides {
newTargets.updateValue(logLevel, forKey: target) newTargets.updateValue(logLevel, forKey: target)
} }
@@ -93,7 +93,7 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
let client = try authenticationService.restoreWithAccessToken(token: token, deviceId: deviceID) let client = try authenticationService.restoreWithAccessToken(token: token, deviceId: deviceID)
return await userSession(for: client) return await userSession(for: client)
} catch { } catch {
MXLog.error(error) MXLog.error("Failed restoring with access token: \(error)")
return .failure(.failedLoggingIn) return .failure(.failedLoggingIn)
} }
} }
@@ -48,7 +48,7 @@ actor MediaLoader: MediaLoaderProtocol {
func loadMediaFileForSource(_ source: MediaSourceProxy) async throws -> MediaFileHandleProxy { func loadMediaFileForSource(_ source: MediaSourceProxy) async throws -> MediaFileHandleProxy {
let result = try await Task.dispatch(on: clientQueue) { let result = try await Task.dispatch(on: clientQueue) {
try self.client.getMediaFile(source: source.underlyingSource, mimeType: source.mimeType ?? "application/octet-stream") try self.client.getMediaFile(mediaSource: source.underlyingSource, mimeType: source.mimeType ?? "application/octet-stream")
} }
return MediaFileHandleProxy(handle: result) return MediaFileHandleProxy(handle: result)
@@ -80,7 +80,13 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
// MARK: - Private // MARK: - Private
fileprivate func updateRoomsWithDiffs(_ diffs: [SlidingSyncListRoomsListDiff]) { fileprivate func updateRoomsWithDiffs(_ diffs: [SlidingSyncListRoomsListDiff]) {
MXLog.info("Received \(diffs.count) diffs") let span = MXLog.createSpan("process_room_list_diffs")
span.enter()
defer {
span.exit()
}
MXLog.verbose("Received \(diffs.count) diffs")
rooms = diffs rooms = diffs
.reduce(rooms) { currentItems, diff in .reduce(rooms) { currentItems, diff in
@@ -101,7 +107,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
detectDuplicatesInRoomList(rooms) detectDuplicatesInRoomList(rooms)
MXLog.info("Finished applying \(diffs.count) diffs, new count: \(rooms.count)") MXLog.verbose("Finished applying \(diffs.count) diffs, new count: \(rooms.count)")
} }
private func buildRoomSummaryForIdentifier(_ identifier: String, invalidated: Bool) -> RoomSummary { private func buildRoomSummaryForIdentifier(_ identifier: String, invalidated: Bool) -> RoomSummary {
@@ -60,6 +60,7 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
switch await roomProxy.addTimelineListener(listener: roomTimelineListener) { switch await roomProxy.addTimelineListener(listener: roomTimelineListener) {
case .success(let items): case .success(let items):
itemProxies = items.map(TimelineItemProxy.init) itemProxies = items.map(TimelineItemProxy.init)
MXLog.info("Added timeline listener, current items (\(items.count)) : \(items.map(\.debugIdentifier))")
case .failure: case .failure:
let roomID = await roomProxy.id let roomID = await roomProxy.id
MXLog.error("Failed adding timeline listener on room with identifier: \(roomID)") MXLog.error("Failed adding timeline listener on room with identifier: \(roomID)")
@@ -70,7 +71,13 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
// MARK: - Private // MARK: - Private
private func updateItemsWithDiffs(_ diffs: [TimelineDiff]) { private func updateItemsWithDiffs(_ diffs: [TimelineDiff]) {
MXLog.verbose("Received timeline diff") let span = MXLog.createSpan("process_timeline_list_diffs")
span.enter()
defer {
span.exit()
}
MXLog.info("Received timeline diff")
itemProxies = diffs itemProxies = diffs
.reduce(itemProxies) { currentItems, diff in .reduce(itemProxies) { currentItems, diff in
@@ -84,12 +91,10 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
return currentItems return currentItems
} }
MXLog.verbose("Applied diff \(collectionDiff), new count: \(updatedItems.count)")
return updatedItems return updatedItems
} }
MXLog.verbose("Finished applying diff") MXLog.info("Finished applying diffs, current items (\(itemProxies.count)) : \(itemProxies.map(\.debugIdentifier))")
} }
// swiftlint:disable:next cyclomatic_complexity function_body_length // swiftlint:disable:next cyclomatic_complexity function_body_length
@@ -100,60 +105,64 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
case .pushFront: case .pushFront:
guard let item = diff.pushFront() else { fatalError() } guard let item = diff.pushFront() else { fatalError() }
MXLog.verbose("Push Front") MXLog.info("Push Front: \(item.debugIdentifier)")
let itemProxy = TimelineItemProxy(item: item) let itemProxy = TimelineItemProxy(item: item)
changes.append(.insert(offset: 0, element: itemProxy, associatedWith: nil)) changes.append(.insert(offset: 0, element: itemProxy, associatedWith: nil))
case .pushBack: case .pushBack:
guard let item = diff.pushBack() else { fatalError() } guard let item = diff.pushBack() else { fatalError() }
MXLog.verbose("Push Back") MXLog.info("Push Back \(item.debugIdentifier)")
let itemProxy = TimelineItemProxy(item: item) let itemProxy = TimelineItemProxy(item: item)
changes.append(.insert(offset: Int(itemProxies.count), element: itemProxy, associatedWith: nil)) changes.append(.insert(offset: Int(itemProxies.count), element: itemProxy, associatedWith: nil))
case .insert: case .insert:
guard let update = diff.insert() else { fatalError() } guard let update = diff.insert() else { fatalError() }
MXLog.verbose("Insert at \(update.index), current total count: \(itemProxies.count)") MXLog.info("Insert \(update.item.debugIdentifier) at \(update.index)")
let itemProxy = TimelineItemProxy(item: update.item) let itemProxy = TimelineItemProxy(item: update.item)
changes.append(.insert(offset: Int(update.index), element: itemProxy, associatedWith: nil)) changes.append(.insert(offset: Int(update.index), element: itemProxy, associatedWith: nil))
case .append: case .append:
guard let items = diff.append() else { fatalError() } guard let items = diff.append() else { fatalError() }
MXLog.verbose("Append new items with count: \(items.count), to current total count: \(itemProxies.count)") MXLog.info("Append \(items.map(\.debugIdentifier))")
for (index, item) in items.enumerated() { for (index, item) in items.enumerated() {
changes.append(.insert(offset: Int(itemProxies.count) + index, element: TimelineItemProxy(item: item), associatedWith: nil)) changes.append(.insert(offset: Int(itemProxies.count) + index, element: TimelineItemProxy(item: item), associatedWith: nil))
} }
case .set: case .set:
guard let update = diff.set() else { fatalError() } guard let update = diff.set() else { fatalError() }
MXLog.verbose("Update \(update.index), current total count: \(itemProxies.count)") MXLog.info("Set \(update.item.debugIdentifier) at index \(update.index)")
let itemProxy = TimelineItemProxy(item: update.item) let itemProxy = TimelineItemProxy(item: update.item)
changes.append(.remove(offset: Int(update.index), element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: Int(update.index), element: itemProxy, associatedWith: nil))
changes.append(.insert(offset: Int(update.index), element: itemProxy, associatedWith: nil)) changes.append(.insert(offset: Int(update.index), element: itemProxy, associatedWith: nil))
case .popFront: case .popFront:
MXLog.verbose("Pop Front, current total count: \(itemProxies.count)")
guard let itemProxy = itemProxies.first else { fatalError() } guard let itemProxy = itemProxies.first else { fatalError() }
MXLog.info("Pop Front \(itemProxy.debugIdentifier)")
changes.append(.remove(offset: 0, element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: 0, element: itemProxy, associatedWith: nil))
case .popBack: case .popBack:
MXLog.verbose("Pop Back, current total count: \(itemProxies.count)")
guard let itemProxy = itemProxies.last else { fatalError() } guard let itemProxy = itemProxies.last else { fatalError() }
MXLog.info("Pop Back \(itemProxy.debugIdentifier)")
changes.append(.remove(offset: itemProxies.count - 1, element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: itemProxies.count - 1, element: itemProxy, associatedWith: nil))
case .remove: case .remove:
guard let index = diff.remove() else { fatalError() } guard let index = diff.remove() else { fatalError() }
MXLog.verbose("Remove item at: \(index), current total count: \(itemProxies.count)")
let itemProxy = itemProxies[Int(index)] let itemProxy = itemProxies[Int(index)]
MXLog.info("Remove \(itemProxy.debugIdentifier) at: \(index)")
changes.append(.remove(offset: Int(index), element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: Int(index), element: itemProxy, associatedWith: nil))
case .clear: case .clear:
MXLog.verbose("Clear all items, current total count: \(itemProxies.count)") MXLog.info("Clear all items")
for (index, itemProxy) in itemProxies.enumerated() { for (index, itemProxy) in itemProxies.enumerated() {
changes.append(.remove(offset: index, element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: index, element: itemProxy, associatedWith: nil))
} }
case .reset: case .reset:
guard let items = diff.reset() else { fatalError() } guard let items = diff.reset() else { fatalError() }
MXLog.verbose("Replace all items with new count: \(items.count), current total count: \(itemProxies.count)") MXLog.info("Replace all items with \(items.map(\.debugIdentifier))")
for (index, itemProxy) in itemProxies.enumerated() { for (index, itemProxy) in itemProxies.enumerated() {
changes.append(.remove(offset: index, element: itemProxy, associatedWith: nil)) changes.append(.remove(offset: index, element: itemProxy, associatedWith: nil))
} }
@@ -166,3 +175,43 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
return CollectionDifference(changes) return CollectionDifference(changes)
} }
} }
private extension TimelineItem {
var debugIdentifier: String {
if let virtualTimelineItem = asVirtual() {
return virtualTimelineItem.debugIdentifier
} else if let eventTimelineItem = asEvent() {
return eventTimelineItem.uniqueIdentifier()
}
return "UnknownTimelineItem"
}
}
private extension TimelineItemProxy {
var debugIdentifier: String {
switch self {
case .event(let eventTimelineItem):
return eventTimelineItem.item.uniqueIdentifier()
case .virtual(let virtualTimelineItem):
return virtualTimelineItem.debugIdentifier
case .unknown:
return "UnknownTimelineItem"
}
}
}
private extension VirtualTimelineItem {
var debugIdentifier: String {
switch self {
case .dayDivider(let timestamp):
return "DayDiviver(\(timestamp))"
case .loadingIndicator:
return "LoadingIndicator"
case .readMarker:
return "ReadMarker"
case .timelineStart:
return "TimelineStart"
}
}
}
@@ -106,7 +106,7 @@ struct EventTimelineItemProxy {
} }
var reactions: [Reaction] { var reactions: [Reaction] {
item.reactions() ?? [] item.reactions()
} }
var timestamp: Date { var timestamp: Date {
-1
View File
@@ -140,7 +140,6 @@ targets:
- package: Kingfisher - package: Kingfisher
- package: KZFileWatchers - package: KZFileWatchers
- package: PostHog - package: PostHog
- package: SwiftyBeaver
- package: SwiftState - package: SwiftState
- package: GZIP - package: GZIP
- package: Sentry - package: Sentry
@@ -36,7 +36,6 @@ targets:
- package: DTCoreText - package: DTCoreText
- package: KeychainAccess - package: KeychainAccess
- package: Kingfisher - package: Kingfisher
- package: SwiftyBeaver
- package: SwiftState - package: SwiftState
- package: GZIP - package: GZIP
- package: Sentry - package: Sentry
+1 -18
View File
@@ -81,24 +81,7 @@ class NSELogger {
} }
isConfigured = true isConfigured = true
let configuration = MXLogConfiguration() MXLog.configure(target: "nse")
configuration.maxLogFilesCount = 10
configuration.subLogName = "nse"
#if DEBUG
setupTracing(configuration: .debug)
configuration.logLevel = .debug
#else
setupTracing(configuration: .release)
configuration.logLevel = .info
#endif
// Avoid redirecting NSLogs to files if we are attached to a debugger.
if isatty(STDERR_FILENO) == 0 {
configuration.redirectLogsToFiles = true
}
MXLog.configure(configuration)
} }
static func logMemory(with tag: String) { static func logMemory(with tag: String) {
-1
View File
@@ -33,7 +33,6 @@ targets:
dependencies: dependencies:
- package: MatrixRustSDK - package: MatrixRustSDK
- package: SwiftyBeaver
- package: KeychainAccess - package: KeychainAccess
- package: Kingfisher - package: Kingfisher
- package: Collections - package: Collections
-1
View File
@@ -36,7 +36,6 @@ targets:
- package: Kingfisher - package: Kingfisher
- package: KZFileWatchers - package: KZFileWatchers
- package: PostHog - package: PostHog
- package: SwiftyBeaver
- package: SwiftState - package: SwiftState
- package: GZIP - package: GZIP
- package: Sentry - package: Sentry
+22 -39
View File
@@ -36,9 +36,8 @@ class LoggingTests: XCTestCase {
let log = UUID().uuidString let log = UUID().uuidString
let configuration = MXLogConfiguration() MXLog.configure(redirectToFiles: true)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration)
MXLog.info(log) MXLog.info(log)
guard let logFile = MXLogger.logFiles.first else { guard let logFile = MXLogger.logFiles.first else {
XCTFail(Constants.genericFailure) XCTFail(Constants.genericFailure)
@@ -56,9 +55,7 @@ class LoggingTests: XCTestCase {
// When launching the app 5 times. // When launching the app 5 times.
let launchCount = 5 let launchCount = 5
for index in 0..<launchCount { for index in 0..<launchCount {
let configuration = MXLogConfiguration() MXLog.configure(redirectToFiles: true)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration) // This call is only made at app launch.
MXLog.info("Launch \(index + 1)") MXLog.info("Launch \(index + 1)")
} }
@@ -77,10 +74,7 @@ class LoggingTests: XCTestCase {
let launchCount = 10 let launchCount = 10
let logFileCount = 5 let logFileCount = 5
for index in 0..<launchCount { for index in 0..<launchCount {
let configuration = MXLogConfiguration() MXLog.configure(redirectToFiles: true, maxLogFileCount: UInt(logFileCount))
configuration.maxLogFilesCount = UInt(logFileCount)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration) // This call is only made at app launch.
MXLog.info("Launch \(index + 1)") MXLog.info("Launch \(index + 1)")
} }
@@ -97,12 +91,9 @@ class LoggingTests: XCTestCase {
// When launching the app 10 times, with a max total log size of 25KB and logging ~5KB data each time. // When launching the app 10 times, with a max total log size of 25KB and logging ~5KB data each time.
let launchCount = 10 let launchCount = 10
let logFileSizeLimit = 25 * 1024 let logFileSizeLimit: UInt = 25 * 1024
for index in 0..<launchCount { for index in 0..<launchCount {
let configuration = MXLogConfiguration() MXLog.configure(redirectToFiles: true, logFileSizeLimit: logFileSizeLimit)
configuration.logFilesSizeLimit = UInt(logFileSizeLimit)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration) // This call is only made at app launch.
MXLog.info("Launch \(index + 1)") MXLog.info("Launch \(index + 1)")
// Add ~5KB of logs // Add ~5KB of logs
@@ -142,11 +133,9 @@ class LoggingTests: XCTestCase {
let log = UUID().uuidString let log = UUID().uuidString
let configuration = MXLogConfiguration() MXLog.configure(redirectToFiles: true)
configuration.logLevel = .error
configuration.redirectLogsToFiles = true MXLog.verbose(log)
MXLog.configure(configuration)
MXLog.info(log)
guard let logFile = MXLogger.logFiles.first else { guard let logFile = MXLogger.logFiles.first else {
XCTFail(Constants.genericFailure) XCTFail(Constants.genericFailure)
return return
@@ -159,19 +148,17 @@ class LoggingTests: XCTestCase {
func testSubLogName() { func testSubLogName() {
XCTAssert(MXLogger.logFiles.isEmpty) XCTAssert(MXLogger.logFiles.isEmpty)
let subLogName = "nse" let target = "nse"
let configuration = MXLogConfiguration() MXLog.configure(target: target, redirectToFiles: true)
configuration.subLogName = subLogName
configuration.redirectLogsToFiles = true
MXLog.configure(configuration)
MXLog.info(UUID().uuidString) MXLog.info(UUID().uuidString)
guard let logFile = MXLogger.logFiles.first else { guard let logFile = MXLogger.logFiles.first else {
XCTFail(Constants.genericFailure) XCTFail(Constants.genericFailure)
return return
} }
XCTAssertTrue(logFile.lastPathComponent.contains(subLogName)) XCTAssertTrue(logFile.lastPathComponent.contains(target))
} }
func testRoomSummaryContentIsRedacted() throws { func testRoomSummaryContentIsRedacted() throws {
@@ -189,10 +176,9 @@ class LoggingTests: XCTestCase {
// When logging that value // When logging that value
XCTAssert(MXLogger.logFiles.isEmpty) XCTAssert(MXLogger.logFiles.isEmpty)
let configuration = MXLogConfiguration()
configuration.logLevel = .info MXLog.configure(redirectToFiles: true)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration)
MXLog.info(roomSummary) MXLog.info(roomSummary)
// Then the log file should not include the sensitive information // Then the log file should not include the sensitive information
@@ -207,7 +193,6 @@ class LoggingTests: XCTestCase {
XCTAssertFalse(content.contains(lastMessage)) XCTAssertFalse(content.contains(lastMessage))
} }
// swiftlint:disable:next function_body_length
func testTimelineContentIsRedacted() throws { func testTimelineContentIsRedacted() throws {
// Given timeline items that contain text // Given timeline items that contain text
let textAttributedString = "TextAttributed" let textAttributedString = "TextAttributed"
@@ -235,10 +220,9 @@ class LoggingTests: XCTestCase {
// When logging that value // When logging that value
XCTAssert(MXLogger.logFiles.isEmpty) XCTAssert(MXLogger.logFiles.isEmpty)
let configuration = MXLogConfiguration()
configuration.logLevel = .info MXLog.configure(redirectToFiles: true)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration)
MXLog.info(textMessage) MXLog.info(textMessage)
MXLog.info(noticeMessage) MXLog.info(noticeMessage)
MXLog.info(emoteMessage) MXLog.info(emoteMessage)
@@ -294,10 +278,9 @@ class LoggingTests: XCTestCase {
// When logging that value // When logging that value
XCTAssert(MXLogger.logFiles.isEmpty) XCTAssert(MXLogger.logFiles.isEmpty)
let configuration = MXLogConfiguration()
configuration.logLevel = .info MXLog.configure(redirectToFiles: true)
configuration.redirectLogsToFiles = true
MXLog.configure(configuration)
MXLog.info(textMessage) MXLog.info(textMessage)
MXLog.info(noticeMessage) MXLog.info(noticeMessage)
MXLog.info(emoteMessage) MXLog.info(emoteMessage)
@@ -95,7 +95,7 @@ private class MockMediaLoadingClient: ClientProtocol {
func uploadMedia(mimeType: String, data content: [UInt8]) throws -> String { fatalError() } func uploadMedia(mimeType: String, data content: [UInt8]) throws -> String { fatalError() }
func getMediaFile(source: MatrixRustSDK.MediaSource, mimeType: String) throws -> MatrixRustSDK.MediaFileHandle { fatalError() } func getMediaFile(mediaSource: MatrixRustSDK.MediaSource, mimeType: String) throws -> MatrixRustSDK.MediaFileHandle { fatalError() }
func getProfile(userId: String) throws -> MatrixRustSDK.UserProfile { fatalError() } func getProfile(userId: String) throws -> MatrixRustSDK.UserProfile { fatalError() }
@@ -129,10 +129,6 @@ private class MockMediaLoadingClient: ClientProtocol {
fatalError() fatalError()
} }
func searchUsers(searchTerm: String, limit: UInt64) throws -> MatrixRustSDK.SearchUsersResults {
fatalError()
}
func unignoreUser(userId: String) throws { func unignoreUser(userId: String) throws {
fatalError() fatalError()
} }
@@ -146,4 +142,6 @@ private class MockMediaLoadingClient: ClientProtocol {
lang: String) throws { lang: String) throws {
fatalError() fatalError()
} }
func searchUsers(searchTerm: String, limit: UInt64) throws -> MatrixRustSDK.SearchUsersResults { fatalError() }
} }
@@ -156,7 +156,7 @@ private struct MockEventTimelineItem: EventTimelineItemProtocol {
func raw() -> String? { nil } func raw() -> String? { nil }
func reactions() -> [MatrixRustSDK.Reaction]? { nil } func reactions() -> [MatrixRustSDK.Reaction] { [] }
func sender() -> String { "@user:server.com" } func sender() -> String { "@user:server.com" }
+1 -4
View File
@@ -42,7 +42,7 @@ include:
packages: packages:
MatrixRustSDK: MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.0.54-alpha exactVersion: 1.0.56-alpha
# path: ../matrix-rust-sdk # path: ../matrix-rust-sdk
DesignKit: DesignKit:
path: DesignKit path: DesignKit
@@ -83,9 +83,6 @@ packages:
PostHog: PostHog:
url: https://github.com/PostHog/posthog-ios url: https://github.com/PostHog/posthog-ios
minorVersion: 2.0.0 minorVersion: 2.0.0
SwiftyBeaver:
url: https://github.com/SwiftyBeaver/SwiftyBeaver
minorVersion: 2.0.0
SwiftState: SwiftState:
url: https://github.com/ReactKit/SwiftState url: https://github.com/ReactKit/SwiftState
minorVersion: 6.0.0 minorVersion: 6.0.0