From 30bb72d324e2b3c803b68bdd67a69ee9576b6ca3 Mon Sep 17 00:00:00 2001 From: Matthew Lorentz Date: Tue, 7 Mar 2023 11:25:43 -0500 Subject: [PATCH] Copied latest version from Planetary --- .gitignore | 124 +----------------- .../xcshareddata/xcschemes/Logger.xcscheme | 22 +--- Package.swift | 9 +- README.md | 22 +++- Sources/Delivery/Log.swift | 112 ++++++++++++++++ Sources/Delivery/Models/LogProtocol.swift | 40 ++++++ Sources/Delivery/Models/Reason.swift | 15 +++ .../Domain/Services/FileLoggerService.swift | 5 +- .../Domain/Services/LoggerService.swift | 4 +- .../Services/LoggerServiceAdapter.swift | 9 +- .../Services/CocoaLumberjackService.swift | 13 +- Sources/Logger/Delivery/Logger.swift | 52 -------- Tests/Delivery/LoggerTests.swift | 99 ++++++++++++++ .../Domain/LoggerServiceAdapterTests.swift | 1 - .../CocoaLumberjackServiceTests.swift | 2 +- Tests/LoggerTests/Delivery/LoggerTests.swift | 51 ------- .../Mocks/FileLoggerServiceMock.swift | 3 +- .../Mocks/LoggerServiceMock.swift | 3 +- 18 files changed, 321 insertions(+), 265 deletions(-) create mode 100644 Sources/Delivery/Log.swift create mode 100644 Sources/Delivery/Models/LogProtocol.swift create mode 100644 Sources/Delivery/Models/Reason.swift rename Sources/{Logger => }/Domain/Services/FileLoggerService.swift (83%) rename Sources/{Logger => }/Domain/Services/LoggerService.swift (86%) rename Sources/{Logger => }/Domain/Services/LoggerServiceAdapter.swift (80%) rename Sources/{Logger/Infraestructure => Infrastructure}/Services/CocoaLumberjackService.swift (67%) delete mode 100644 Sources/Logger/Delivery/Logger.swift create mode 100644 Tests/Delivery/LoggerTests.swift rename Tests/{LoggerTests => }/Domain/LoggerServiceAdapterTests.swift (99%) rename Tests/{LoggerTests/Infraestructure => Infrastructure}/CocoaLumberjackServiceTests.swift (95%) delete mode 100644 Tests/LoggerTests/Delivery/LoggerTests.swift rename Tests/{LoggerTests => }/Mocks/FileLoggerServiceMock.swift (97%) rename Tests/{LoggerTests => }/Mocks/LoggerServiceMock.swift (97%) diff --git a/.gitignore b/.gitignore index 2e32ae7..bb460e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,121 +1,7 @@ -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings +.DS_Store +/.build +/Packages +/*.xcodeproj xcuserdata/ - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -*.xcscmblueprint -*.xccheckout - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) -build/ DerivedData/ -*.moved-aside -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 - -## Obj-C/Swift specific -*.hmap - -## App packaging -*.ipa -*.dSYM.zip -*.dSYM - -## Playgrounds -timeline.xctimeline -playground.xcworkspace - -# Swift Package Manager -# -# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. -# Packages/ -# Package.pins -# Package.resolved -# *.xcodeproj -# -# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata -# hence it is not needed unless you have added a package configuration file to your project -# .swiftpm - -.build/ - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# Pods/ -# -# Add this line if you want to avoid checking in source code from the Xcode workspace -# *.xcworkspace - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - -Carthage/Build/ - -# Accio dependency management -Dependencies/ -.accio/ - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. -# Instead, use fastlane to re-generate the screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/#source-control - -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots/**/*.png -fastlane/test_output - -# Code Injection -# -# After new code Injection tools there's a generated folder /iOSInjectionProject -# https://github.com/johnno1962/injectionforxcode - -iOSInjectionProject/ - -# MacOS -# -# Taken from https://github.com/github/gitignore/blob/master/Global/macOS.gitignore - -## General -.DS_Store -.AppleDouble -.LSOverride - -## Icon must end with two \r -Icon - -## Thumbnails -._* - -## Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -## Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Logger.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Logger.xcscheme index 4c4d334..70e1a32 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Logger.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Logger.xcscheme @@ -1,6 +1,6 @@ - - - - + codeCoverageEnabled = "YES"> + skipped = "NO" + parallelizable = "YES" + testExecutionOrdering = "random"> Bool { + service.optional(error, detail) + } + + public func info(_ string: String) { + service.info(string) + } + + public func debug(_ string: String) { + service.debug(string) + } + + public func error(_ string: String) { + service.unexpected(string, nil) + } + + public func unexpected(_ reason: Reason, _ detail: String?) { + service.unexpected(reason.rawValue, detail) + } + + public func fatal(_ reason: Reason, _ detail: String?) { + service.fatal(reason.rawValue, detail) + } +} + +public extension Log { + + /// URLs in the device's filesystem that contain what was logged in this and previous sessions + static var fileUrls: [URL] { + shared.fileUrls + } + + /// Log a ERROR message + /// - Returns: True if an error could be unwrapped + /// + /// Convenience function that unwraps the error (if exists) and logs its description + @discardableResult + static func optional(_ error: Error?, _ detail: String? = nil) -> Bool { + shared.optional(error, detail) + } + + /// Log a INFO message + static func info(_ string: String) { + shared.info(string) + } + + /// Log a DEBUG message + static func debug(_ string: String) { + shared.debug(string) + } + + /// Log a ERROR message + /// + /// Convencience function that categorize common errors that the app can handle + static func unexpected(_ reason: Reason, _ detail: String?) { + shared.unexpected(reason, detail) + } + + /// Log a FATAL message + /// + /// Convencience function that categorize common errors that the app cannot handle + static func fatal(_ reason: Reason, _ detail: String?) { + shared.fatal(reason, detail) + } + + /// Log a ERROR message + /// + /// Convenience function that unwraps an error and a response from a network call + static func optional(_ error: Error?, from response: URLResponse?) { + guard let error = error else { return } + guard let response = response else { return } + let path = response.url?.path ?? "unknown path" + let detail = "\(path) \(error)" + shared.unexpected(.apiError, detail) + } + + /// Log a ERROR message + static func error(_ message: String) { + shared.error(message) + } +} diff --git a/Sources/Delivery/Models/LogProtocol.swift b/Sources/Delivery/Models/LogProtocol.swift new file mode 100644 index 0000000..3a68ebe --- /dev/null +++ b/Sources/Delivery/Models/LogProtocol.swift @@ -0,0 +1,40 @@ +// +// LogProtocol.swift +// +// +// Created by Martin Dutra on 26/4/22. +// + +import Foundation + +public protocol LogProtocol { + + /// URLs in the device's filesystem that contain what was logged in this and previous sessions + var fileUrls: [URL] { get } + + /// Log a ERROR message + /// - Returns: True if an error could be unwrapped + /// + /// Convenience function that unwraps the error (if exists) and logs its description + @discardableResult + func optional(_ error: Error?, _ detail: String?) -> Bool + + /// Log a INFO message + func info(_ string: String) + + /// Log a DEBUG message + func debug(_ string: String) + + /// Log a ERROR message + func error(_ string: String) + + /// Log a ERROR message + /// + /// Convencience function that categorize common errors that the app can handle + func unexpected(_ reason: Reason, _ detail: String?) + + /// Log a FATAL message + /// + /// Convencience function that categorize common errors that the app cannot handle + func fatal(_ reason: Reason, _ detail: String?) +} diff --git a/Sources/Delivery/Models/Reason.swift b/Sources/Delivery/Models/Reason.swift new file mode 100644 index 0000000..2d44abc --- /dev/null +++ b/Sources/Delivery/Models/Reason.swift @@ -0,0 +1,15 @@ +// +// Reason.swift +// +// +// Created by Martin Dutra on 26/4/22. +// + +import Foundation + +public enum Reason: String { + case apiError + case botError + case missingValue + case incorrectValue +} diff --git a/Sources/Logger/Domain/Services/FileLoggerService.swift b/Sources/Domain/Services/FileLoggerService.swift similarity index 83% rename from Sources/Logger/Domain/Services/FileLoggerService.swift rename to Sources/Domain/Services/FileLoggerService.swift index 3557d3e..ea35e31 100644 --- a/Sources/Logger/Domain/Services/FileLoggerService.swift +++ b/Sources/Domain/Services/FileLoggerService.swift @@ -2,7 +2,7 @@ // FileLoggerService.swift // // -// Created by Martin Dutra on 1/12/21. +// Created by Martin Dutra on 10/2/22. // import Foundation @@ -12,9 +12,8 @@ protocol FileLoggerService { var fileUrls: [URL] { get } func debug(_ string: String) - + func info(_ string: String) func error(_ string: String) - } diff --git a/Sources/Logger/Domain/Services/LoggerService.swift b/Sources/Domain/Services/LoggerService.swift similarity index 86% rename from Sources/Logger/Domain/Services/LoggerService.swift rename to Sources/Domain/Services/LoggerService.swift index b2edb15..1415951 100644 --- a/Sources/Logger/Domain/Services/LoggerService.swift +++ b/Sources/Domain/Services/LoggerService.swift @@ -2,11 +2,10 @@ // LoggerService.swift // // -// Created by Martin Dutra on 22/11/21. +// Created by Martin Dutra on 10/2/22. // import Foundation -import os.log protocol LoggerService { @@ -21,5 +20,4 @@ protocol LoggerService { func unexpected(_ reason: String, _ detail: String?) func fatal(_ reason: String, _ detail: String?) - } diff --git a/Sources/Logger/Domain/Services/LoggerServiceAdapter.swift b/Sources/Domain/Services/LoggerServiceAdapter.swift similarity index 80% rename from Sources/Logger/Domain/Services/LoggerServiceAdapter.swift rename to Sources/Domain/Services/LoggerServiceAdapter.swift index 88a6434..35cc114 100644 --- a/Sources/Logger/Domain/Services/LoggerServiceAdapter.swift +++ b/Sources/Domain/Services/LoggerServiceAdapter.swift @@ -2,12 +2,16 @@ // LoggerServiceAdapter.swift // // -// Created by Martin Dutra on 1/12/21. +// Created by Martin Dutra on 10/2/22. // import Foundation import os.log +/// The LoggerServiceAdapter class can be used to output logs to files in the device's filesystem +/// and to the Console (in real-time) when debugging +/// +/// It implements LoggerService so it is meant to be used by Log as a plug-in that actually outputs to logs somewhere. class LoggerServiceAdapter: LoggerService { var fileLoggerService: FileLoggerService @@ -17,7 +21,7 @@ class LoggerServiceAdapter: LoggerService { } var fileUrls: [URL] { - return fileLoggerService.fileUrls + fileLoggerService.fileUrls } func debug(_ string: String) { @@ -53,5 +57,4 @@ class LoggerServiceAdapter: LoggerService { os_log("%@", type: OSLogType.fault, message) fileLoggerService.error(message) } - } diff --git a/Sources/Logger/Infraestructure/Services/CocoaLumberjackService.swift b/Sources/Infrastructure/Services/CocoaLumberjackService.swift similarity index 67% rename from Sources/Logger/Infraestructure/Services/CocoaLumberjackService.swift rename to Sources/Infrastructure/Services/CocoaLumberjackService.swift index 2814e35..670e696 100644 --- a/Sources/Logger/Infraestructure/Services/CocoaLumberjackService.swift +++ b/Sources/Infrastructure/Services/CocoaLumberjackService.swift @@ -2,7 +2,7 @@ // CocoaLumberjackService.swift // // -// Created by Martin Dutra on 22/11/21. +// Created by Martin Dutra on 10/2/22. // import Foundation @@ -13,7 +13,7 @@ class CocoaLumberjackService: FileLoggerService { private var fileLogger: DDFileLogger var fileUrls: [URL] { - return fileLogger.logFileManager.sortedLogFilePaths.map { URL(fileURLWithPath: $0) } + fileLogger.logFileManager.sortedLogFilePaths.map { URL(fileURLWithPath: $0) } } init() { @@ -24,15 +24,14 @@ class CocoaLumberjackService: FileLoggerService { } func debug(_ string: String) { - DDLogDebug(string) + DDLogDebug(string, asynchronous: false) } func info(_ string: String) { - DDLogInfo(string) + DDLogInfo(string, asynchronous: false) } - + func error(_ string: String) { - DDLogError(string) + DDLogError(string, asynchronous: false) } - } diff --git a/Sources/Logger/Delivery/Logger.swift b/Sources/Logger/Delivery/Logger.swift deleted file mode 100644 index feb303b..0000000 --- a/Sources/Logger/Delivery/Logger.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Logger.swift -// -// -// Created by Martin Dutra on 22/11/21. -// - -import Foundation - -public class Logger { - - public enum Reason: String { - case apiError - case botError - case missingValue - case incorrectValue - } - - public static let shared = Logger() - - var service: LoggerService - - init(service: LoggerService = LoggerServiceAdapter(fileLoggerService: CocoaLumberjackService())) { - self.service = service - } - - public var fileUrls: [URL] { - return service.fileUrls - } - - @discardableResult - public func optional(_ error: Error?, _ detail: String? = nil) -> Bool { - service.optional(error, detail) - } - - public func info(_ string: String) { - service.info(string) - } - - public func debug(_ string: String) { - service.debug(string) - } - - public func unexpected(_ reason: Reason, _ detail: String?) { - service.unexpected(reason.rawValue, detail) - } - - public func fatal(_ reason: Reason, _ detail: String?) { - service.fatal(reason.rawValue, detail) - } - -} diff --git a/Tests/Delivery/LoggerTests.swift b/Tests/Delivery/LoggerTests.swift new file mode 100644 index 0000000..3b6c268 --- /dev/null +++ b/Tests/Delivery/LoggerTests.swift @@ -0,0 +1,99 @@ +// +// LoggerTests.swift +// +// +// Created by Martin Dutra on 22/11/21. +// + +import XCTest +@testable import Logger + +final class LoggerTests: XCTestCase { + + private var service: LoggerServiceMock! + private var logger: Log! + + override func setUp() { + service = LoggerServiceMock() + logger = Log(service: service) + Log.shared.service = service + } + + func testOptional() { + let error = NSError(domain: "domain", code: 1, userInfo: nil) + _ = logger.optional(error, "test") + XCTAssert(service.invokedOptional) + } + + func testDebug() { + logger.debug("test") + XCTAssert(service.invokedDebug) + } + + func testInfo() { + logger.info("test") + XCTAssert(service.invokedInfo) + } + + func testFatal() { + logger.fatal(.apiError, "test") + XCTAssert(service.invokedFatal) + } + + func testUnexpected() { + logger.unexpected(.incorrectValue, "test") + XCTAssert(service.invokedUnexpected) + } + + func testFileUrls() { + XCTAssertEqual(logger.fileUrls, service.fileUrls) + } + + // MARK: Static functions + + func testStaticOptional() { + let error = NSError(domain: "domain", code: 1, userInfo: nil) + _ = Log.optional(error, "test") + XCTAssert(service.invokedOptional) + } + + func testStaticOptionalFromResponse() throws { + let error = NSError(domain: "domain", code: 1, userInfo: nil) + let url = try XCTUnwrap(URL(string: "planetary.social")) + let response = URLResponse(url: url, + mimeType: nil, + expectedContentLength: 2, + textEncodingName: nil) + Log.optional(error, from: response) + XCTAssert(service.invokedUnexpected) + } + + func testStaticDebug() { + Log.debug("test") + XCTAssert(service.invokedDebug) + } + + func testStaticInfo() { + Log.info("test") + XCTAssert(service.invokedInfo) + } + + func testStaticFatal() { + Log.fatal(.apiError, "test") + XCTAssert(service.invokedFatal) + } + + func testStaticUnexpected() { + Log.unexpected(.incorrectValue, "test") + XCTAssert(service.invokedUnexpected) + } + + func testStaticError() { + Log.error("test") + XCTAssert(service.invokedUnexpected) + } + + func testStaticFileUrls() { + XCTAssertEqual(Log.fileUrls, service.fileUrls) + } +} diff --git a/Tests/LoggerTests/Domain/LoggerServiceAdapterTests.swift b/Tests/Domain/LoggerServiceAdapterTests.swift similarity index 99% rename from Tests/LoggerTests/Domain/LoggerServiceAdapterTests.swift rename to Tests/Domain/LoggerServiceAdapterTests.swift index eb9f7b9..14dfec1 100644 --- a/Tests/LoggerTests/Domain/LoggerServiceAdapterTests.swift +++ b/Tests/Domain/LoggerServiceAdapterTests.swift @@ -55,5 +55,4 @@ final class LoggerServiceAdapterTests: XCTestCase { func testFileUrls() { XCTAssertEqual(fileLoggerService.fileUrls, loggerService.fileUrls) } - } diff --git a/Tests/LoggerTests/Infraestructure/CocoaLumberjackServiceTests.swift b/Tests/Infrastructure/CocoaLumberjackServiceTests.swift similarity index 95% rename from Tests/LoggerTests/Infraestructure/CocoaLumberjackServiceTests.swift rename to Tests/Infrastructure/CocoaLumberjackServiceTests.swift index e8bc81c..7974705 100644 --- a/Tests/LoggerTests/Infraestructure/CocoaLumberjackServiceTests.swift +++ b/Tests/Infrastructure/CocoaLumberjackServiceTests.swift @@ -37,7 +37,7 @@ final class CocoaLumberjackServiceTests: XCTestCase { } func readLastLine() -> String? { - guard let url = service.fileUrls.last else { + guard let url = service.fileUrls.first else { return nil } do { diff --git a/Tests/LoggerTests/Delivery/LoggerTests.swift b/Tests/LoggerTests/Delivery/LoggerTests.swift deleted file mode 100644 index 8193432..0000000 --- a/Tests/LoggerTests/Delivery/LoggerTests.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// LoggerTests.swift -// -// -// Created by Martin Dutra on 22/11/21. -// - -import XCTest -@testable import Logger - -final class LoggerTests: XCTestCase { - - private var service: LoggerServiceMock! - private var logger: Logger! - - override func setUp() { - service = LoggerServiceMock() - logger = Logger(service: service) - } - - func testOptional() { - let error = NSError(domain: "domain", code: 1, userInfo: nil) - _ = logger.optional(error, "test") - XCTAssert(service.invokedOptional) - } - - func testDebug() { - logger.debug("test") - XCTAssert(service.invokedDebug) - } - - func testInfo() { - logger.info("test") - XCTAssert(service.invokedInfo) - } - - func testFatal() { - logger.fatal(.apiError, "test") - XCTAssert(service.invokedFatal) - } - - func testUnexpected() { - logger.unexpected(.incorrectValue, "test") - XCTAssert(service.invokedUnexpected) - } - - func testFileUrls() { - XCTAssertEqual(logger.fileUrls, service.fileUrls) - } - -} diff --git a/Tests/LoggerTests/Mocks/FileLoggerServiceMock.swift b/Tests/Mocks/FileLoggerServiceMock.swift similarity index 97% rename from Tests/LoggerTests/Mocks/FileLoggerServiceMock.swift rename to Tests/Mocks/FileLoggerServiceMock.swift index a7b2ef2..9fb4ebb 100644 --- a/Tests/LoggerTests/Mocks/FileLoggerServiceMock.swift +++ b/Tests/Mocks/FileLoggerServiceMock.swift @@ -16,7 +16,7 @@ class FileLoggerServiceMock: FileLoggerService { var lastLine: String = "" var fileUrls: [URL] { - return [] + [] } func debug(_ string: String) { @@ -33,5 +33,4 @@ class FileLoggerServiceMock: FileLoggerService { invokedError = true lastLine = string } - } diff --git a/Tests/LoggerTests/Mocks/LoggerServiceMock.swift b/Tests/Mocks/LoggerServiceMock.swift similarity index 97% rename from Tests/LoggerTests/Mocks/LoggerServiceMock.swift rename to Tests/Mocks/LoggerServiceMock.swift index a5d35c9..0c98db5 100644 --- a/Tests/LoggerTests/Mocks/LoggerServiceMock.swift +++ b/Tests/Mocks/LoggerServiceMock.swift @@ -17,7 +17,7 @@ class LoggerServiceMock: LoggerService { var invokedFatal = false var fileUrls: [URL] { - return [] + [] } func debug(_ string: String) { @@ -40,5 +40,4 @@ class LoggerServiceMock: LoggerService { func fatal(_ reason: String, _ detail: String?) { invokedFatal = true } - }