feat: observability features for MAUI and Android#421
feat: observability features for MAUI and Android#421abelonogov-ld wants to merge 71 commits intomainfrom
Conversation
* main: feat: Optional Jet Compose (#402) feat: Android Incremental Image Diff compression (#390) chore: add CLAUDE.md (#398) chore: release main (#400) fix: correct react native session replay build step (#399) chore: release main (#396) fix: Android span e2e tests (#397) fix: improve network response capture (#379) # Conflicts: # sdk/@launchdarkly/mobile-dotnet/.vscode/tasks.json
* main: chore: release main (#401)
(cherry picked from commit 9901600)
* andrey/hooks: comment identify stuff fat working working can launch
sdk/@launchdarkly/mobile-dotnet/observability/observe/plugin/ObservabilityHook.cs
Show resolved
Hide resolved
This reverts commit 65d1f0d.
sdk/@launchdarkly/mobile-dotnet/observability/Directory.Build.props
Outdated
Show resolved
Hide resolved
sdk/@launchdarkly/mobile-dotnet/observability/bridge/DictionaryTypeConverters.cs
Show resolved
Hide resolved
This reverts commit 5145c71.
...vability-android/lib/src/main/kotlin/com/launchdarkly/observability/network/GraphQLClient.kt
Outdated
Show resolved
Hide resolved
...ib/src/main/kotlin/com/launchdarkly/observability/replay/exporter/SessionReplayApiService.kt
Outdated
Show resolved
Hide resolved
| .anonymous(true) | ||
| .build() | ||
|
|
||
| //LDClient.get().identify(anonContext) |
There was a problem hiding this comment.
Unused variable and commented-out code committed
Low Severity
The anonContext variable is created but never used because the LDClient.get().identify(anonContext) call on line 96 is commented out. This looks like leftover debugging or experimentation code that was accidentally included in the commit.
| } | ||
|
|
||
| otelRUM = rumBuilder.build() | ||
| sessionManager = capturedSessionManager!! |
There was a problem hiding this comment.
Force-unwrap of captured session manager is fragile
Medium Severity
capturedSessionManager!! will throw a NullPointerException if the ld-session-manager-bridge instrumentation's install callback is never invoked during rumBuilder.build(). This relies on OpenTelemetryRum synchronously installing all instrumentations during build(), which is an internal implementation detail of the OTel Android library that could change.
| #elif ANDROID | ||
| var bridge = new LDObserveAndroid.ObservabilityBridge(); | ||
| var map = DictionaryTypeConverters.ToJavaDictionary(attributes); | ||
| bridge.RecordLog(message, severity, map); |
There was a problem hiding this comment.
New bridge instance allocated per API call
Low Severity
Every Android method (RecordLog, RecordError, RecordMetric, RecordCount, RecordIncr, RecordHistogram, RecordUpDownCounter) creates a new LDObserveAndroid.ObservabilityBridge() on each call. This allocates a new JVM object via JNI for every telemetry event, which adds unnecessary overhead—especially for high-frequency metrics or logs. A single cached instance would suffice.
Additional Locations (2)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| // put wake up in the try/catch do not break buffering logic | ||
| logger.error(e) | ||
| } | ||
| } |
There was a problem hiding this comment.
Wake-up state inconsistent with export rollback on failure
Medium Severity
The wakeUpEvents method calls nextPayloadId() and sets shouldWakeUpSession = false inside its own try-catch, but these side effects are not part of the snapshot/restore logic in the outer catch block. If wake-up events are successfully sent but a subsequent pushPayload for a different session in the same batch throws, payloadIdCounter is rolled back to the snapshot while shouldWakeUpSession remains false. On retry, the payload ID used by the already-sent wake-up payload may be reused for a regular payload, and no wake-up will be re-sent.
| LDObserveBridge.RecordLog(message, severity, dict); | ||
| #elif ANDROID | ||
| var bridge = new LDObserveAndroid.ObservabilityBridge(); | ||
| var map = DictionaryTypeConverters.ToJavaDictionary(attributes); |
There was a problem hiding this comment.
I have been trying out the observability plugin locally in Android using SessionReplay V0.4.1 (https://www.nuget.org/packages/LaunchDarkly.SessionReplay) and the attribute I am passing does not seem to show up in LaunchDarkly in the log entry. Could I be doing something wrong or looking in the wrong place? I would have expected to see "anonymous" show up in the log entry somewhere:
new Dictionary<string, object?>
{
{ "anonymous", context.UserId.IsBlank() }
}
There was a problem hiding this comment.
Let me know if an issue should be opened for this instead. I noticed that there was an open PR for this, so I thought it would be most convenient to add here.
There was a problem hiding this comment.
@cam-line we have an issue of processing Boolean values on the backend. Thanks for filling. It should be fixed soon.
There was a problem hiding this comment.
Backend fixed and SessionReplay V0.5.0 published


Summary
How did you test this change?
Are there any deployment considerations?
Note
Medium Risk
Touches cross-platform native bridge layers and session replay export/identify flow, so regressions could impact telemetry capture and replay attribution. Changes are mostly additive but span Android SDK internals, MAUI bindings, and sample/e2e apps.
Overview
Adds end-to-end observability recording support for MAUI: the Android/iOS native bridges now expose
LDObserveAPIs for logs, errors, and multiple metric types, plus updated bindings/exports (including a separate session replay hook proxy) so C# hooks can forwardidentifyevents into native replay.Refactors Android session replay integration to initialize via plugin lifecycle (removing the instrumentation-contributor pattern), plumbs the OTel
SessionManagerthroughObservabilityContext, and updates replay exporting to include an apptitle, setclient_id, attach identify events even when session id is missing, and emit one-time "wake-up" events for resumed playback.Updates docs and packaging metadata (README rebrand to Observability, dependency/version bumps), and extends the MAUI sample/e2e apps with a new Dialogs test page plus small UI tweaks and benchmark generator updates.
Written by Cursor Bugbot for commit 14fb38c. This will update automatically on new commits. Configure here.