Skip to content

Options for client-side apps #212

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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