Skip to content

Commit cb9e1ba

Browse files
committed
Add and improve logging for Messages and Reactions (+2 squashed commits)
Squashed commits: [234cc2e] Add and improve logging for Messages and Reactions [6697e1e] Spec complete for Presence in line with [1]. [1] - ably/specification#200
1 parent 900386e commit cb9e1ba

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

Sources/AblyChat/DefaultMessages.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
1616
public nonisolated let featureChannel: FeatureChannel
1717
private let chatAPI: ChatAPI
1818
private let clientID: String
19+
private let logger: InternalLogger
1920

2021
// TODO: https://github.com/ably-labs/ably-chat-swift/issues/36 - Handle unsubscribing in line with CHA-M4b
2122
// UUID acts as a unique identifier for each listener/subscription. MessageSubscriptionWrapper houses the subscription and the timeserial of when it was attached or resumed.
2223
private var subscriptionPoints: [UUID: MessageSubscriptionWrapper] = [:]
2324

24-
internal nonisolated init(featureChannel: FeatureChannel, chatAPI: ChatAPI, roomID: String, clientID: String) async {
25+
internal nonisolated init(featureChannel: FeatureChannel, chatAPI: ChatAPI, roomID: String, clientID: String, logger: InternalLogger) async {
2526
self.featureChannel = featureChannel
2627
self.chatAPI = chatAPI
2728
self.roomID = roomID
2829
self.clientID = clientID
30+
self.logger = logger
2931

3032
// Implicitly handles channel events and therefore listners within this class. Alternative is to explicitly call something like `DefaultMessages.start()` which makes the SDK more cumbersome to interact with. This class is useless without kicking off this flow so I think leaving it here is suitable.
3133
// "Calls to instance method 'handleChannelEvents(roomId:)' from outside of its actor context are implicitly asynchronous" hence the `await` here.
@@ -38,6 +40,7 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
3840

3941
// (CHA-M4) Messages can be received via a subscription in realtime.
4042
internal func subscribe(bufferingPolicy: BufferingPolicy) async throws -> MessageSubscription {
43+
logger.log(message: "Subscribing to messages", level: .debug)
4144
let uuid = UUID()
4245
let timeserial = try await resolveSubscriptionStart()
4346
let messageSubscription = MessageSubscription(
@@ -162,29 +165,37 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
162165
}
163166

164167
private func handleAttach(fromResume: Bool) async throws {
168+
logger.log(message: "Handling attach", level: .debug)
165169
// Do nothing if we have resumed as there is no discontinuity in the message stream
166170
if fromResume {
171+
logger.log(message: "Channel has resumed, no need to handle attach", level: .debug)
167172
return
168173
}
169174

170175
do {
171176
let timeserialOnChannelAttach = try await timeserialOnChannelAttach()
172177

173178
for uuid in subscriptionPoints.keys {
179+
logger.log(message: "Resetting subscription point for listener: \(uuid)", level: .debug)
174180
subscriptionPoints[uuid]?.timeserial = timeserialOnChannelAttach
175181
}
176182
} catch {
183+
logger.log(message: "Error handling attach: \(error)", level: .error)
177184
throw ARTErrorInfo.create(from: error)
178185
}
179186
}
180187

181188
private func resolveSubscriptionStart() async throws -> TimeserialString {
189+
logger.log(message: "Resolving subscription start", level: .debug)
182190
// (CHA-M5a) If a subscription is added when the underlying realtime channel is ATTACHED, then the subscription point is the current channelSerial of the realtime channel.
183191
if channel.state == .attached {
184192
if let channelSerial = channel.properties.channelSerial {
193+
logger.log(message: "Channel is attached, returning channelSerial: \(channelSerial)", level: .debug)
185194
return channelSerial
186195
} else {
187-
throw ARTErrorInfo.create(withCode: 40000, status: 400, message: "channel is attached, but channelSerial is not defined")
196+
let error = ARTErrorInfo.create(withCode: 40000, status: 400, message: "channel is attached, but channelSerial is not defined")
197+
logger.log(message: "Error resolving subscription start: \(error)", level: .error)
198+
throw error
188199
}
189200
}
190201

@@ -194,12 +205,16 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
194205

195206
// Always returns the attachSerial and not the channelSerial to also serve (CHA-M5c) - If a channel leaves the ATTACHED state and then re-enters ATTACHED with resumed=false, then it must be assumed that messages have been missed. The subscription point of any subscribers must be reset to the attachSerial.
196207
private func timeserialOnChannelAttach() async throws -> TimeserialString {
208+
logger.log(message: "Resolving timeserial on channel attach", level: .debug)
197209
// If the state is already 'attached', return the attachSerial immediately
198210
if channel.state == .attached {
199211
if let attachSerial = channel.properties.attachSerial {
212+
logger.log(message: "Channel is attached, returning attachSerial: \(attachSerial)", level: .debug)
200213
return attachSerial
201214
} else {
202-
throw ARTErrorInfo.create(withCode: 40000, status: 400, message: "Channel is attached, but attachSerial is not defined")
215+
let error = ARTErrorInfo.create(withCode: 40000, status: 400, message: "Channel is attached, but attachSerial is not defined")
216+
logger.log(message: "Error resolving timeserial on channel attach: \(error)", level: .error)
217+
throw error
203218
}
204219
}
205220

@@ -217,13 +232,16 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities {
217232
case .attached:
218233
// Handle successful attachment
219234
if let attachSerial = channel.properties.attachSerial {
235+
logger.log(message: "Channel is attached, returning attachSerial: \(attachSerial)", level: .debug)
220236
nillableContinuation?.resume(returning: attachSerial)
221237
} else {
238+
logger.log(message: "Channel is attached, but attachSerial is not defined", level: .error)
222239
nillableContinuation?.resume(throwing: ARTErrorInfo.create(withCode: 40000, status: 400, message: "Channel is attached, but attachSerial is not defined"))
223240
}
224241
nillableContinuation = nil
225242
case .failed, .suspended:
226243
// TODO: Revisit as part of https://github.com/ably-labs/ably-chat-swift/issues/32
244+
logger.log(message: "Channel failed to attach", level: .error)
227245
nillableContinuation?.resume(
228246
throwing: ARTErrorInfo.create(
229247
withCode: ErrorCode.messagesAttachmentFailed.rawValue,

Sources/AblyChat/DefaultRoomReactions.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@ internal final class DefaultRoomReactions: RoomReactions, EmitsDiscontinuities {
2222
// (CHA-ER3) Ephemeral room reactions are sent to Ably via the Realtime connection via a send method.
2323
// (CHA-ER3a) Reactions are sent on the channel using a message in a particular format - see spec for format.
2424
internal func send(params: SendReactionParams) async throws {
25+
logger.log(message: "Sending reaction with params: \(params)", level: .debug)
2526
let extras = ["headers": params.headers ?? [:]] as ARTJsonCompatible
2627
channel.publish(RoomReactionEvents.reaction.rawValue, data: params.asQueryItems(), extras: extras)
2728
}
2829

2930
// (CHA-ER4) A user may subscribe to reaction events in Realtime.
3031
// (CHA-ER4a) A user may provide a listener to subscribe to reaction events. This operation must have no side-effects in relation to room or underlying status. When a realtime message with name roomReaction is received, this message is converted into a reaction object and emitted to subscribers.
3132
internal func subscribe(bufferingPolicy: BufferingPolicy) async -> Subscription<Reaction> {
33+
logger.log(message: "Subscribing to reaction events", level: .debug)
3234
let subscription = Subscription<Reaction>(bufferingPolicy: bufferingPolicy)
3335

3436
// (CHA-ER4c) Realtime events with an unknown name shall be silently discarded.
3537
channel.subscribe(RoomReactionEvents.reaction.rawValue) { [clientID, logger] message in
38+
logger.log(message: "Received roomReaction message: \(message)", level: .debug)
3639
Task {
3740
do {
3841
guard let data = message.data as? [String: Any],
@@ -65,7 +68,7 @@ internal final class DefaultRoomReactions: RoomReactions, EmitsDiscontinuities {
6568
clientID: messageClientID,
6669
isSelf: messageClientID == clientID
6770
)
68-
71+
logger.log(message: "Emitting reaction: \(reaction)", level: .debug)
6972
subscription.emit(reaction)
7073
} catch {
7174
logger.log(message: "Error processing incoming reaction message: \(error)", level: .error)

Sources/AblyChat/Room.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ internal actor DefaultRoom<LifecycleManagerFactory: RoomLifecycleManagerFactory>
9999
featureChannel: featureChannels[.messages]!,
100100
chatAPI: chatAPI,
101101
roomID: roomID,
102-
clientID: clientId
102+
clientID: clientId,
103+
logger: logger
103104
)
104105

105106
_reactions = options.reactions != nil ? await DefaultRoomReactions(

Tests/AblyChatTests/DefaultMessagesTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ struct DefaultMessagesTests {
1212
let chatAPI = ChatAPI(realtime: realtime)
1313
let channel = MockRealtimeChannel()
1414
let featureChannel = MockFeatureChannel(channel: channel)
15-
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId")
15+
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId", logger: TestLogger())
1616

1717
// Then
1818
await #expect(throws: ARTErrorInfo.create(withCode: 40000, status: 400, message: "channel is attached, but channelSerial is not defined"), performing: {
@@ -30,7 +30,7 @@ struct DefaultMessagesTests {
3030
let chatAPI = ChatAPI(realtime: realtime)
3131
let channel = MockRealtimeChannel()
3232
let featureChannel = MockFeatureChannel(channel: channel)
33-
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId")
33+
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId", logger: TestLogger())
3434

3535
// Then
3636
await #expect(throws: Never.self, performing: {
@@ -55,7 +55,7 @@ struct DefaultMessagesTests {
5555
)
5656
)
5757
let featureChannel = MockFeatureChannel(channel: channel)
58-
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId")
58+
let defaultMessages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId", logger: TestLogger())
5959
let subscription = try await defaultMessages.subscribe(bufferingPolicy: .unbounded)
6060
let expectedPaginatedResult = PaginatedResultWrapper<Message>(
6161
paginatedResponse: MockHTTPPaginatedResponse.successGetMessagesWithNoItems,
@@ -77,7 +77,7 @@ struct DefaultMessagesTests {
7777
let chatAPI = ChatAPI(realtime: realtime)
7878
let channel = MockRealtimeChannel()
7979
let featureChannel = MockFeatureChannel(channel: channel)
80-
let messages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId")
80+
let messages = await DefaultMessages(featureChannel: featureChannel, chatAPI: chatAPI, roomID: "basketball", clientID: "clientId", logger: TestLogger())
8181

8282
// When: The feature channel emits a discontinuity through `subscribeToDiscontinuities`
8383
let featureChannelDiscontinuity = ARTErrorInfo.createUnknownError() // arbitrary

0 commit comments

Comments
 (0)