Repository: Infomaniak/multiplatform-SwissTransfer
Pull request #202: feat: Error Report interface
Author: login: adrien-coye
Created: 2025-04-14T12:04:33Z | Merged: 2025-04-23T10:34:09Z

#### Abstract

A new interface to abstract `Error reporting` was added.

Now, each OS will be able to pass to `SwissTransferInjection` a `CrashReportInterface` to add breadcrumbs and capture errors within the KMP code.

This approach in simple, yet allows to leverage native code of each platform.


Diff:
diff --git a/STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt b/STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
new file mode 100644
index 00000000..112c6f77
--- /dev/null
+++ b/STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
@@ -0,0 +1,77 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param data Optional additional data providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        data: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param data Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param category An optional string to identify or categorize the provided event.
+     */
+    fun capture(
+        error: Throwable,
+        data: Map<String, Any>? = null,
+        category: String? = null
+    )
+
+    /**
+     * Captures a custom message and reports it to the crash reporting system with optional context,
+     * severity level, and additional metadata.
+     *
+     * @param message The custom message to be reported (e.g., an error message or event description).
+     * @param data Optional contextual data that provides additional information about the environment
+     *                or state when the message was logged.
+     * @param category An optional string to categorize or identify the provided event.
+     * @param level The severity level of the message (e.g., `info`, `warning`, `error`).
+     */
+    fun capture(
+        message: String,
+        data: Map<String, Any>? = null,
+        category: String? = null,
+        level: CrashReportLevel? = null
+    )
+}
+
diff --git a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SwissTransferInjection.kt b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SwissTransferInjection.kt
index f303776b..e4294c38 100644
--- a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SwissTransferInjection.kt
+++ b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SwissTransferInjection.kt
@@ -17,6 +17,7 @@
  */
 package com.infomaniak.multiplatform_swisstransfer
 
+import com.infomaniak.multiplatform_swisstransfer.common.interfaces.CrashReportInterface
 import com.infomaniak.multiplatform_swisstransfer.common.utils.ApiEnvironment
 import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider
 import com.infomaniak.multiplatform_swisstransfer.database.controllers.*
@@ -37,6 +38,7 @@ import com.infomaniak.multiplatform_swisstransfer.utils.EmailLanguageUtils
  * @property environment Customize client api base url, for example [ApiEnvironment.Prod]
  * @property userAgent Customize client api userAgent.
  * @property databaseRootDirectory Customize root directory for realm, eg. iOS app group container.
+ * @property crashReport A crash report interface used to report errors and add breadcrumbs.
  * @property transferManager A manager used to orchestrate transfer operations.
  * @property appSettingsManager A manager used to orchestrate AppSettings operations.
  * @property accountManager A manager used to orchestrate Accounts operations.
