|
13 | 13 | //===----------------------------------------------------------------------===//
|
14 | 14 |
|
15 | 15 | import Crdkafka
|
| 16 | +import Logging |
16 | 17 |
|
17 | 18 | /// A collection of helper functions wrapping common `rd_kafka_conf_*` functions in Swift.
|
18 | 19 | struct RDKafkaConfig {
|
19 | 20 | typealias KafkaAcknowledgementResult = Result<KafkaAcknowledgedMessage, KafkaAcknowledgedMessageError>
|
20 | 21 | /// Wraps a Swift closure inside of a class to be able to pass it to `librdkafka` as an `OpaquePointer`.
|
21 |
| - final class CapturedClosure { |
22 |
| - typealias Closure = (KafkaAcknowledgementResult?) -> Void |
23 |
| - let closure: Closure |
| 22 | + final class CapturedClosures { |
| 23 | + typealias DeliveryReportClosure = (KafkaAcknowledgementResult?) -> Void |
| 24 | + var deliveryReportClosure: DeliveryReportClosure? |
24 | 25 |
|
25 |
| - init(_ closure: @escaping Closure) { |
26 |
| - self.closure = closure |
27 |
| - } |
| 26 | + typealias LoggingClosure = (Int32, UnsafePointer<CChar>, UnsafePointer<CChar>) -> Void |
| 27 | + var loggingClosure: LoggingClosure? |
| 28 | + |
| 29 | + init() { } |
28 | 30 | }
|
29 | 31 |
|
30 | 32 | /// Create a new `rd_kafka_conf_t` object in memory and initialize it with the given configuration properties.
|
@@ -63,46 +65,119 @@ struct RDKafkaConfig {
|
63 | 65 | }
|
64 | 66 | }
|
65 | 67 |
|
66 |
| - /// A Swift wrapper for `rd_kafka_conf_set_dr_msg_cb`. |
67 |
| - /// Defines a function that is called upon every message acknowledgement. |
| 68 | + /// Registers passed closures as callbacks and sets the application's opaque pointer that will be passed to callbacks |
| 69 | + /// - Parameter type: Kafka client type: `Consumer` or `Producer` |
68 | 70 | /// - Parameter configPointer: An `OpaquePointer` pointing to the `rd_kafka_conf_t` object in memory.
|
69 |
| - /// - Parameter callback: A closure that is invoked upon message acknowledgement. |
70 |
| - /// - Returns: A ``CapturedClosure`` object that must me retained by the caller as long as acknowledgements are received. |
71 |
| - static func setDeliveryReportCallback( |
| 71 | + /// - Parameter deliveryReportCallback: A closure that is invoked upon message acknowledgement. |
| 72 | + /// - Parameter logger: Logger instance |
| 73 | + /// - Returns: A ``CapturedClosures`` object that must me retained by the caller as long as it exists. |
| 74 | + static func setCallbackClosures( |
72 | 75 | configPointer: OpaquePointer,
|
73 |
| - _ callback: @escaping ((KafkaAcknowledgementResult?) -> Void) |
74 |
| - ) -> CapturedClosure { |
75 |
| - let capturedClosure = CapturedClosure(callback) |
76 |
| - // Pass the captured closure to the C closure as an opaque object |
77 |
| - let opaquePointer: UnsafeMutableRawPointer? = Unmanaged.passUnretained(capturedClosure).toOpaque() |
| 76 | + deliveryReportCallback: CapturedClosures.DeliveryReportClosure? = nil, |
| 77 | + logger: Logger |
| 78 | + ) -> CapturedClosures { |
| 79 | + let closures = CapturedClosures() |
| 80 | + |
| 81 | + // Pass the the reference to Opaque as an opaque object |
| 82 | + let opaquePointer: UnsafeMutableRawPointer? = Unmanaged.passUnretained(closures).toOpaque() |
78 | 83 | rd_kafka_conf_set_opaque(
|
79 | 84 | configPointer,
|
80 | 85 | opaquePointer
|
81 | 86 | )
|
82 | 87 |
|
| 88 | + // Set delivery report callback |
| 89 | + if let deliveryReportCallback { |
| 90 | + Self.setDeliveryReportCallback(configPointer: configPointer, capturedClosures: closures, deliveryReportCallback) |
| 91 | + } |
| 92 | + // Set logging callback |
| 93 | + Self.setLoggingCallback(configPointer: configPointer, capturedClosures: closures, logger: logger) |
| 94 | + |
| 95 | + return closures |
| 96 | + } |
| 97 | + |
| 98 | + /// A Swift wrapper for `rd_kafka_conf_set_dr_msg_cb`. |
| 99 | + /// Defines a function that is called upon every message acknowledgement. |
| 100 | + /// - Parameter configPointer: An `OpaquePointer` pointing to the `rd_kafka_conf_t` object in memory. |
| 101 | + /// - Parameter callback: A closure that is invoked upon message acknowledgement. |
| 102 | + private static func setDeliveryReportCallback( |
| 103 | + configPointer: OpaquePointer, |
| 104 | + capturedClosures: CapturedClosures, |
| 105 | + _ deliveryReportCallback: @escaping RDKafkaConfig.CapturedClosures.DeliveryReportClosure |
| 106 | + ) { |
| 107 | + capturedClosures.deliveryReportClosure = deliveryReportCallback |
| 108 | + |
83 | 109 | // Create a C closure that calls the captured closure
|
84 | 110 | let callbackWrapper: (
|
85 | 111 | @convention(c) (OpaquePointer?, UnsafePointer<rd_kafka_message_t>?, UnsafeMutableRawPointer?) -> Void
|
86 | 112 | ) = { _, messagePointer, opaquePointer in
|
87 | 113 | guard let opaquePointer = opaquePointer else {
|
88 |
| - fatalError("Could not resolve reference to KafkaProducer instance") |
| 114 | + fatalError("Could not resolve reference to CapturedClosures") |
89 | 115 | }
|
90 |
| - let opaque = Unmanaged<CapturedClosure>.fromOpaque(opaquePointer).takeUnretainedValue() |
| 116 | + let closures = Unmanaged<CapturedClosures>.fromOpaque(opaquePointer).takeUnretainedValue() |
91 | 117 |
|
92 |
| - let actualCallback = opaque.closure |
| 118 | + guard let actualCallback = closures.deliveryReportClosure else { |
| 119 | + fatalError("Delivery report callback is set, but user closure is not defined") |
| 120 | + } |
93 | 121 | let messageResult = Self.convertMessageToAcknowledgementResult(messagePointer: messagePointer)
|
94 | 122 | actualCallback(messageResult)
|
95 |
| - |
96 |
| - // The messagePointer is automatically destroyed by librdkafka |
97 |
| - // For safety reasons, we only use it inside of this callback |
98 | 123 | }
|
99 | 124 |
|
100 | 125 | rd_kafka_conf_set_dr_msg_cb(
|
101 | 126 | configPointer,
|
102 | 127 | callbackWrapper
|
103 | 128 | )
|
| 129 | + } |
| 130 | + |
| 131 | + /// A Swift wrapper for `rd_kafka_conf_set_log_cb`. |
| 132 | + /// Defines a function that is called upon every log and redirects output to ``logger``. |
| 133 | + /// - Parameter configPointer: An `OpaquePointer` pointing to the `rd_kafka_conf_t` object in memory. |
| 134 | + /// - Parameter logger: Logger instance |
| 135 | + private static func setLoggingCallback( |
| 136 | + configPointer: OpaquePointer, |
| 137 | + capturedClosures: CapturedClosures, |
| 138 | + logger: Logger |
| 139 | + ) { |
| 140 | + let loggingClosure: RDKafkaConfig.CapturedClosures.LoggingClosure = { level, fac, buf in |
| 141 | + // Mapping according to https://en.wikipedia.org/wiki/Syslog |
| 142 | + switch level { |
| 143 | + case 0 ... 2: /* Emergency, Alert, Critical */ |
| 144 | + logger.critical(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 145 | + case 3: /* Error */ |
| 146 | + logger.error(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 147 | + case 4: /* Warning */ |
| 148 | + logger.warning(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 149 | + case 5: /* Notice */ |
| 150 | + logger.notice(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 151 | + case 6: /* Informational */ |
| 152 | + logger.info(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 153 | + default: /* Debug */ |
| 154 | + logger.debug(Logger.Message(stringLiteral: String(cString: buf)), source: String(cString: fac)) |
| 155 | + } |
| 156 | + } |
| 157 | + capturedClosures.loggingClosure = loggingClosure |
| 158 | + |
| 159 | + let loggingWrapper: ( |
| 160 | + @convention(c) (OpaquePointer?, Int32, UnsafePointer<CChar>?, UnsafePointer<CChar>?) -> Void |
| 161 | + ) = { rkKafkaT, level, fac, buf in |
| 162 | + guard let fac, let buf else { |
| 163 | + return |
| 164 | + } |
| 165 | + |
| 166 | + guard let opaquePointer = rd_kafka_opaque(rkKafkaT) else { |
| 167 | + fatalError("Could not resolve reference to CapturedClosures") |
| 168 | + } |
| 169 | + let opaque = Unmanaged<CapturedClosures>.fromOpaque(opaquePointer).takeUnretainedValue() |
| 170 | + |
| 171 | + guard let closure = opaque.loggingClosure else { |
| 172 | + fatalError("Could not resolve logger instance") |
| 173 | + } |
| 174 | + closure(level, fac, buf) |
| 175 | + } |
104 | 176 |
|
105 |
| - return capturedClosure |
| 177 | + rd_kafka_conf_set_log_cb( |
| 178 | + configPointer, |
| 179 | + loggingWrapper |
| 180 | + ) |
106 | 181 | }
|
107 | 182 |
|
108 | 183 | /// Convert an unsafe`rd_kafka_message_t` object to a safe ``KafkaAcknowledgementResult``.
|
|
0 commit comments