Skip to content

Options for client-side apps #212

@zacharee

Description

@zacharee

Description

I've got a desktop JVM app as part of a Compose Multiplatform project. I'm using Bugsnag's JVM library for reporting errors, but it's pretty obvious it was built with server-sided apps in mind.

I can handle reporting sessions using the library's API, by calling startSession() when I initialize the Bugsnag client, and then using close() before the application terminates, but I haven't figured out how to associate users with sessions to enable the User Stability metric.

Describe the solution you'd like
It'd be really helpful to have (default-null/disabled) APIs in the library for enabling a "client-side mode" of some sort that could handle session and user tracking like the Android and iOS libraries, or at least a public way to add that information into the payloads sent into Bugsnag.

Describe alternatives you've considered
I checked around for third-party Bugsnag libraries for client-side Java apps but couldn't find any.

It might be possible to implement the C++ Bugsnag library, but then I'd lose Java stacktraces.

Additional context
I noticed that the logic for associating users with sessions is at least partly implemented, but I couldn't figure out how to get it to work, even when using reflection.

To add the user object into a Report, I made a CustomReport class that extends Report and overrides setSession():

class CustomReport(config: Configuration, throwable: Throwable?) : Report(config, throwable) {
    constructor(bugsnag: Bugsnag, throwable: Throwable?) : this(bugsnag.config, throwable)
    internal override fun setSession(session: Session?) {
        super.setSession(session)
        getSession()["user"] = hashMapOf("id" to BifrostSettings.Keys.bugsnagUuid())
    }
}

I use that in my notify() wrapper instead of the normal Report class, and I have a custom Exception handler that calls my notify() wrapper on unhandled Exceptions.

I also used reflection to add a BeforeSendSessionCallback to the Bugsnag client and add a user object to the session diagnostics:

val beforeSendSessionClass = Class.forName("com.bugsnag.BeforeSendSession")
val sessionPayloadClass = Class.forName("com.bugsnag.SessionPayload")
val diagnosticsClass = Class.forName("com.bugsnag.Diagnostics")
bugsnag::class.java.getDeclaredMethod("addBeforeSendSession", beforeSendSessionClass)
    .apply {
        isAccessible = true
    }.invoke(
        bugsnag,
        Proxy.newProxyInstance(
            beforeSendSessionClass.classLoader,
            arrayOf(beforeSendSessionClass)
        ) { _, _, args ->
            val payload = args[0]

            val diagnostics = sessionPayloadClass.getDeclaredField("diagnostics")
                .apply { isAccessible = true }
                .get(payload)

            val userMap = diagnosticsClass.getDeclaredField("user")
                .apply { isAccessible = true }
                .get(diagnostics) as HashMap<String, String?>

            userMap["id"] = uuid

            null
        }
    )

But this combination doesn't seem to make User Stability show up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backlogWe hope to fix this feature/bug in the future

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions