Skip to content

Commit

Permalink
[i129] provide non-fatal error handling mechanism (#4959)
Browse files Browse the repository at this point in the history
* i129] provide non-fatal error handling mechanism

* [i129] add CHANGELOG

* [i129] fix detekt

* [i129] fix detekt

* [i129] fix debugging docs

* [i129] more detailed reason of connection restart

* [i129] fix unit tests

* [i129] fix spotless
  • Loading branch information
kanat authored Sep 13, 2023
1 parent 2c33fd5 commit 742acf7
Show file tree
Hide file tree
Showing 17 changed files with 322 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
### ⬆️ Improved

### ✅ Added
- Added `ChatClientDebugger.onNonFatalErrorOccurred` to handle non-fatal errors. [#4959](https://github.com/GetStream/stream-chat-android/pull/4959)

### ⚠️ Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Debugging

:::note
*ChatClientDebugger* might be useful in investigation of issues happening on users side.
:::

To debug various flows inside the SDK, you can pass your own `ChatClientDebugger` implementation:

<Tabs>
<TabItem value="kotlin" label="Kotlin">

```kotlin
val client = ChatClient.Builder("apiKey", context)
.clientDebugger(CustomChatClientDebugger())
.build()
```
</TabItem>

<TabItem value="java" label="Java">

```java
ChatClient client = new ChatClient.Builder("apiKey", context)
.clientDebugger(new CustomChatClientDebugger())
.build()
```
</TabItem>
</Tabs>

### Debug Message Sending

To debug a message sending flow you can override `ChatClientDebugger.debugSendMessage` function and provide your own `SendMessageDebugger` implementation:

<Tabs>
<TabItem value="kotlin" label="Kotlin">

```kotlin
class CustomChatClientDebugger : ChatClientDebugger {

override fun onNonFatalErrorOccurred(
tag: String,
src: String,
desc: String,
error: ChatError
) {
// TODO: Implement your custom logic here
}

override fun debugSendMessage(
channelType: String,
channelId: String,
message: Message,
isRetrying: Boolean,
): SendMessageDebugger {
return CustomSendMessageDebugger(
channelType,
channelId,
message,
isRetrying
)
}
}
```
</TabItem>

<TabItem value="java" label="Java">

```java
class CustomChatClientDebugger implements ChatClientDebugger {

@Override
public void onNonFatalErrorOccurred(
@NonNull String tag,
@NonNull String src,
@NonNull String desc,
@NonNull ChatError error
) {
// TODO: Implement your custom logic here
}

@Override
public SendMessageDebugger debugSendMessage(
@NonNull String channelType,
@NonNull String channelId,
@NonNull Message message,
boolean isRetrying
) {
return new CustomSendMessageDebugger(
channelType,
channelId,
message,
isRetrying
);
}
}
```
</TabItem>
</Tabs>

Then in you custom `SendMessageDebugger` implementation you will be able to handle the entire message sending flow:
<Tabs>
<TabItem value="kotlin" label="Kotlin">

```kotlin
class CustomSendMessageDebugger(
private val channelType: String,
private val channelId: String,
private val message: Message,
private val isRetrying: Boolean,
) : SendMessageDebugger {

override fun onStart(message: Message) {
// Called when the message sending flow starts.
}

override fun onInterceptionStart(message: Message) {
// Called when the message interception before sending it to the API starts.
// Reasons for interception might be:
// - message attachment uploading
// - message enriching by all required information
}

override fun onInterceptionUpdate(message: Message) {
// Called when there are intermediate message data updates.
}

override fun onInterceptionStop(result: Result<Message>) {
// Called when the message interception before sending it to the API stops.
}

override fun onSendStart(message: Message) {
// Called when the message sending to the API starts.
}

override fun onSendStop(result: Result<Message>) {
// Called when the message sending to the API stops.
}

override fun onStop(result: Result<Message>) {
// Called when the message sending flow stops.
}
}
```
</TabItem>

<TabItem value="java" label="Java">

```java
public class CustomSendMessageDebugger implements SendMessageDebugger {
@NonNull
private final String channelType;
@NonNull
private final String channelId;
@NonNull
private final Message message;
@NonNull
private final boolean isRetrying;

public JavaSendMessageDebugger(
@NonNull String channelType,
@NonNull String channelId,
@NonNull Message message,
boolean isRetrying
) {
this.channelType = channelType;
this.channelId = channelId;
this.message = message;
this.isRetrying = isRetrying;
}

@Override
public void onStart(@NonNull Message message) {
// Called when the message sending flow starts.
}

@Override
public void onInterceptionStart(@NonNull Message message) {
// Called when the message interception before sending it to the API starts.
// Reasons for interception might be:
// - message attachment uploading
// - message enriching by all required information
}

@Override
public void onInterceptionUpdate(@NonNull Message message) {
// Called when there are intermediate message data updates.
}

@Override
public void onInterceptionStop(@NonNull Result<Message> result) {
// Called when the message interception before sending it to the API stops.
}

@Override
public void onSendStart(@NonNull Message message) {
// Called when the message sending to the API starts.
}

@Override
public void onSendStop(@NonNull Result<Message> result) {
// Called when the message sending to the API stops.
}

@Override
public void onStop(@NonNull Result<Message> result) {
// Called when the message sending flow stops.
}
}
```
</TabItem>
</Tabs>
33 changes: 28 additions & 5 deletions docusaurus/docs/Android/01-basics/07-debugging.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ To debug a message sending flow you can override `ChatClientDebugger.debugSendMe
<TabItem value="kotlin" label="Kotlin">

```kotlin
import io.getstream.result.Error

class CustomChatClientDebugger : ChatClientDebugger {

override fun onNonFatalErrorOccurred(
tag: String,
src: String,
desc: String,
error: Error,
) {
// TODO: Implement your custom logic here
}

override fun debugSendMessage(
channelType: String,
channelId: String,
Expand All @@ -59,21 +70,33 @@ class CustomChatClientDebugger : ChatClientDebugger {
<TabItem value="java" label="Java">

```java
class CustomChatClientDebugger : ChatClientDebugger {
import io.getstream.result.Error;

class CustomChatClientDebugger implements ChatClientDebugger {

@Override
public void debugSendMessage(
public void onNonFatalErrorOccurred(
@NonNull String tag,
@NonNull String src,
@NonNull String desc,
@NonNull Error error
) {
// TODO: Implement your custom logic here
}

@Override
public SendMessageDebugger debugSendMessage(
@NonNull String channelType,
@NonNull String channelId,
@NonNull Message message,
boolean isRetrying,
): SendMessageDebugger {
boolean isRetrying
) {
return new CustomSendMessageDebugger(
channelType,
channelId,
message,
isRetrying
)
);
}
}
```
Expand Down
2 changes: 2 additions & 0 deletions stream-chat-android-client/api/stream-chat-android-client.api
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,13 @@ public final class io/getstream/chat/android/client/clientstate/DisconnectCause$

public abstract interface class io/getstream/chat/android/client/debugger/ChatClientDebugger {
public abstract fun debugSendMessage (Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Z)Lio/getstream/chat/android/client/debugger/SendMessageDebugger;
public abstract fun onNonFatalErrorOccurred (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/result/Error;)V
}

public final class io/getstream/chat/android/client/debugger/ChatClientDebugger$DefaultImpls {
public static fun debugSendMessage (Lio/getstream/chat/android/client/debugger/ChatClientDebugger;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;Z)Lio/getstream/chat/android/client/debugger/SendMessageDebugger;
public static synthetic fun debugSendMessage$default (Lio/getstream/chat/android/client/debugger/ChatClientDebugger;Ljava/lang/String;Ljava/lang/String;Lio/getstream/chat/android/models/Message;ZILjava/lang/Object;)Lio/getstream/chat/android/client/debugger/SendMessageDebugger;
public static fun onNonFatalErrorOccurred (Lio/getstream/chat/android/client/debugger/ChatClientDebugger;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/getstream/result/Error;)V
}

public abstract interface class io/getstream/chat/android/client/debugger/SendMessageDebugger {
Expand Down
3 changes: 2 additions & 1 deletion stream-chat-android-client/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
<ID>FunctionOnlyReturningConstant:StringExtensionsKtTest.kt$StringExtensionsKtTest.Companion$fun createStreamCdnImageLinkWithoutDimensionParameters()</ID>
<ID>LongMethod:ChatClient.kt$ChatClient$private suspend fun handleEvent(event: ChatEvent)</ID>
<ID>LongMethod:ChatClientDebuggerTest.kt$ChatClientDebuggerTest$@BeforeEach fun setUp()</ID>
<ID>LongMethod:ChatSocket.kt$ChatSocket$@Suppress("ComplexMethod") private fun observeSocketStateService(): Job</ID>
<ID>LongMethod:PinnedMessagesRequest.kt$PinnedMessagesRequest.Companion$fun create( limit: Int, sort: QuerySorter&lt;Message>, pagination: PinnedMessagesPagination, ): PinnedMessagesRequest</ID>
<ID>LongParameterList:BaseChatModule.kt$BaseChatModule$( private val appContext: Context, private val clientScope: ClientScope, private val userScope: UserScope, private val config: ChatClientConfig, private val notificationsHandler: NotificationHandler, private val notificationConfig: NotificationConfig, private val fileUploader: FileUploader? = null, private val tokenManager: TokenManager = TokenManagerImpl(), private val customOkHttpClient: OkHttpClient? = null, private val lifecycle: Lifecycle, private val httpClientConfig: (OkHttpClient.Builder) -> OkHttpClient.Builder = { it }, )</ID>
<ID>LongParameterList:BaseChatModule.kt$BaseChatModule$( private val appContext: Context, private val clientScope: ClientScope, private val userScope: UserScope, private val config: ChatClientConfig, private val notificationsHandler: NotificationHandler, private val notificationConfig: NotificationConfig, private val fileUploader: FileUploader? = null, private val tokenManager: TokenManager = TokenManagerImpl(), private val customOkHttpClient: OkHttpClient? = null, private val clientDebugger: ChatClientDebugger? = null, private val lifecycle: Lifecycle, private val httpClientConfig: (OkHttpClient.Builder) -> OkHttpClient.Builder = { it }, )</ID>
<ID>MagicNumber:WaveformExtractor.kt$WaveformExtractor$127f</ID>
<ID>MagicNumber:WaveformExtractor.kt$WaveformExtractor$16</ID>
<ID>MagicNumber:WaveformExtractor.kt$WaveformExtractor$1_000_000f</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package io.getstream.chat.android.client.di
import android.content.Context
import androidx.lifecycle.Lifecycle
import io.getstream.chat.android.client.api.ChatClientConfig
import io.getstream.chat.android.client.debugger.ChatClientDebugger
import io.getstream.chat.android.client.notifications.handler.NotificationConfig
import io.getstream.chat.android.client.notifications.handler.NotificationHandler
import io.getstream.chat.android.client.parser.ChatParser
Expand All @@ -44,6 +45,7 @@ internal class ChatModule(
uploader: FileUploader?,
tokenManager: TokenManager,
customOkHttpClient: OkHttpClient?,
clientDebugger: ChatClientDebugger?,
lifecycle: Lifecycle,
) : BaseChatModule(
appContext,
Expand All @@ -55,6 +57,7 @@ internal class ChatModule(
uploader,
tokenManager,
customOkHttpClient,
clientDebugger,
lifecycle,
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3129,6 +3129,7 @@ internal constructor(
fileUploader,
tokenManager,
customOkHttpClient,
clientDebugger,
lifecycle,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,23 @@
package io.getstream.chat.android.client.debugger

import io.getstream.chat.android.models.Message
import io.getstream.result.Error

/**
* Debugs the [io.getstream.chat.android.client.ChatClient].
*/
public interface ChatClientDebugger {

/**
* Called when a non-fatal error occurs.
*
* @param tag The location where the error occurred.
* @param src The source of the error.
* @param desc The description of the error.
* @param error The error that occurred.
*/
public fun onNonFatalErrorOccurred(tag: String, src: String, desc: String, error: Error) {}

/**
* Creates an instance of [SendMessageDebugger] that allows you to debug the sending process of a message.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import io.getstream.chat.android.client.api2.endpoint.ModerationApi
import io.getstream.chat.android.client.api2.endpoint.UserApi
import io.getstream.chat.android.client.api2.endpoint.VideoCallApi
import io.getstream.chat.android.client.clientstate.UserStateService
import io.getstream.chat.android.client.debugger.ChatClientDebugger
import io.getstream.chat.android.client.logger.ChatLogLevel
import io.getstream.chat.android.client.network.NetworkStateProvider
import io.getstream.chat.android.client.notifications.ChatNotifications
Expand Down Expand Up @@ -85,6 +86,7 @@ internal open class BaseChatModule(
private val fileUploader: FileUploader? = null,
private val tokenManager: TokenManager = TokenManagerImpl(),
private val customOkHttpClient: OkHttpClient? = null,
private val clientDebugger: ChatClientDebugger? = null,
private val lifecycle: Lifecycle,
private val httpClientConfig: (OkHttpClient.Builder) -> OkHttpClient.Builder = { it },
) {
Expand Down Expand Up @@ -221,6 +223,7 @@ internal open class BaseChatModule(
userScope,
lifecycleObserver,
networkStateProvider,
clientDebugger,
)

@Suppress("RemoveExplicitTypeArguments")
Expand Down
Loading

0 comments on commit 742acf7

Please sign in to comment.