Skip to content
Snippets Groups Projects
Commit 6bb4946a authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Merge branch 'feature/messenger-example-logging' into 'development'

XXMessengerClient - logging

See merge request elixxir/elixxir-dapps-sdk-swift!116
parents 2ec03685 e7bc04ea
No related branches found
No related tags found
2 merge requests!116XXMessengerClient - logging,!102Release 1.0.0
Showing
with 310 additions and 11 deletions
......@@ -56,6 +56,10 @@ let package = Package(
url: "https://github.com/pointfreeco/swift-custom-dump.git",
.upToNextMajor(from: "0.5.2")
),
.package(
url: "https://github.com/apple/swift-log.git",
.upToNextMajor(from: "1.4.4")
),
.package(
url: "https://github.com/kean/Pulse.git",
.upToNextMajor(from: "2.1.2")
......@@ -65,7 +69,7 @@ let package = Package(
.target(
name: "AppCore",
dependencies: [
.product(name: "Pulse", package: "Pulse"),
.product(name: "Logging", package: "swift-log"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXDatabase", package: "client-ios-db"),
......@@ -104,6 +108,8 @@ let package = Package(
.target(name: "WelcomeFeature"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ComposablePresentation", package: "swift-composable-presentation"),
.product(name: "Logging", package: "swift-log"),
.product(name: "PulseLogHandler", package: "Pulse"),
.product(name: "PulseUI", package: "Pulse"),
.product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXModels", package: "client-ios-db"),
......
import Foundation
import Pulse
import Logging
import XCTestDynamicOverlay
public struct Logger {
......@@ -21,14 +21,12 @@ public struct Logger {
extension Logger {
public static func live() -> Logger {
Logger { msg, file, function, line in
let logger = Logging.Logger(label: "xx.network.XXMessengerExample")
return Logger { msg, file, function, line in
switch msg {
case .error(let error):
LoggerStore.shared.storeMessage(
label: "xx-messenger",
level: .error,
message: error.localizedDescription,
metadata: [:],
logger.error(
.init(stringLiteral: error.localizedDescription),
file: file,
function: function,
line: line
......
import ComposableArchitecture
import Logging
import PulseLogHandler
import SwiftUI
@main
struct App: SwiftUI.App {
init() {
LoggingSystem.bootstrap(PersistentLogHandler.init)
}
var body: some Scene {
WindowGroup {
AppView(store: Store(
......
......@@ -34,6 +34,11 @@ extension AppEnvironment {
let mainQueue = DispatchQueue.main.eraseToAnyScheduler()
let bgQueue = DispatchQueue.global(qos: .background).eraseToAnyScheduler()
defer {
_ = try! messenger.setLogLevel(.debug)
messenger.startLogging()
}
let contactEnvironment = ContactEnvironment(
messenger: messenger,
db: dbManager.getDB,
......
......@@ -18,6 +18,15 @@
"version" : "0.5.2"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-log.git",
"state" : {
"revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c",
"version" : "1.4.4"
}
},
{
"identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl",
......
......@@ -31,6 +31,10 @@ let package = Package(
url: "https://github.com/kishikawakatsumi/KeychainAccess.git",
.upToNextMajor(from: "4.2.2")
),
.package(
url: "https://github.com/apple/swift-log.git",
.upToNextMajor(from: "1.4.4")
),
],
targets: [
.target(
......@@ -55,6 +59,7 @@ let package = Package(
dependencies: [
.target(name: "XXClient"),
.product(name: "KeychainAccess", package: "KeychainAccess"),
.product(name: "Logging", package: "swift-log"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
],
swiftSettings: swiftSettings
......
import XCTestDynamicOverlay
import XXClient
public struct MessengerSetLogLevel {
public var run: (LogLevel) throws -> Bool
public func callAsFunction(_ logLevel: LogLevel) throws -> Bool {
try run(logLevel)
}
}
extension MessengerSetLogLevel {
public static func live(_ env: MessengerEnvironment) -> MessengerSetLogLevel {
MessengerSetLogLevel(run: env.setLogLevel.run)
}
}
extension MessengerSetLogLevel {
public static let unimplemented = MessengerSetLogLevel(
run: XCTUnimplemented("\(Self.self)")
)
}
import Foundation
import Logging
import XCTestDynamicOverlay
import XXClient
public struct MessengerStartLogging {
public var run: () -> Void
public func callAsFunction() -> Void {
run()
}
}
extension MessengerStartLogging {
public static func live(_ env: MessengerEnvironment) -> MessengerStartLogging {
return MessengerStartLogging {
env.registerLogWriter(.init { messageString in
let message = LogMessage.parse(messageString)
env.log(message)
})
}
}
}
extension MessengerStartLogging {
public static let unimplemented = MessengerStartLogging(
run: XCTUnimplemented("\(Self.self)")
)
}
......@@ -38,6 +38,8 @@ public struct Messenger {
public var resumeBackup: MessengerResumeBackup
public var backupParams: MessengerBackupParams
public var stopBackup: MessengerStopBackup
public var setLogLevel: MessengerSetLogLevel
public var startLogging: MessengerStartLogging
}
extension Messenger {
......@@ -79,7 +81,9 @@ extension Messenger {
startBackup: .live(env),
resumeBackup: .live(env),
backupParams: .live(env),
stopBackup: .live(env)
stopBackup: .live(env),
setLogLevel: .live(env),
startLogging: .live(env)
)
}
}
......@@ -122,6 +126,8 @@ extension Messenger {
startBackup: .unimplemented,
resumeBackup: .unimplemented,
backupParams: .unimplemented,
stopBackup: .unimplemented
stopBackup: .unimplemented,
setLogLevel: .unimplemented,
startLogging: .unimplemented
)
}
import Foundation
import Logging
import XXClient
import XCTestDynamicOverlay
......@@ -18,6 +19,7 @@ public struct MessengerEnvironment {
public var isListeningForMessages: Stored<Bool>
public var isRegisteredWithUD: IsRegisteredWithUD
public var loadCMix: LoadCMix
public var log: (LogMessage) -> Void
public var login: Login
public var lookupUD: LookupUD
public var messageListeners: ListenersRegistry
......@@ -29,8 +31,10 @@ public struct MessengerEnvironment {
public var newUdManagerFromBackup: NewUdManagerFromBackup
public var passwordStorage: PasswordStorage
public var registerForNotifications: RegisterForNotifications
public var registerLogWriter: RegisterLogWriter
public var resumeBackup: ResumeBackup
public var searchUD: SearchUD
public var setLogLevel: SetLogLevel
public var sleep: (TimeInterval) -> Void
public var storageDir: String
public var ud: Stored<UserDiscovery?>
......@@ -47,7 +51,9 @@ extension MessengerEnvironment {
.path
public static func live() -> MessengerEnvironment {
MessengerEnvironment(
let logger = Logger(label: "xx.network.client")
return MessengerEnvironment(
authCallbacks: .live(),
backup: .inMemory(),
backupCallbacks: .live(),
......@@ -63,6 +69,7 @@ extension MessengerEnvironment {
isListeningForMessages: .inMemory(false),
isRegisteredWithUD: .live,
loadCMix: .live,
log: { logger.log(level: $0.level, .init(stringLiteral: $0.text)) },
login: .live,
lookupUD: .live,
messageListeners: .live(),
......@@ -74,8 +81,10 @@ extension MessengerEnvironment {
newUdManagerFromBackup: .live,
passwordStorage: .keychain,
registerForNotifications: .live,
registerLogWriter: .live,
resumeBackup: .live,
searchUD: .live,
setLogLevel: .live,
sleep: { Thread.sleep(forTimeInterval: $0) },
storageDir: MessengerEnvironment.defaultStorageDir,
ud: .inMemory(),
......@@ -103,6 +112,7 @@ extension MessengerEnvironment {
isListeningForMessages: .unimplemented(placeholder: false),
isRegisteredWithUD: .unimplemented,
loadCMix: .unimplemented,
log: XCTUnimplemented("\(Self.self).log"),
login: .unimplemented,
lookupUD: .unimplemented,
messageListeners: .unimplemented,
......@@ -114,8 +124,10 @@ extension MessengerEnvironment {
newUdManagerFromBackup: .unimplemented,
passwordStorage: .unimplemented,
registerForNotifications: .unimplemented,
registerLogWriter: .unimplemented,
resumeBackup: .unimplemented,
searchUD: .unimplemented,
setLogLevel: .unimplemented,
sleep: XCTUnimplemented("\(Self.self).sleep"),
storageDir: "unimplemented",
ud: .unimplemented(),
......
import Foundation
import Logging
public struct LogMessage: Equatable {
public init(level: Logger.Level, text: String) {
self.level = level
self.text = text
}
public var level: Logger.Level
public var text: String
}
extension LogMessage {
public static func parse(_ string: String) -> LogMessage {
let level: Logger.Level
let text: String
let pattern = #"^([A-Z]+)( \d{4}/\d{2}/\d{2})?( \d{1,2}:\d{2}:\d{2}\.\d+)? (.*)"#
let regex = try! NSRegularExpression(
pattern: pattern,
options: .dotMatchesLineSeparators
)
let stringRange = NSRange(location: 0, length: string.utf16.count)
if let match = regex.firstMatch(in: string, range: stringRange) {
var groups: [Int: String] = [:]
for rangeIndex in 1..<match.numberOfRanges {
let nsRange = match.range(at: rangeIndex)
if !NSEqualRanges(nsRange, NSMakeRange(NSNotFound, 0)) {
let group = (string as NSString).substring(with: nsRange)
groups[rangeIndex] = group
}
}
level = .fromString(groups[1])
text = groups[4] ?? string
} else {
level = .notice
text = string
}
return LogMessage(level: level, text: text)
}
}
private extension Logger.Level {
static func fromString(_ string: String?) -> Logger.Level {
switch string {
case "TRACE": return .trace
case "DEBUG": return .debug
case "INFO": return .info
case "WARN": return .warning
case "ERROR": return .error
case "CRITICAL": return .critical
case "FATAL": return .critical
default: return .notice
}
}
}
import CustomDump
import XCTest
import XXClient
@testable import XXMessengerClient
final class MessengerSetLogLevelTests: XCTestCase {
func testSetLogLevel() throws {
var didSetLogLevel: [LogLevel] = []
var env: MessengerEnvironment = .unimplemented
env.setLogLevel.run = { level in
didSetLogLevel.append(level)
return true
}
let setLogLevel: MessengerSetLogLevel = .live(env)
let result = try setLogLevel(.debug)
XCTAssertNoDifference(didSetLogLevel, [.debug])
XCTAssertNoDifference(result, true)
}
func testSetLogLevelReturnsFalse() throws {
var env: MessengerEnvironment = .unimplemented
env.setLogLevel.run = { _ in return false }
let setLogLevel: MessengerSetLogLevel = .live(env)
let result = try setLogLevel(.debug)
XCTAssertNoDifference(result, false)
}
func testSetLogLevelFailure() {
struct Failure: Error, Equatable {}
let failure = Failure()
var env: MessengerEnvironment = .unimplemented
env.setLogLevel.run = { _ in throw failure }
let setLogLevel: MessengerSetLogLevel = .live(env)
XCTAssertThrowsError(try setLogLevel(.debug)) { error in
XCTAssertNoDifference(error as NSError, failure as NSError)
}
}
}
import CustomDump
import XCTest
import XXClient
@testable import XXMessengerClient
final class MessengerStartLoggingTests: XCTestCase {
func testStartLogging() {
var registeredLogWriters: [LogWriter] = []
var logMessages: [LogMessage] = []
var env: MessengerEnvironment = .unimplemented
env.registerLogWriter.run = { writer in
registeredLogWriters.append(writer)
}
env.log = { message in
logMessages.append(message)
}
let start: MessengerStartLogging = .live(env)
start()
XCTAssertNoDifference(registeredLogWriters.count, 1)
registeredLogWriters.first?.handle("DEBUG Hello, World!")
XCTAssertNoDifference(logMessages, [
.init(level: .debug, text: "Hello, World!"),
])
}
}
import CustomDump
import XCTest
@testable import XXMessengerClient
final class LogMessageTests: XCTestCase {
func testParsing() {
XCTAssertNoDifference(
LogMessage.parse("TRACE Tracing..."),
LogMessage(level: .trace, text: "Tracing...")
)
XCTAssertNoDifference(
LogMessage.parse("DEBUG Debugging..."),
LogMessage(level: .debug, text: "Debugging...")
)
XCTAssertNoDifference(
LogMessage.parse("INFO Informing..."),
LogMessage(level: .info, text: "Informing...")
)
XCTAssertNoDifference(
LogMessage.parse("WARN Warning!"),
LogMessage(level: .warning, text: "Warning!")
)
XCTAssertNoDifference(
LogMessage.parse("ERROR Failure!"),
LogMessage(level: .error, text: "Failure!")
)
XCTAssertNoDifference(
LogMessage.parse("CRITICAL Critical failure!"),
LogMessage(level: .critical, text: "Critical failure!")
)
XCTAssertNoDifference(
LogMessage.parse("FATAL Fatal failure!"),
LogMessage(level: .critical, text: "Fatal failure!")
)
}
func testParsingFallbacks() {
XCTAssertNoDifference(
LogMessage.parse("1234 Wrongly formatted"),
LogMessage(level: .notice, text: "1234 Wrongly formatted")
)
}
func testParsingStripsDateTime() {
XCTAssertNoDifference(
LogMessage.parse("INFO 2022/10/04 Informing..."),
LogMessage(level: .info, text: "Informing...")
)
XCTAssertNoDifference(
LogMessage.parse("INFO 23:36:55.755390 Informing..."),
LogMessage(level: .info, text: "Informing...")
)
XCTAssertNoDifference(
LogMessage.parse("INFO 2022/10/04 23:36:55.755390 Informing..."),
LogMessage(level: .info, text: "Informing...")
)
}
func testParsingMultilineMessage() {
XCTAssertNoDifference(
LogMessage.parse("""
ERROR 2022/10/04 23:51:15.021658 First line
Second line
"""),
LogMessage(level: .error, text: """
First line
Second line
""")
)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment