Skip to content

Commit

Permalink
Merge pull request #3 from jakehawken/jake/LoggingWithExternalHooks
Browse files Browse the repository at this point in the history
Updated logging to include external hook for extended logging functionality.
  • Loading branch information
jakehawken committed Jul 26, 2022
2 parents 69988dd + 5f13953 commit 7692063
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 59 deletions.
38 changes: 28 additions & 10 deletions Sources/Propagate/PublisherAndSubscriber/PropagateDebug.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,36 @@ public enum DebugLogLevel {
case none
}

typealias DebugPair = (logLevel: DebugLogLevel, message: String)
public typealias LoggingHook = (String) -> Void
typealias LoggingCombo = (logLevel: DebugLogLevel, message: String, loggingMethod: LoggingMethod?)

internal func safePrint(_ message: String, logType: LogType, debugPair: DebugPair?) {
guard let pair = debugPair else {
public enum LoggingMethod {

case external(LoggingHook)

@available(*, deprecated, message: "This logging method is intended for debug use only. It is highly recommended that you not check in code using .")
case debugPrint
}

internal func safePrint(_ message: String, logType: LogType, loggingCombo: LoggingCombo?) {
guard let combo = loggingCombo else {
return
}
guard logType.canBeShownAt(logLevel: pair.logLevel) else {
guard logType.canBeShownAt(logLevel: combo.logLevel) else {
return
}
var output = "<>DEBUG: "
if pair.message.count > 0 {
output += "\(pair.message) - "
var output = "<>PROPAGATE: "
if combo.message.count > 0 {
output += "\(combo.message) - "
}
output += message
print(output)

switch loggingCombo?.loggingMethod {
case .debugPrint, nil:
print(output)
case .external(let hook):
hook(output)
}
}

internal enum LogType: Equatable {
Expand Down Expand Up @@ -100,6 +115,9 @@ public protocol PropagateDebuggable {
/// `.debug(logLevel: .operatorsOnly, "Mutations on the name stream.")`
///
/// - returns: The type itself, as a `@discardableResult`, to allow for seamless chaining.
@available(*, message: "This method is intended for debug use only. It is highly recommended that you not check in code calling this method.")
@discardableResult func debug(logLevel: DebugLogLevel, _ additionalMessage: String) -> Self
@discardableResult func enableLogging(
logLevel: DebugLogLevel,
_ additionalMessage: String,
_ logMethod: LoggingMethod
) -> Self
}
24 changes: 14 additions & 10 deletions Sources/Propagate/PublisherAndSubscriber/Publisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public class Publisher<T, E: Error> {
fileprivate var subscribers = WeakBag<Subscriber<T, E>>()
private(set) public var isCancelled = false

internal var debugPair: (DebugPair)?
internal var loggingCombo: (LoggingCombo)?

public init() {
}

/// Emits a new `StreamState`. If this publisher has previously been
/// cancelled, this method will be a no-op.
internal func publishNewState(_ state: State) {
safePrint("Publishing state \(state). -- \(self)", logType: .pubSub, debugPair: debugPair)
safePrint("Publishing state \(state). -- \(self)", logType: .pubSub, loggingCombo: loggingCombo)
lockQueue.async {
if self.isCancelled {
return
Expand All @@ -35,7 +35,7 @@ public class Publisher<T, E: Error> {
}

deinit {
safePrint("Releasing \(self) from memory.", logType: .lifeCycle, debugPair: debugPair)
safePrint("Releasing \(self) from memory.", logType: .lifeCycle, loggingCombo: loggingCombo)
// The asynchronous cancelAll() can't be called from deinit
// because it results in a bad access crash.
handleCancellation()
Expand All @@ -58,7 +58,7 @@ public class Publisher<T, E: Error> {
lockQueue.async { [weak self] in
self?.subscribers.insert(newSub)
}
safePrint("Generating new subscriber: \(newSub) from \(self)", logType: .lifeCycle, debugPair: debugPair)
safePrint("Generating new subscriber: \(newSub) from \(self)", logType: .lifeCycle, loggingCombo: loggingCombo)
return newSub
}

Expand Down Expand Up @@ -119,8 +119,12 @@ public extension Publisher {

extension Publisher: PropagateDebuggable {

@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
@discardableResult public func enableLogging(
logLevel: DebugLogLevel = .all,
_ additionalMessage: String = "",
_ logMethod: LoggingMethod = .debugPrint
) -> Self {
self.loggingCombo = (logLevel, additionalMessage, logMethod)
return self
}

Expand All @@ -131,7 +135,7 @@ extension Publisher: PropagateDebuggable {
private extension Publisher {

func removeSubscriber(_ subscriber: Subscriber<T,E>) {
safePrint("Removing \(subscriber) from \(self)", logType: .pubSub, debugPair: debugPair)
safePrint("Removing \(subscriber) from \(self)", logType: .pubSub, loggingCombo: loggingCombo)
self.subscribers.pruneIf { $0 === subscriber }
subscriber.receive(.cancelled)
}
Expand All @@ -142,9 +146,9 @@ private extension Publisher {
}
isCancelled = true
let removedSubscribers = subscribers.removeAll()
safePrint("Removing subscribers: \(removedSubscribers)", logType: .pubSub, debugPair: debugPair)
safePrint("Removing subscribers: \(removedSubscribers)", logType: .pubSub, loggingCombo: loggingCombo)
removedSubscribers.forEach {
safePrint("Sending cancellation signal to \($0)", logType: .pubSub, debugPair: debugPair)
safePrint("Sending cancellation signal to \($0)", logType: .pubSub, loggingCombo: loggingCombo)
$0.receive(.cancelled)
}
}
Expand Down Expand Up @@ -230,7 +234,7 @@ public class StatefulPublisher<T,E: Error>: Publisher<T, E> {
safePrint(
"Generating new subscriber. -- \(self) --> \(newSub)",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
return newSub
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public extension Subscriber {
safePrint(
"Filtering \(self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)

return publisher.subscriber()
Expand Down Expand Up @@ -97,7 +97,7 @@ public extension Subscriber where T: Equatable {
safePrint(
"Removing contiguous duplicates values from \(self).",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return publisher.subscriber()
.onCancelled {
Expand Down Expand Up @@ -137,7 +137,7 @@ public extension Subscriber where T: Equatable, E: Equatable {
safePrint(
"Removing contiguous duplicates values from \(self).",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)

return publisher.subscriber()
Expand Down
6 changes: 3 additions & 3 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber+Map.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public extension Subscriber {
safePrint(
"Mapping states from StreamState<\(T.self),\(E.self)> to StreamState<\(NewT.self),\(NewE.self)>",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return newSubscriber
.onCancelled {
Expand All @@ -41,7 +41,7 @@ public extension Subscriber {
safePrint(
"Mapping values from \(T.self) to \(NewT.self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return mapStates { oldState in
switch oldState {
Expand Down Expand Up @@ -77,7 +77,7 @@ public extension Subscriber {
safePrint(
"Mapping errors from \(E.self) to \(NewE.self).",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return mapStates { oldState in
switch oldState {
Expand Down
26 changes: 20 additions & 6 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber+Statify.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ public extension Subscriber {
/// Generates a new subscriber that synchronously
/// emits the last state (if any) when subscribed to.
func stateful() -> Subscriber<T,E> {
let publisher = StatefulPublisher<T,E>()

subscribe { state in
publisher.publishNewState(state)
}

boundStatefulPublisher()
.subscriber()
.onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
}
}

func startWith(_ value: T) -> Subscriber<T,E> {
let publisher = boundStatefulPublisher()
publisher.publish(value)
return publisher.subscriber()
.onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
Expand Down Expand Up @@ -114,3 +118,13 @@ class ScanState<T,E: Error> {
}

}

private extension Subscriber {

func boundStatefulPublisher() -> StatefulPublisher<T,E> {
let publisher = StatefulPublisher<T,E>()
bindTo(publisher)
return publisher
}

}
16 changes: 10 additions & 6 deletions Sources/Propagate/PublisherAndSubscriber/Subscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class Subscriber<T, E: Error> {
private lazy var callbacks = SinglyLinkedList<ExecutionPair>(firstValue: (callbackQueue, { _ in }))

private(set) public var isCancelled = false
internal var debugPair: DebugPair?
internal var loggingCombo: LoggingCombo?

internal init(canceller: Canceller<T,E>) {
self.canceller = canceller
Expand All @@ -30,7 +30,7 @@ public class Subscriber<T, E: Error> {
safePrint(
"Releasing \(self) from memory.",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
cancel()
}
Expand All @@ -56,8 +56,12 @@ public class Subscriber<T, E: Error> {

extension Subscriber: PropagateDebuggable {

@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
@discardableResult public func enableLogging(
logLevel: DebugLogLevel = .all,
_ additionalMessage: String = "",
_ logMethod: LoggingMethod = .debugPrint
) -> Self {
self.loggingCombo = (logLevel, additionalMessage, logMethod)
return self
}

Expand Down Expand Up @@ -88,7 +92,7 @@ internal extension Subscriber {
safePrint(
"Cancelling \(self)...",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
canceller.cancel(for: self)
receive(.cancelled)
Expand All @@ -107,7 +111,7 @@ private extension Subscriber {
safePrint(
"Received \(state). -- \(self)",
logType: .pubSub,
debugPair: debugPair
loggingCombo: loggingCombo
)
callbacks.forEach { (queue, action) in
queue.async { action(state) }
Expand Down
26 changes: 15 additions & 11 deletions Sources/Propagate/PublisherAndSubscriber/ValueOnlySubscriber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ValueOnlySubscriber<T> {
private var valueCallbacks = [ValueExecutionPair]()
private var cancelCallbacks = [CancelExecutionPair]()
private(set) public var isCancelled = false
private var debugPair: DebugPair?
private var loggingCombo: LoggingCombo?

fileprivate init() {}

Expand Down Expand Up @@ -74,7 +74,7 @@ public class ValueOnlySubscriber<T> {
safePrint(
"Inflating ValueOnlySubscriber<T\(T.self)> to \(Subscriber<T,E>.self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return publisher.subscriber().onCancelled {
_ = self // Capturing self to keep subscriber alive for easier chaining.
Expand All @@ -85,7 +85,7 @@ public class ValueOnlySubscriber<T> {
safePrint(
"Releasing \(self) from memory.",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
cancel()
}
Expand All @@ -94,7 +94,7 @@ public class ValueOnlySubscriber<T> {
safePrint(
"Received \(value). -- \(self)",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
valueCallbacks.forEach { (queue, action) in
queue.async { action(value) }
Expand All @@ -111,7 +111,7 @@ public class ValueOnlySubscriber<T> {
safePrint(
"Cancelling \(self)...",
logType: .lifeCycle,
debugPair: debugPair
loggingCombo: loggingCombo
)
lockQueue.async { [weak self] in
self?.isCancelled = true
Expand All @@ -125,8 +125,12 @@ public class ValueOnlySubscriber<T> {

extension ValueOnlySubscriber: PropagateDebuggable, CustomStringConvertible {

@discardableResult public func debug(logLevel: DebugLogLevel = .all, _ additionalMessage: String = "") -> Self {
self.debugPair = (logLevel, additionalMessage)
@discardableResult public func enableLogging(
logLevel: DebugLogLevel = .all,
_ additionalMessage: String = "",
_ logMethod: LoggingMethod = .debugPrint
) -> Self {
self.loggingCombo = (logLevel, additionalMessage, logMethod)
return self
}

Expand Down Expand Up @@ -193,7 +197,7 @@ public extension ValueOnlySubscriber {
safePrint(
"Mapping from \(T.self) to \(NewT.self). -- \(self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return newSub
}
Expand All @@ -204,7 +208,7 @@ public extension ValueOnlySubscriber {
safePrint(
"Compact mapping from \(T.self) to \(NewT.self). -- \(self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)

let newSub = ValueOnlySubscriber<NewT>()
Expand Down Expand Up @@ -260,7 +264,7 @@ public extension ValueOnlySubscriber where T: Equatable {
safePrint(
"Removing contiguous duplicates from \(self)",
logType: .operators,
debugPair: debugPair
loggingCombo: loggingCombo
)
return new.onCancelled {
_ = self
Expand Down Expand Up @@ -295,7 +299,7 @@ public extension ValueOnlySubscriber {
safePrint(
"Combining ValueOnlySubscribers <\(T.self)> and <\(T2.self)>: -- \(memoryAddressStringFor(sub1)) & \(memoryAddressStringFor(sub2))",
logType: .operators,
debugPair: sub1.debugPair ?? sub2.debugPair // This will probably be a problem at some point.
loggingCombo: sub1.loggingCombo ?? sub2.loggingCombo // This will probably be a problem at some point.
)

return new.onCancelled {
Expand Down
4 changes: 1 addition & 3 deletions Tests/PropagateTests/PromiseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import Nimble
import Quick
import XCTest

// swiftlint:disable file_length
// swiftlint:disable:next type_body_length
class PromiseTests: QuickSpec {
// swiftlint:disable:next function_body_length
override func spec() {

var subject: Promise<Int,NSError>!
Expand Down
Loading

0 comments on commit 7692063

Please sign in to comment.