Integrate logging capabilities (#93)

* Integrate logging capabilities

* Relocate file
This commit is contained in:
Francisco Gindre 2020-03-09 17:25:27 -03:00 committed by GitHub
parent 4db57d1be0
commit 72c3cb59ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 200 additions and 46 deletions

View File

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
0D2343EE238C91B900606F71 /* sapling-output.params in Resources */ = {isa = PBXBuildFile; fileRef = 0D2343EC238C91B900606F71 /* sapling-output.params */; };
0D2343EF238C91B900606F71 /* sapling-spend.params in Resources */ = {isa = PBXBuildFile; fileRef = 0D2343ED238C91B900606F71 /* sapling-spend.params */; };
0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D49A18B241698A800CC0649 /* SampleLogger.swift */; };
0D4EBA312396CFD70041B507 /* SendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4EBA302396CFD70041B507 /* SendViewController.swift */; };
0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; };
0D7A4A83236CCD88001F4DD8 /* SyncBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */; };
@ -72,6 +73,7 @@
/* Begin PBXFileReference section */
0D2343EC238C91B900606F71 /* sapling-output.params */ = {isa = PBXFileReference; lastKnownFileType = file; name = "sapling-output.params"; path = "../../../ZcashLightClientKitTests/sapling-output.params"; sourceTree = "<group>"; };
0D2343ED238C91B900606F71 /* sapling-spend.params */ = {isa = PBXFileReference; lastKnownFileType = file; name = "sapling-spend.params"; path = "../../../ZcashLightClientKitTests/sapling-spend.params"; sourceTree = "<group>"; };
0D49A18B241698A800CC0649 /* SampleLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleLogger.swift; sourceTree = "<group>"; };
0D4EBA302396CFD70041B507 /* SendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendViewController.swift; sourceTree = "<group>"; };
0D756A93236C761E009B041B /* GetAddressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetAddressViewController.swift; sourceTree = "<group>"; };
0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksViewController.swift; sourceTree = "<group>"; };
@ -218,6 +220,7 @@
0D907F212322CC5B00D641FE /* Info.plist */,
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */,
0D7C85E423AD5A9B006878FC /* SampleStorage.swift */,
0D49A18B241698A800CC0649 /* SampleLogger.swift */,
);
path = ZcashLightClientSample;
sourceTree = "<group>";
@ -668,6 +671,7 @@
0DDFB33E236B844900AED892 /* DemoAppConfig.swift in Sources */,
0D907F162322CC5900D641FE /* AppDelegate.swift in Sources */,
0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */,
0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */,
0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */,
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */,
);

View File

