From 9ecc7a9ddf5889ca278a3d6e8f77a8e92874fd20 Mon Sep 17 00:00:00 2001 From: Lucas Nelaupe Date: Wed, 18 Apr 2018 12:24:05 +0800 Subject: [PATCH 1/3] Implement internal logger --- Sources/SwiftQueue/JobBuilder.swift | 6 +- Sources/SwiftQueue/JobInfo.swift | 1 - Sources/SwiftQueue/SqOperation.swift | 32 +++++--- Sources/SwiftQueue/SqOperationQueue.swift | 7 +- Sources/SwiftQueue/SwiftQueueLogger.swift | 76 +++++++++++++++++++ Sources/SwiftQueue/SwiftQueueManager.swift | 11 ++- SwiftQueue.xcodeproj/project.pbxproj | 14 ++++ Tests/SwiftQueueTests/LoggerTests.swift | 49 ++++++++++++ .../SwiftQueueBuilderTests.swift | 3 +- Tests/SwiftQueueTests/SwiftQueueTests.swift | 10 ++- 10 files changed, 184 insertions(+), 25 deletions(-) create mode 100644 Sources/SwiftQueue/SwiftQueueLogger.swift create mode 100644 Tests/SwiftQueueTests/LoggerTests.swift diff --git a/Sources/SwiftQueue/JobBuilder.swift b/Sources/SwiftQueue/JobBuilder.swift index 1072c7ee..7d1a2493 100644 --- a/Sources/SwiftQueue/JobBuilder.swift +++ b/Sources/SwiftQueue/JobBuilder.swift @@ -93,8 +93,8 @@ public final class JobBuilder { return self } - internal func build(job: Job) -> SqOperation { - return SqOperation(job: job, info: info) + internal func build(job: Job, logger: SwiftQueueLogger) -> SqOperation { + return SqOperation(job: job, info: info, logger: logger) } /// Add job to the JobQueue @@ -107,6 +107,6 @@ public final class JobBuilder { let queue = manager.getQueue(queueName: info.group) let job = queue.createHandler(type: info.type, params: info.params) - queue.addOperation(build(job: job)) + queue.addOperation(build(job: job, logger: manager.logger)) } } diff --git a/Sources/SwiftQueue/JobInfo.swift b/Sources/SwiftQueue/JobInfo.swift index 4eb28f66..d1acd119 100644 --- a/Sources/SwiftQueue/JobInfo.swift +++ b/Sources/SwiftQueue/JobInfo.swift @@ -40,7 +40,6 @@ struct JobInfo { init?(dictionary: [String: Any]) { guard let type = dictionary["type"] as? String else { - assertionFailure("Unable to retrieve Job type") return nil } diff --git a/Sources/SwiftQueue/SqOperation.swift b/Sources/SwiftQueue/SqOperation.swift index 550f9931..937b5002 100644 --- a/Sources/SwiftQueue/SqOperation.swift +++ b/Sources/SwiftQueue/SqOperation.swift @@ -14,6 +14,8 @@ internal final class SqOperation: Operation { var lastError: Swift.Error? + let logger: SwiftQueueLogger + override var name: String? { get { return info.uuid } set { } } private var jobIsExecuting: Bool = false @@ -36,9 +38,10 @@ internal final class SqOperation: Operation { } } - internal init(job: Job, info: JobInfo) { + internal init(job: Job, info: JobInfo, logger: SwiftQueueLogger) { self.handler = job self.info = info + self.logger = logger self.constraints = [ DeadlineConstraint(), @@ -56,23 +59,24 @@ internal final class SqOperation: Operation { override func start() { super.start() + logger.log(.verbose, jobId: info.uuid, message: "Job has been started by the system") isExecuting = true run() } override func cancel() { - lastError = SwiftQueueError.canceled - onTerminate() - super.cancel() + self.cancel(with: .canceled) } func cancel(with: SwiftQueueError) { + logger.log(.verbose, jobId: info.uuid, message: "Job has been canceled") lastError = with onTerminate() super.cancel() } func onTerminate() { + logger.log(.verbose, jobId: info.uuid, message: "Job will not run anymore") if isExecuting { isFinished = true } @@ -80,6 +84,7 @@ internal final class SqOperation: Operation { // cancel before schedule and serialise internal func abort(error: Swift.Error) { + logger.log(.verbose, jobId: info.uuid, message: "Job has not been scheduled due to \(error.localizedDescription)") lastError = error // Need to be called manually since the task is actually not in the queue. So cannot call cancel() handler.onRemove(result: .fail(error)) @@ -96,6 +101,7 @@ internal final class SqOperation: Operation { do { try self.willRunJob() } catch let error { + logger.log(.warning, jobId: info.uuid, message: "Job cannot run due to \(error.localizedDescription)") // Will never run again lastError = error onTerminate() @@ -104,14 +110,17 @@ internal final class SqOperation: Operation { guard self.checkIfJobCanRunNow() else { // Constraint fail. // Constraint will call run when it's ready + logger.log(.verbose, jobId: info.uuid, message: "Job cannot run now. Execution is postponed") return } + logger.log(.verbose, jobId: info.uuid, message: "Job is running") handler.onRun(callback: self) } internal func remove() { let result = lastError.map(JobCompletion.fail) ?? JobCompletion.success + logger.log(.verbose, jobId: info.uuid, message: "Job is removed from the queue result=\(result)") handler.onRemove(result: result) } @@ -131,6 +140,7 @@ extension SqOperation: JobResult { } private func completionFail(error: Swift.Error) { + logger.log(.warning, jobId: info.uuid, message: "Job completed with error \(error.localizedDescription)") lastError = error switch info.retries { @@ -168,6 +178,7 @@ extension SqOperation: JobResult { } private func completionSuccess() { + logger.log(.verbose, jobId: info.uuid, message: "Job completed successfully") lastError = nil info.currentRepetition = 0 @@ -197,20 +208,17 @@ extension SqOperation: JobResult { extension SqOperation { - convenience init?(dictionary: [String: Any], creator: JobCreator) { + convenience init?(json: String, creator: JobCreator, logger: SwiftQueueLogger) { + let dictionary = fromJSON(json) as? [String: Any] ?? [:] + guard let info = JobInfo(dictionary: dictionary) else { - assertionFailure("Unable to un-serialise job") + logger.log(.error, jobId: "UNKNOWN", message: "Unable to un-serialise job [\(json)]") return nil } let job = creator.create(type: info.type, params: info.params) - self.init(job: job, info: info) - } - - convenience init?(json: String, creator: JobCreator) { - let dict = fromJSON(json) as? [String: Any] ?? [:] - self.init(dictionary: dict, creator: creator) + self.init(job: job, info: info, logger: logger) } func toJSONString() -> String? { diff --git a/Sources/SwiftQueue/SqOperationQueue.swift b/Sources/SwiftQueue/SqOperationQueue.swift index b1b54fec..ada9ef15 100644 --- a/Sources/SwiftQueue/SqOperationQueue.swift +++ b/Sources/SwiftQueue/SqOperationQueue.swift @@ -11,10 +11,13 @@ internal final class SqOperationQueue: OperationQueue { private let queueName: String - init(_ queueName: String, _ creator: JobCreator, _ persister: JobPersister? = nil, _ isPaused: Bool = false) { + private let logger: SwiftQueueLogger + + init(_ queueName: String, _ creator: JobCreator, _ persister: JobPersister? = nil, _ isPaused: Bool = false, logger: SwiftQueueLogger) { self.creator = creator self.persister = persister self.queueName = queueName + self.logger = logger super.init() @@ -27,7 +30,7 @@ internal final class SqOperationQueue: OperationQueue { private func loadSerializedTasks(name: String) { persister?.restore(queueName: name).flatMapCompact { string -> SqOperation? in - SqOperation(json: string, creator: creator) + return SqOperation(json: string, creator: creator, logger: logger) }.sorted { $0.info.createTime < $1.info.createTime }.forEach(addOperation) diff --git a/Sources/SwiftQueue/SwiftQueueLogger.swift b/Sources/SwiftQueue/SwiftQueueLogger.swift new file mode 100644 index 00000000..83b09119 --- /dev/null +++ b/Sources/SwiftQueue/SwiftQueueLogger.swift @@ -0,0 +1,76 @@ +// +// Created by Lucas Nelaupe on 18/4/18. +// + +import Foundation + +/// Specify different importance of logging +public enum LogLevel: Int { + + /// Basic information about scheduling, running job and completion + case verbose = 1 + /// Important but non fatal information + case warning = 2 + /// Something went wrong during the scheduling or the execution + case error = 3 + +} + +internal extension LogLevel { + var description: String { + switch self { + case .verbose: + return "verbose" + case .warning: + return "warning" + case .error: + return "error" + } + } +} + +/// Func +public protocol SwiftQueueLogger { + + /// Function called by the library to log an event + func log(_ level: LogLevel, jobId: @autoclosure () -> String, message: @autoclosure () -> String) + +} + +/// Class to compute the log and print to the console +open class ConsoleLogger: SwiftQueueLogger { + + private let min: LogLevel + + /// Define minimum level to log. By default, it will log everything + init(min: LogLevel = .verbose) { + self.min = min + } + + /// Check for log level and create the output message + public final func log(_ level: LogLevel, jobId: @autoclosure () -> String, message: @autoclosure () -> String) { + if min.rawValue >= level.rawValue { + printComputed(output: "[SwiftQueue] level=\(level.description) jobId=\(jobId()) message=\(message())") + } + } + + /// Print with default `print()` function. Can be override to changed the output + open func printComputed(output: String) { + print(output) + } + +} + +/// Class to ignore all kind of logs +public class NoLogger: SwiftQueueLogger { + + /// Singleton instance to avoid multiple instance across all queues + public static let shared = NoLogger() + + private init() {} + + /// Default implementation that will not log anything + public func log(_ level: LogLevel, jobId: @autoclosure () -> String, message: @autoclosure () -> String) { + // Nothing to do + } +} diff --git a/Sources/SwiftQueue/SwiftQueueManager.swift b/Sources/SwiftQueue/SwiftQueueManager.swift index 6ceb725b..9c09ff9d 100644 --- a/Sources/SwiftQueue/SwiftQueueManager.swift +++ b/Sources/SwiftQueue/SwiftQueueManager.swift @@ -7,7 +7,7 @@ import Foundation /// Global manager to perform operations on all your queues/ /// You will have to keep this instance. We highly recommend you to store this instance in a Singleton -/// Creating and instance of this class will automatically un-serialise your jobs and schedule them +/// Creating and instance of this class will automatically un-serialise your jobs and schedule them public final class SwiftQueueManager { private let creator: JobCreator @@ -17,14 +17,17 @@ public final class SwiftQueueManager { private var isPaused = true + internal let logger: SwiftQueueLogger + /// Create a new QueueManager with creators to instantiate Job - public init(creator: JobCreator, persister: JobPersister? = nil) { + public init(creator: JobCreator, persister: JobPersister? = nil, logger: SwiftQueueLogger = NoLogger.shared) { self.creator = creator self.persister = persister + self.logger = logger if let data = persister { for queueName in data.restore() { - manage[queueName] = SqOperationQueue(queueName, creator, persister, isPaused) + manage[queueName] = SqOperationQueue(queueName, creator, persister, isPaused, logger: logger) } } start() @@ -51,7 +54,7 @@ public final class SwiftQueueManager { } private func createQueue(queueName: String) -> SqOperationQueue { - let queue = SqOperationQueue(queueName, creator, persister, isPaused) + let queue = SqOperationQueue(queueName, creator, persister, isPaused, logger: logger) manage[queueName] = queue return queue } diff --git a/SwiftQueue.xcodeproj/project.pbxproj b/SwiftQueue.xcodeproj/project.pbxproj index 06140e2e..9abfadf0 100644 --- a/SwiftQueue.xcodeproj/project.pbxproj +++ b/SwiftQueue.xcodeproj/project.pbxproj @@ -32,6 +32,11 @@ 1E68ED598FA15EF36B431571 /* Constrains+Deadline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68EF96F1DE038FFF94FCD8 /* Constrains+Deadline.swift */; }; 1E68EDC2230D2FACC61E7868 /* Constrains+Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68E940C8373221E3EDC771 /* Constrains+Delay.swift */; }; 1E68EE914B5B51CDB2BD7C4D /* Constraints+UniqueUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68E002DC39E9BBE4A0A06A /* Constraints+UniqueUUID.swift */; }; + 95A210264F99EAC94629D786 /* SwiftQueueLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */; }; + 95A214C332FB7115C12F88D2 /* LoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A2193AF039008357A8D362 /* LoggerTests.swift */; }; + 95A21576964E9A1FE1EDC5DB /* SwiftQueueLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */; }; + 95A21B708B2910C7CF8BD39D /* SwiftQueueLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */; }; + 95A21FA05AABFA4A59096E8E /* SwiftQueueLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */; }; B06405031FA04AA000086C69 /* UserDefaultsPersister.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8479A11F82A84A00B3BBFB /* UserDefaultsPersister.swift */; }; B06405041FA04AA000086C69 /* SwiftQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8479A21F82A84A00B3BBFB /* SwiftQueue.swift */; }; B06405061FA04AA000086C69 /* Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8479A61F82A84B00B3BBFB /* Constraints.swift */; }; @@ -96,6 +101,8 @@ 1E68E82C1E071C7AA1AC4BB6 /* Constrains+Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constrains+Network.swift"; path = "SwiftQueue/Constrains+Network.swift"; sourceTree = ""; }; 1E68E940C8373221E3EDC771 /* Constrains+Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constrains+Delay.swift"; path = "SwiftQueue/Constrains+Delay.swift"; sourceTree = ""; }; 1E68EF96F1DE038FFF94FCD8 /* Constrains+Deadline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constrains+Deadline.swift"; path = "SwiftQueue/Constrains+Deadline.swift"; sourceTree = ""; }; + 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftQueueLogger.swift; path = SwiftQueue/SwiftQueueLogger.swift; sourceTree = ""; }; + 95A2193AF039008357A8D362 /* LoggerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggerTests.swift; sourceTree = ""; }; B064050E1FA04AA000086C69 /* SwiftQueue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftQueue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B064051D1FA04AAC00086C69 /* SwiftQueue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftQueue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B064052C1FA04CCF00086C69 /* SwiftQueue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftQueue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -174,6 +181,7 @@ D0B7C62E5894FA6EE00D3160 /* PersisterTests.swift */, D0B7C898344F1EB3A0815D67 /* ConstraintDeadlineTests.swift */, D0B7C2A57EFC83B94E3146B3 /* Package.swift */, + 95A2193AF039008357A8D362 /* LoggerTests.swift */, ); name = SwiftQueueTests; path = Tests/SwiftQueueTests; @@ -217,6 +225,7 @@ D0B7C4E41A1BF73329A7D03D /* JobInfo.swift */, D0B7CB3B79E039D3B11D8921 /* JobBuilder.swift */, D0B7CB571CECEEA34DD07398 /* SqOperationQueue.swift */, + 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */, ); path = Sources; sourceTree = SOURCE_ROOT; @@ -360,6 +369,7 @@ D0B7CC03343274087B4C7955 /* JobInfo.swift in Sources */, D0B7C8729FA2F6EF3A6C42F8 /* JobBuilder.swift in Sources */, D0B7C9AE1E4717DE474FD804 /* SqOperationQueue.swift in Sources */, + 95A21FA05AABFA4A59096E8E /* SwiftQueueLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -380,6 +390,7 @@ D0B7C0848D0FF83C8B04EBB6 /* JobInfo.swift in Sources */, D0B7CFA2089DFF576E0D765B /* JobBuilder.swift in Sources */, D0B7C2F3F465996A939596F7 /* SqOperationQueue.swift in Sources */, + 95A21576964E9A1FE1EDC5DB /* SwiftQueueLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -400,6 +411,7 @@ D0B7CC89D901F6C8AD2666D9 /* JobInfo.swift in Sources */, D0B7C6B8833437575C438715 /* JobBuilder.swift in Sources */, D0B7C276BFD64AF0EA757C55 /* SqOperationQueue.swift in Sources */, + 95A21B708B2910C7CF8BD39D /* SwiftQueueLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -420,6 +432,7 @@ D0B7C996BDB2A748B6B8843A /* JobInfo.swift in Sources */, D0B7C437539F0BEE35FBE8F0 /* JobBuilder.swift in Sources */, D0B7CD1E797A69C89CB0ADE2 /* SqOperationQueue.swift in Sources */, + 95A210264F99EAC94629D786 /* SwiftQueueLogger.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -436,6 +449,7 @@ D0B7C50629CD1E0F8F40E7F3 /* ConstraintNetworkTests.swift in Sources */, D0B7C9B78BFACD1BEDA4D42E /* PersisterTests.swift in Sources */, D0B7C6C3E08DF6BFD0737E4D /* ConstraintDeadlineTests.swift in Sources */, + 95A214C332FB7115C12F88D2 /* LoggerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tests/SwiftQueueTests/LoggerTests.swift b/Tests/SwiftQueueTests/LoggerTests.swift new file mode 100644 index 00000000..f3c0fffa --- /dev/null +++ b/Tests/SwiftQueueTests/LoggerTests.swift @@ -0,0 +1,49 @@ +// +// Created by Lucas Nelaupe on 18/4/18. +// + +import XCTest +import Dispatch +@testable import SwiftQueue + +class LoggerTests: XCTestCase { + + func testRunSuccessJobLogger() { + let id = UUID().uuidString + + let debugLogger = DebugLogger() + + let (type, job) = (UUID().uuidString, TestJob()) + + let creator = TestCreator([type: job]) + + let manager = SwiftQueueManager(creator: creator, logger: debugLogger) + JobBuilder(type: type) + .singleInstance(forId: id) + .internet(atLeast: .wifi) + .schedule(manager: manager) + + job.awaitForRemoval() + job.assertSingleCompletion() + + let outputs = debugLogger.outputs + + XCTAssertEqual(5, outputs.count) + XCTAssertEqual(outputs[0], "[SwiftQueue] level=verbose jobId=\(id) message=Job has been started by the system") + XCTAssertEqual(outputs[1], "[SwiftQueue] level=verbose jobId=\(id) message=Job is running") + XCTAssertEqual(outputs[2], "[SwiftQueue] level=verbose jobId=\(id) message=Job completed successfully") + XCTAssertEqual(outputs[3], "[SwiftQueue] level=verbose jobId=\(id) message=Job will not run anymore") + XCTAssertEqual(outputs[4], "[SwiftQueue] level=verbose jobId=\(id) message=Job is removed from the queue result=success") + } + +} + +class DebugLogger: ConsoleLogger { + + var outputs = [String]() + + override func printComputed(output: String) { + outputs.append(output) + } + +} diff --git a/Tests/SwiftQueueTests/SwiftQueueBuilderTests.swift b/Tests/SwiftQueueTests/SwiftQueueBuilderTests.swift index cd2d040c..9611379b 100644 --- a/Tests/SwiftQueueTests/SwiftQueueBuilderTests.swift +++ b/Tests/SwiftQueueTests/SwiftQueueBuilderTests.swift @@ -153,8 +153,7 @@ class SwiftQueueBuilderTests: XCTestCase { builder.persist(required: true).schedule(manager: manager) - let actual = SqOperation(json: persister.putData[0], creator: creator) - return actual?.info + return SqOperation(json: persister.putData[0], creator: creator, logger: manager.logger)?.info } } diff --git a/Tests/SwiftQueueTests/SwiftQueueTests.swift b/Tests/SwiftQueueTests/SwiftQueueTests.swift index 74846367..9945c81c 100644 --- a/Tests/SwiftQueueTests/SwiftQueueTests.swift +++ b/Tests/SwiftQueueTests/SwiftQueueTests.swift @@ -121,7 +121,7 @@ class SwiftQueueManagerTests: XCTestCase { } func testAddOperationNotJobTask() { - let queue = SqOperationQueue(UUID().uuidString, TestCreator([:])) + let queue = SqOperationQueue(UUID().uuidString, TestCreator([:]), logger: NoLogger.shared) let operation = Operation() queue.addOperation(operation) // Should not crash } @@ -139,3 +139,11 @@ class SwiftQueueManagerTests: XCTestCase { } } + +extension JobBuilder { + + internal func build(job: Job) -> SqOperation { + return self.build(job: job, logger: NoLogger.shared) + } + +} From 045c84149e637c150f9c6dade596eab99ebb11a1 Mon Sep 17 00:00:00 2001 From: Lucas Nelaupe Date: Wed, 18 Apr 2018 16:28:18 +0800 Subject: [PATCH 2/3] Fix constraint does not properly cancel the job and execution flow should stop --- Sources/SwiftQueue/Constrains+Deadline.swift | 2 +- Sources/SwiftQueue/SqOperation.swift | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/SwiftQueue/Constrains+Deadline.swift b/Sources/SwiftQueue/Constrains+Deadline.swift index 49404fb7..c1952fe1 100644 --- a/Sources/SwiftQueue/Constrains+Deadline.swift +++ b/Sources/SwiftQueue/Constrains+Deadline.swift @@ -23,7 +23,7 @@ internal final class DeadlineConstraint: JobConstraint { guard let ope = operation else { return } guard !ope.isFinished else { return } - ope.cancel(with: .deadline) + ope.cancel(with: SwiftQueueError.deadline) }) return true } diff --git a/Sources/SwiftQueue/SqOperation.swift b/Sources/SwiftQueue/SqOperation.swift index 937b5002..d4c3161b 100644 --- a/Sources/SwiftQueue/SqOperation.swift +++ b/Sources/SwiftQueue/SqOperation.swift @@ -65,10 +65,10 @@ internal final class SqOperation: Operation { } override func cancel() { - self.cancel(with: .canceled) + self.cancel(with: SwiftQueueError.canceled) } - func cancel(with: SwiftQueueError) { + func cancel(with: Swift.Error) { logger.log(.verbose, jobId: info.uuid, message: "Job has been canceled") lastError = with onTerminate() @@ -103,8 +103,8 @@ internal final class SqOperation: Operation { } catch let error { logger.log(.warning, jobId: info.uuid, message: "Job cannot run due to \(error.localizedDescription)") // Will never run again - lastError = error - onTerminate() + cancel(with: error) + return } guard self.checkIfJobCanRunNow() else { From 3bfea34489a67f7410a6b7ce36e433c7f9cc1338 Mon Sep 17 00:00:00 2001 From: Lucas Nelaupe Date: Wed, 18 Apr 2018 17:21:54 +0800 Subject: [PATCH 3/3] Fix logger level and add test --- Sources/SwiftQueue/SwiftQueueLogger.swift | 2 +- Tests/SwiftQueueTests/LoggerTests.swift | 42 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftQueue/SwiftQueueLogger.swift b/Sources/SwiftQueue/SwiftQueueLogger.swift index 83b09119..6be5275e 100644 --- a/Sources/SwiftQueue/SwiftQueueLogger.swift +++ b/Sources/SwiftQueue/SwiftQueueLogger.swift @@ -49,7 +49,7 @@ open class ConsoleLogger: SwiftQueueLogger { /// Check for log level and create the output message public final func log(_ level: LogLevel, jobId: @autoclosure () -> String, message: @autoclosure () -> String) { - if min.rawValue >= level.rawValue { + if min.rawValue <= level.rawValue { printComputed(output: "[SwiftQueue] level=\(level.description) jobId=\(jobId()) message=\(message())") } } diff --git a/Tests/SwiftQueueTests/LoggerTests.swift b/Tests/SwiftQueueTests/LoggerTests.swift index f3c0fffa..a2b876db 100644 --- a/Tests/SwiftQueueTests/LoggerTests.swift +++ b/Tests/SwiftQueueTests/LoggerTests.swift @@ -2,6 +2,7 @@ // Created by Lucas Nelaupe on 18/4/18. // +import Foundation import XCTest import Dispatch @testable import SwiftQueue @@ -36,6 +37,47 @@ class LoggerTests: XCTestCase { XCTAssertEqual(outputs[4], "[SwiftQueue] level=verbose jobId=\(id) message=Job is removed from the queue result=success") } + func testLoggerLevel() { + + let verbose = DebugLogger(min: .verbose) + let warning = DebugLogger(min: .warning) + let error = DebugLogger(min: .error) + + let verbose1 = UUID().uuidString + let verbose2 = UUID().uuidString + + verbose.log(.verbose, jobId: verbose1, message: verbose2) + warning.log(.verbose, jobId: verbose1, message: verbose2) + error.log(.verbose, jobId: verbose1, message: verbose2) + + let warning1 = UUID().uuidString + let warning2 = UUID().uuidString + + verbose.log(.warning, jobId: warning1, message: warning2) + warning.log(.warning, jobId: warning1, message: warning2) + error.log(.warning, jobId: warning1, message: warning2) + + let error1 = UUID().uuidString + let error2 = UUID().uuidString + + verbose.log(.error, jobId: error1, message: error2) + warning.log(.error, jobId: error1, message: error2) + error.log(.error, jobId: error1, message: error2) + + XCTAssertEqual(3, verbose.outputs.count) + XCTAssertEqual(2, warning.outputs.count) + XCTAssertEqual(1, error.outputs.count) + + XCTAssertEqual(verbose.outputs[0], "[SwiftQueue] level=\(LogLevel.verbose.description) jobId=\(verbose1) message=\(verbose2)") + XCTAssertEqual(verbose.outputs[1], "[SwiftQueue] level=\(LogLevel.warning.description) jobId=\(warning1) message=\(warning2)") + XCTAssertEqual(verbose.outputs[2], "[SwiftQueue] level=\(LogLevel.error.description) jobId=\(error1) message=\(error2)") + + XCTAssertEqual(warning.outputs[0], "[SwiftQueue] level=\(LogLevel.warning.description) jobId=\(warning1) message=\(warning2)") + XCTAssertEqual(warning.outputs[1], "[SwiftQueue] level=\(LogLevel.error.description) jobId=\(error1) message=\(error2)") + + XCTAssertEqual(error.outputs[0], "[SwiftQueue] level=\(LogLevel.error.description) jobId=\(error1) message=\(error2)") + } + } class DebugLogger: ConsoleLogger {