@@ -49,6 +51,7 @@ class SwissTransferInjection(
     private val environment: ApiEnvironment,
     private val userAgent: String,
     private val databaseRootDirectory: String? = null,
+    private val crashReport: CrashReportInterface,
 ) {
 
     private val realmProvider by lazy { RealmProvider(databaseRootDirectory) }


Reviews:
user: login: LouisCAD
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T12:45:48Z
user: login: adrien-coye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:52:44Z
user: login: KevinBoulongne
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T09:22:05Z
user: login: KevinBoulongne
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T09:26:11Z
user: login: KevinBoulongne
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T09:41:46Z
user: login: LouisCAD
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T10:54:57Z
user: login: adrien-coye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T11:24:33Z
user: login: adrien-coye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T12:10:35Z
user: login: adrien-coye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T12:10:44Z
user: login: adrien-coye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T12:22:23Z
user: login: KevinBoulongne
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T09:44:51Z
user: login: LouisCAD
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T15:01:40Z

Review comments:
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: LouisCAD
body: I'm afraid this would be not intuitive enough to actually be used.

I think it'd be simpler to just use [kermit](https://github.com/touchlab/Kermit) [[website here](https://kermit.touchlab.co/)] (or make our own wrapper if really needed), that offers a straightforward API, and allows us to plug any reporting system.

It's also not directly tied to Sentry, which avoid tight-coupling with it, and gives us freedom as to how to report the issues.
created_at: 2025-04-14T12:45:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: adrien-coye
body: I'm not sure the `intuitive` argument stands, this has three functions known and used similarly by our devs today (at least on iOS, I figured it would look the same on Android) versus a new external library we do not use currently. To me, this _is_ the dead simple solution.

> […] directly tied to Sentry

It is not linked to sentry except by name, it has no dependency to it. 
The idea was to start the discussion with a simple interface.
I'll remove the `Sentry` name. I'm open to naming feedback and conventions.

Furthermore, I'm not a fan of adding an external library to perform a simple task we can abstract and hook into the existing native codebase.

I'm open to discuss this further, but I think this approach was validated by a lead in the first place. 
Check with @PhilippeWeidmann when he comes back.
created_at: 2025-04-22T06:52:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: KevinBoulongne
body: We are trying as much as possible to not add external libraries when we can do without it.
This approach was indeed discussed & validated in a meeting.
created_at: 2025-04-22T09:22:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param metadata Optional additional metadata providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param context Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param contextKey An optional key to identify or categorize the provided context.
+     * @param extras Optional additional metadata to include in the report for debugging purposes.
+     */
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
user: login: KevinBoulongne
body: `context` already is a strong concept in Android, which has nothing to do with the subject we're talking about here.
I'd really prefer another name.

In Sentry breadcrumbs, there's already a concept to handle this, and it's named `data`.
On a Sentry breadcrumb, we can simply add some data :
<img width="630" alt="image" src="https://github.com/user-attachments/assets/b67af87f-adac-4dd8-9316-d6da0529dddd" />

created_at: 2025-04-22T09:26:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param metadata Optional additional metadata providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param context Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param contextKey An optional key to identify or categorize the provided context.
+     * @param extras Optional additional metadata to include in the report for debugging purposes.
+     */
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
user: login: KevinBoulongne
body: Why do we have 2 maps for data ?
The `context` one and the `extras` one.
created_at: 2025-04-22T09:41:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: LouisCAD
body: Even if it doesn't contain the name "Sentry", it seems to me that it's the same API, which is much less standard than loggers that support throwables/exceptions.

Do we really need this API in this KMP module, or would a relatively basic logger do?

About your argument on libraries, I understand the feeling, but I don't understand the rationale, for these reasons:
1. it's far from an herculean effort to swap it, if the API isn't too complex, the "Replace in Files" (cmd+shift+R) IDE action can help a lot in that regard.
2. Kermit is from Touchlab, the same people behind SKIE, which we heavily rely on, and (unlike SKIE) Kermit is not a very complex library to maintain, if we ever needed to fork it.
3. In the end binary, it makes zero difference to have some code be a library, or not, it's just compiled code. I think Kermit is minimal, as far as I know, and perfectly suited for our needs too, so I don't think we'd end up with any significant amount of bloat in the app.

That said, I took another look at the code, and if we actually agree and stick to our own conventions, and if Sentry surfaces it nicely, I think the `category` parameter would be useful to help sort things out.
created_at: 2025-04-22T10:54:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param metadata Optional additional metadata providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param context Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param contextKey An optional key to identify or categorize the provided context.
+     * @param extras Optional additional metadata to include in the report for debugging purposes.
+     */
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
user: login: adrien-coye
body: I mapped Sentry's iOS API to do this interface, where both exists. 
Since the KMP side is consuming this API, if you do not need extra, I'll simply remove it.

<img width="786" alt="Screenshot 2025-04-22 at 13 21 38" src="https://github.com/user-attachments/assets/30428801-27be-4b9c-b89f-01ee049a1686" />
<img width="671" alt="Screenshot 2025-04-22 at 13 21 10" src="https://github.com/user-attachments/assets/949da484-93b4-48a7-a5e7-26f9d3509953" />

created_at: 2025-04-22T11:24:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param metadata Optional additional metadata providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param context Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param contextKey An optional key to identify or categorize the provided context.
+     * @param extras Optional additional metadata to include in the report for debugging purposes.
+     */
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
user: login: adrien-coye
body: Okay I kept a map for data, this way you can pass as much key/value as you want for each breadcrumb.
created_at: 2025-04-22T12:10:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class CrashReportLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface CrashReportInterface {
+
+    /**
+     * Adds a breadcrumb to the crash reporting system to provide contextual information
+     * leading up to a potential crash.
+     *
+     * @param message A descriptive message for the breadcrumb, explaining the event or action.
+     * @param category A category string to group related breadcrumbs (e.g., "UI", "Network").
+     * @param level The severity level of the breadcrumb (e.g., info, warning, error).
+     * @param metadata Optional additional metadata providing more context about the event.
+     */
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: CrashReportLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    /**
+     * Captures and reports an error to the crash reporting system with optional context
+     * and additional metadata.
+     *
+     * @param error The [Throwable] to be reported.
+     * @param context Optional contextual data to provide more insight into the environment
+     *                or state when the error occurred.
+     * @param contextKey An optional key to identify or categorize the provided context.
+     * @param extras Optional additional metadata to include in the report for debugging purposes.
+     */
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/CrashReportInterface.kt
user: login: adrien-coye
body: done.
created_at: 2025-04-22T12:10:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: adrien-coye
body: Thanks for the feedback @LouisCAD 
It's not strictly the same API, here we take care of errors and breadcrumbs, not an abstract logger that does much more.

1. Swapping it right now is not an issue, later it can be. I think Realm is a good example of a hassle for us.
2. Maintaining was not an issue I raised.
3. It makes a difference at least in binary weight, something we track on iOS. We take pride and effort in keeping it low for platform specific reasons. This issue probably exists also on Android. Even if the library is light, it must be several orders of magnitudes heavier than a simple interface.

Happy to hear `category` works for you. 👍 
created_at: 2025-04-22T12:22:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+/*
+ * Infomaniak SwissTransfer - Multiplatform
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.infomaniak.multiplatform_swisstransfer.common.interfaces
+
+public enum class SentryLevel {
+    DEBUG,
+    INFO,
+    WARNING,
+    ERROR,
+    FATAL
+}
+
+public interface SentryInterface {
+    fun addBreadcrumb(
+        message: String,
+        category: String,
+        level: SentryLevel,
+        metadata: Map<String, Any>? = null
+    )
+
+    fun capture(
+        error: Throwable,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        extras: Map<String, Any>? = null
+    )
+
+    fun capture(
+        message: String,
+        context: Map<String, Any>? = null,
+        contextKey: String? = null,
+        level: SentryLevel? = null,
+        extras: Map<String, Any>? = null
+    )
path: STCommon/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/common/interfaces/SentryInterface.kt
user: login: LouisCAD
body: Just seeing this now…

It's much hard to swap a database library, compared to a rather simple and modular logging library like kermit, because of existing data that might need to be migrated, and Realm is a special beast in this area, with its specialties like "live objects" and thread-bound objects…

Plus the database is more critical to the app features than logging, so I don't think it's fair to compare anything to Realm and how a hassle it is, unless it's also storage related with a custom format.

BTW, in case you didn't know, there are existing sentry, bugsnag, and crashlytics integrations in kermit, which means it can actually bring a little less lock-in.
I'd be curious about the actual size impact of kermit (with or without the official Sentry integration) in an iOS app.

If anything, my philosophy is definitely to use libraries when they come from a trustworthy source and match the use case without adding bloat that can't be automatically minimized, given that they aren't likely to cause future incompatibility issues with lack of maintenance. In other words, it's on a per case basis, and I'm never ruling out adding libraries, it's the same as if equivalent code was written "in-house" in the end, so I'll look at the actual API and code to judge whether it's worth giving it a try.
created_at: 2025-05-07T15:01:40Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-23T09:37:13Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=Infomaniak_multiplatform-SwissTransfer&pullRequest=202)


author_association: NONE