@ -9,6 +9,9 @@
import UIKit
import ZcashLightClientKit
import NotificationBubbles
var loggerProxy = SampleLogger(logLevel: .info)
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ -30,8 +33,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if let wallet = wallet {
return wallet
} else {
let wallet = Initializer(cacheDbURL:try! __cacheDbURL() , dataDbURL: try! __dataDbURL(), pendingDbURL: try! __pendingDbURL(), endpoint: DemoAppConfig.endpoint, spendParamsURL: try! __spendParamsURL(), outputParamsURL: try! __outputParamsURL())
let addresses = try! wallet.initialize(seedProvider: DemoAppConfig(), walletBirthdayHeight: BlockHeight(DemoAppConfig.birthdayHeight)) // Init or DIE
let wallet = Initializer(cacheDbURL:try! __cacheDbURL(),
dataDbURL: try! __dataDbURL(),
pendingDbURL: try! __pendingDbURL(),
endpoint: DemoAppConfig.endpoint,
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
loggerProxy: loggerProxy)
let addresses = try! wallet.initialize(seedProvider: DemoAppConfig(),
walletBirthdayHeight: BlockHeight(DemoAppConfig.birthdayHeight)) // Init or DIE
var storage = SampleStorage.shared
storage!.seed = Data(DemoAppConfig().seed())
@ -47,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
@objc func txMinedNotification(_ notification: Notification) {
guard let tx = notification.userInfo?[SDKSynchronizer.NotificationKeys.minedTransaction] as? PendingTransactionEntity else {
print("no tx information on notification")
loggerProxy.error("no tx information on notification")
return
}
NotificationBubble.display(in: window!.rootViewController!.view, options: NotificationBubble.sucessOptions(animation: NotificationBubble.Animation.fade(duration: 1)), attributedText: NSAttributedString(string: "Transaction \(String(describing: tx.id))mined!")) {}
@ -57,7 +67,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
_ = self.sharedWallet
subscribeToMinedTxNotifications()
subscribeToMinedTxNotifications()
return true
}
@ -99,19 +110,19 @@ extension AppDelegate {
do {
try FileManager.default.removeItem(at: try __cacheDbURL())
} catch {
print("error clearing cache DB: \(error)")
loggerProxy.error("error clearing cache DB: \(error)")
}
do {
try FileManager.default.removeItem(at: try __dataDbURL())
} catch {
print("error clearing data db: \(error)")
loggerProxy.error("error clearing data db: \(error)")
}
do {
try FileManager.default.removeItem(at: try __pendingDbURL())
} catch {
print("error clearing data db: \(error)")
loggerProxy.error("error clearing data db: \(error)")
}
var storage = SampleStorage.shared

View File

@ -24,7 +24,8 @@ class GetAddressViewController: UIViewController {
spendingKeyLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(spendingKeyTapped(_:))))
spendingKeyLabel.isUserInteractionEnabled = true
print("Address: \(String(describing: Initializer.shared.getAddress()))")
loggerProxy.info("Address: \(String(describing: Initializer.shared.getAddress()))")
// NOTE: NEVER LOG YOUR PRIVATE KEYS IN REAL LIFE
print("Spending Key: \(SampleStorage.shared.privateKey ?? "No Spending Key found")")
}
@ -45,11 +46,11 @@ class GetAddressViewController: UIViewController {
@IBAction func spendingKeyTapped(_ gesture: UIGestureRecognizer) {
guard let key = SampleStorage.shared.privateKey else {
print("nothing to copy")
loggerProxy.warn("nothing to copy")
return
}
print("copied to clipboard")
loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = key
let alert = UIAlertController(title: "", message: "Spending Key Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
@ -58,7 +59,7 @@ class GetAddressViewController: UIViewController {
}
@IBAction func addressTapped(_ gesture: UIGestureRecognizer) {
print("copied to clipboard")
loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = legibleAddresses()
let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))

View File

@ -0,0 +1,62 @@
//
// SampleLogger.swift
// ZcashLightClientSample
//
// Created by Francisco Gindre on 3/9/20.
// Copyright © 2020 Electric Coin Company. All rights reserved.
//
import Foundation
import ZcashLightClientKit
import os
class SampleLogger: Logger {
enum LogLevel: Int {
case debug
case error
case warning
case event
case info
}
var level: LogLevel
init(logLevel: LogLevel) {
self.level = logLevel
}
private static let subsystem = Bundle.main.bundleIdentifier!
static let oslog = OSLog(subsystem: subsystem, category: "sample-logs")
func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue == LogLevel.debug.rawValue else { return }
log(level: "DEBUG 🐞", message: message, file: file, function: function, line: line)
}
func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.error.rawValue else { return }
log(level: "ERROR 💥", message: message, file: file, function: function, line: line)
}
func warn(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.warning.rawValue else { return }
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
}
func event(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.event.rawValue else { return }
log(level: "EVENT ⏱", message: message, file: file, function: function, line: line)
}
func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
guard level.rawValue <= LogLevel.info.rawValue else { return }
log(level: "INFO ", message: message, file: file, function: function, line: line)
}
private func log(level: String, message: String, file: String, function: String, line: Int) {
let fileName = file as NSString
os_log("[%@] %@ - %@ - Line: %d -> %@", log: Self.oslog, type: .default, level, fileName.lastPathComponent, function, line, message)
}
}

View File

@ -122,7 +122,7 @@ class SendViewController: UIViewController {
@IBAction func send(_ sender: Any) {
guard isFormValid() else {
print("WARNING: Form is invalid")
loggerProxy.warn("WARNING: Form is invalid")
return
}
@ -146,13 +146,13 @@ class SendViewController: UIViewController {
func send() {
guard isFormValid(), let amount = amountTextField.text, let zec = Double(amount)?.toZatoshi(), let recipient = addressTextField.text else {
print("WARNING: Form is invalid")
loggerProxy.warn("WARNING: Form is invalid")
return
}
guard let address = SampleStorage.shared.privateKey else {
print("NO ADDRESS")
loggerProxy.error("NO ADDRESS")
return
}
@ -165,12 +165,12 @@ class SendViewController: UIViewController {
}
switch result {
case .success(let pendingTransaction):
print("transaction created: \(pendingTransaction)")
loggerProxy.info("transaction created: \(pendingTransaction)")
case .failure(let error):
DispatchQueue.main.async {
self?.fail(error)
print("SEND FAILED: \(error)")
loggerProxy.error("SEND FAILED: \(error)")
}
}
}

View File

@ -35,7 +35,7 @@ class TransactionDetailViewController: UITableViewController {
expiryHeightLabel.text = heightToString(height: model.expiryHeight)
createdLabel.text = model.created
print(model.transactionId.toHexStringTxId())
loggerProxy.debug("tx id: \(model.transactionId.toHexStringTxId()))")
}
func formatMemo(_ memo: Data?) -> String {

View File

@ -163,6 +163,29 @@ The `LIGHTWALLETD_ADDRESS` environment variable can also be added to your shell
We advice setting this value as a secret variable on your CD/CI environment when possible
# Integrating with logging tools
There are a lots of good logging tools for iOS. So we'll leave that choice to you. ZcashLightClientKit relies on a simple protocol to bubble up logs to client applications, which is called `Logger` (kudos for the naming originality...)
```
public protocol Logger {
func debug(_ message: String, file: String, function: String, line: Int)
func info(_ message: String, file: String, function: String, line: Int)
func event(_ message: String, file: String, function: String, line: Int)
func warn(_ message: String, file: String, function: String, line: Int)
func error(_ message: String, file: String, function: String, line: Int)
}
```
To enable logging you need to do 2 simple steps:
1. have one class conform the `Logger` protocol
2. inject that logger when creating the `Initializer`
For more details look the Sample App's `AppDelegate` code.
# Swiftlint
We don't like reinveing the wheel, so be gently borrowed swift lint rules from AirBnB which we find pretty cool and reasonable.

View File

@ -32,7 +32,7 @@ class CompactBlockScanningOperation: ZcashOperation {
}
guard self.rustBackend.scanBlocks(dbCache: self.cacheDb, dbData: self.dataDb) else {
self.error = self.rustBackend.lastError() ?? ZcashOperationError.unknown
print("block scanning failed with error: \(String(describing: self.error))")
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.cancel()
return
}

View File

@ -232,7 +232,7 @@ public class CompactBlockProcessor {
}
guard shouldStart else {
print("Warning: compact block processor was started while busy!!!!")
LoggerProxy.debug("Warning: compact block processor was started while busy!!!!")
return
}
@ -331,24 +331,24 @@ public class CompactBlockProcessor {
validateChainOperation.completionHandler = { (finished, cancelled) in
guard !cancelled else {
print("Warning: operation cancelled")
LoggerProxy.debug("Warning: operation cancelled")
return
}
print("validateChainFinished")
LoggerProxy.debug("validateChainFinished")
}
validateChainOperation.errorHandler = { [weak self] (error) in
guard let self = self else { return }
guard let validationError = error as? CompactBlockValidationError else {
print("Warning: validateChain operation returning generic error: \(error)")
LoggerProxy.debug("Warning: validateChain operation returning generic error: \(error)")
return
}
switch validationError {
case .validationFailed(let height):
print("chain validation at height: \(height)")
LoggerProxy.debug("chain validation at height: \(height)")
self.validationFailed(at: height)
}
@ -371,7 +371,7 @@ public class CompactBlockProcessor {
scanBlocksOperation.completionHandler = { [weak self] (finished, cancelled) in
guard !cancelled else {
print("Warning: operation cancelled")
LoggerProxy.debug("Warning: operation cancelled")
return
}
self?.processBatchFinished(range: range)
@ -401,7 +401,7 @@ public class CompactBlockProcessor {
func notifyProgress(completedRange: CompactBlockRange) {
let progress = calculateProgress(start: self.lowerBoundHeight ?? config.walletBirthday, current: completedRange.upperBound, latest: self.latestBlockHeight)
print("\(self) progress: \(progress)")
LoggerProxy.debug("\(self) progress: \(progress)")
NotificationCenter.default.post(name: Notification.Name.blockProcessorUpdated,
object: self,
userInfo: [ CompactBlockProcessorNotificationKey.progress : progress,
@ -511,7 +511,7 @@ public class CompactBlockProcessor {
func fail(_ error: Error) {
// todo specify: failure
print(error)
LoggerProxy.error("\(error)")
queue.cancelAllOperations()
self.retryAttempts = self.retryAttempts + 1
self.processingError = error

View File

@ -40,7 +40,7 @@ class CompactBlockValidationOperation: ZcashOperation {
let error = CompactBlockValidationError.validationFailed(height: BlockHeight(result))
self.error = error
print("block scanning failed with error: \(String(describing: self.error))")
LoggerProxy.debug("block scanning failed with error: \(String(describing: self.error))")
self.cancel()
self.errorHandler?(error)
return

View File

@ -41,7 +41,7 @@ class ZcashOperation: Operation {
}
override func start() {
print("\(self) started")
LoggerProxy.debug("\(self) started")
startedHandler?()
super.start()
}
@ -55,7 +55,7 @@ class ZcashOperation: Operation {
}
func fail() {
print("\(self) started")
LoggerProxy.debug("\(self) started")
self.cancel()
guard let errorHandler = self.errorHandler else {
return

View File

@ -78,7 +78,9 @@ public class Initializer {
- spendParamsURL: location of the spend parameters
- outputParamsURL: location of the output parameters
*/
public init (cacheDbURL: URL, dataDbURL: URL, pendingDbURL: URL, endpoint: LightWalletEndpoint, spendParamsURL: URL, outputParamsURL: URL) {
public init (cacheDbURL: URL, dataDbURL: URL, pendingDbURL: URL, endpoint: LightWalletEndpoint, spendParamsURL: URL, outputParamsURL: URL, loggerProxy: Logger? = nil) {
logger = loggerProxy
self.cacheDbURL = cacheDbURL
self.dataDbURL = dataDbURL
self.endpoint = endpoint

View File

@ -79,7 +79,7 @@ extension LightWalletGRPCService: LightWalletService {
var blocks = [ZcashCompactBlock]()
let call = try compactTxStreamer.getBlockRange(range.blockRange(), completion: { statusCode in
print("finished with statusCode: \(statusCode)")
LoggerProxy.debug("finished with statusCode: \(statusCode)")
})
while let block = try call.receive() {
@ -105,7 +105,7 @@ extension LightWalletGRPCService: LightWalletService {
}
} catch {
// TODO: Handle Error
print(error.localizedDescription)
LoggerProxy.debug(error.localizedDescription)
result(.failure(LightWalletServiceError.generalError))
}
}

View File

@ -35,7 +35,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
guard let insertedTx = try repository.find(by: try repository.create(PendingTransaction(value: zatoshi, toAddress: toAddress, memo: memo, account: accountIndex))) else {
throw TransactionManagerError.couldNotCreateSpend(toAddress: toAddress, account: accountIndex, zatoshi: zatoshi)
}
print("pending transaction \(String(describing: insertedTx.id)) created")
LoggerProxy.debug("pending transaction \(String(describing: insertedTx.id)) created")
return insertedTx
}
@ -80,13 +80,13 @@ class PersistentTransactionManager: OutboundTransactionManager {
}
guard !storedTx.isCancelled else {
print("ignoring cancelled transaction \(storedTx)")
LoggerProxy.debug("ignoring cancelled transaction \(storedTx)")
result(.failure(TransactionManagerError.cancelled(tx: storedTx)))
return
}
guard let raw = storedTx.raw else {
print("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
LoggerProxy.debug("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
result(.failure(TransactionManagerError.internalInconsistency(tx: storedTx)))
return
}

View File

@ -52,7 +52,7 @@ class WalletTransactionEncoder: TransactionEncoder {
throw TransactionEncoderError.notFound(transactionId: txId)
}
print("sentTransaction id: \(txId)")
LoggerProxy.debug("sentTransaction id: \(txId)")
return EncodedTransaction(transactionId: tx.transactionId , raw: tx.raw)
} catch {
throw TransactionEncoderError.notFound(transactionId: txId)

View File

@ -249,14 +249,14 @@ public class SDKSynchronizer: Synchronizer {
guard let userInfo = notification.userInfo,
let progress = userInfo[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewindHeight = userInfo[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
print("error processing reorg notification")
LoggerProxy.debug("error processing reorg notification")
return }
print("handling reorg at: \(progress) with rewind height: \(rewindHeight)")
LoggerProxy.debug("handling reorg at: \(progress) with rewind height: \(rewindHeight)")
do {
try transactionManager.handleReorg(at: rewindHeight)
} catch {
print("error handling reorg: \(error)")
LoggerProxy.debug("error handling reorg: \(error)")
notifyFailure(error)
}
}
@ -364,7 +364,7 @@ public class SDKSynchronizer: Synchronizer {
do {
try stop()
} catch {
print("stop failed with error: \(error)")
LoggerProxy.debug("stop failed with error: \(error)")
}
}
@ -482,7 +482,7 @@ public class SDKSynchronizer: Synchronizer {
try updateMinedTransactions()
try removeConfirmedTransactions()
} catch {
print("error refreshing pending transactions: \(error)")
LoggerProxy.debug("error refreshing pending transactions: \(error)")
}
}

View File

@ -0,0 +1,51 @@
//
// LoggingProxy.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 3/6/20.
//
import Foundation
/**
Represents what's expected from a logging entity
*/
public protocol Logger {
func debug(_ message: String, file: String, function: String, line: Int)
func info(_ message: String, file: String, function: String, line: Int)
func event(_ message: String, file: String, function: String, line: Int)
func warn(_ message: String, file: String, function: String, line: Int)
func error(_ message: String, file: String, function: String, line: Int)
}
var logger: Logger?
class LoggerProxy {
static func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
logger?.debug(message, file: file, function: function, line: line)
}
static func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
logger?.info(message, file: file, function: function, line: line)
}
static func event(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
logger?.event(message, file: file, function: function, line: line)
}
static func warn(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
logger?.warn(message, file: file, function: function, line: line)
}
static func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
logger?.error(message, file: file, function: function, line: line)
}
}

View File

@ -51,7 +51,7 @@ class ZcashLightClientKitTests: XCTestCase {
//
// let expect = XCTestExpectation(description: self.debugDescription)
// let _ = try? service.getAllBlocksSinceSaplingLaunch(){ result in
// print(result)
// LoggerProxy.debug(result)
// expect.fulfill()
// XCTAssert(result.success)
// XCTAssertNotNil(result.resultData)

View File

@ -37,7 +37,7 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
fileprivate func fakeSave(blocks: [ZcashCompactBlock]) {
blocks.forEach {
print("saving block \($0)")
LoggerProxy.debug("saving block \($0)")
self.latestBlockHeight = $0.height
}
}
@ -57,7 +57,7 @@ class ZcashConsoleFakeStorage: CompactBlockRepository {
}
private func fakeRewind(to height: BlockHeight) {
print("rewind to \(height)")
LoggerProxy.debug("rewind to \(height)")
self.latestBlockHeight = min(self.latestBlockHeight, height)
}

View File

@ -111,7 +111,7 @@ struct InMemoryDbProvider: ConnectionProvider {
struct StubBlockCreator {
static func createRandomDataBlock(with height: BlockHeight) -> ZcashCompactBlock? {
guard let data = randomData(ofLength: 100) else {
print("error creating stub block")
LoggerProxy.debug("error creating stub block")
return nil
}
return ZcashCompactBlock(height: height, data: data)
@ -135,7 +135,7 @@ struct StubBlockCreator {
if status == errSecSuccess {
return Data(bytes: &bytes, count: bytes.count)
}
print("Status \(status)")
LoggerProxy.debug("Status \(status)")
return nil
}