[HEL-4907] | Flutter SDK log levels#88
Conversation
📝 WalkthroughWalkthroughAdds a runtime setLogLevel API that forwards a Dart HeliumLogLevel to native (Android/iOS) plugins, updates Helium.config.logLevel, and makes the Android BridgingLogger conditionally forward Flutter log events based on the configured level. ChangesSet Log Level Flow
Sequence Diagram(s)sequenceDiagram
participant Dart as Flutter App (Dart)
participant Channel as MethodChannel
participant Plugin as Platform Plugin (Kotlin/Swift)
participant Core as Native Helium SDK (Helium.config / BridgingLogger)
Dart->>Channel: invoke setLogLevel(level.rawValue)
Channel->>Plugin: onMethodCall("setLogLevel", value)
Plugin->>Plugin: map value -> HeliumLogLevel (validate)
alt valid
Plugin->>Core: Helium.config.logLevel = mappedLevel
Plugin-->>Channel: success (null)
else invalid
Plugin-->>Channel: error("BAD_ARGS", "Invalid log level")
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumFlutterPlugin.kt`:
- Line 443: Replace the deprecated HeliumLogLevel.values() call with
HeliumLogLevel.entries and keep the same lookup logic; specifically update the
expression in HeliumFlutterPlugin.kt that uses
HeliumLogLevel.values().firstOrNull { it.rawValue == value } to use
HeliumLogLevel.entries.firstOrNull { it.rawValue == value } so it compiles
cleanly on Kotlin 1.9+.
- Around line 440-451: The setLogLevel handler updates Helium.config.logLevel
but does not update the already-constructed BridgingLogger instance, so the
bridging layer ignores runtime changes; to fix, make the BridgingLogger a
mutable instance field on the plugin (create a private var
bridgingLoggerInstance: BridgingLogger? set in setupCore where you currently do
bridgingLogger.logLevel = Helium.config.logLevel), and in the "setLogLevel"
handler after assigning Helium.config.logLevel = level also set
bridgingLoggerInstance?.logLevel = level; additionally, when you currently reset
Helium.config.logger = HeliumLogger.Stdout in onDetachedFromEngine and the
resetHelium handler, set bridgingLoggerInstance = null (and/or reassign
Helium.config.logger to bridgingLoggerInstance when reinitializing in setupCore)
so the instance remains the single source of truth for log filtering.
In `@packages/helium_flutter/lib/core/helium_flutter_method_channel.dart`:
- Around line 683-689: The setLogLevel method calls methodChannel.invokeMethod
which returns a Future but currently has no error handling; wrap the invoke call
in defensive async error handling (either await inside a try/catch or attach
.catchError) inside the setLogLevel implementation to handle PlatformException
(e.g., BAD_ARGS) and log/ignore it safely; locate the setLogLevel method in the
HeliumFlutterMethodChannel class and update the invocation of
setLogLevelMethodName with HeliumLogLevel.rawValue to catch and handle any
exceptions rather than letting them propagate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 061e3775-0ca9-4f61-9883-63c038824729
📒 Files selected for processing (10)
packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/BridgingLogger.ktpackages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumFlutterPlugin.ktpackages/helium_flutter/ios/helium_flutter/Sources/helium_flutter/HeliumFlutterPlugin.swiftpackages/helium_flutter/lib/core/const/contants.dartpackages/helium_flutter/lib/core/helium_flutter_method_channel.dartpackages/helium_flutter/lib/core/helium_flutter_platform.dartpackages/helium_flutter/lib/helium_flutter.dartpackages/helium_flutter/lib/types/helium_log_level.dartpackages/helium_flutter/test/helium_flutter_test.dartpackages/helium_stripe/test/helium_stripe_test.dart
| "setLogLevel" -> { | ||
| val rawValue = (call.arguments as? Number)?.toInt() | ||
| val level = rawValue?.let { value -> | ||
| HeliumLogLevel.values().firstOrNull { it.rawValue == value } | ||
| } | ||
| if (level == null) { | ||
| result.error("BAD_ARGS", "Invalid log level", null) | ||
| } else { | ||
| Helium.config.logLevel = level | ||
| result.success(null) | ||
| } | ||
| } |
There was a problem hiding this comment.
setLogLevel after initialization doesn't update the BridgingLogger's filtering level on Android.
Helium.config.logLevel = level (line 448) updates the config, but BridgingLogger.logLevel — which controls the >= guards in e/w/i/d/v — is a copied field set once in setupCore (line 587: bridgingLogger.logLevel = Helium.config.logLevel). Subsequent changes to Helium.config.logLevel don't propagate to the already-constructed BridgingLogger, so any setLogLevel call after initialization is silently ignored by the bridging layer on Android. This contradicts the public docstring: "Safe to call before or after initialize; the level is preserved across re-initializations."
The fix below adds an instance variable for the logger so it can be updated at any time:
🐛 Proposed fix
private var nativeViewFactory: HeliumNativeViewFactory? = null
+private var bridgingLogger: BridgingLogger? = nullIn setupCore (around line 586):
-val bridgingLogger = BridgingLogger(channel)
-bridgingLogger.logLevel = Helium.config.logLevel
-Helium.config.logger = bridgingLogger
+val logger = BridgingLogger(channel)
+logger.logLevel = Helium.config.logLevel
+Helium.config.logger = logger
+bridgingLogger = loggerIn the setLogLevel handler (line 448):
Helium.config.logLevel = level
+bridgingLogger?.logLevel = level
result.success(null)In both onDetachedFromEngine and the resetHelium handler where Helium.config.logger = HeliumLogger.Stdout is set:
Helium.config.logger = HeliumLogger.Stdout
+bridgingLogger = null📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "setLogLevel" -> { | |
| val rawValue = (call.arguments as? Number)?.toInt() | |
| val level = rawValue?.let { value -> | |
| HeliumLogLevel.values().firstOrNull { it.rawValue == value } | |
| } | |
| if (level == null) { | |
| result.error("BAD_ARGS", "Invalid log level", null) | |
| } else { | |
| Helium.config.logLevel = level | |
| result.success(null) | |
| } | |
| } | |
| "setLogLevel" -> { | |
| val rawValue = (call.arguments as? Number)?.toInt() | |
| val level = rawValue?.let { value -> | |
| HeliumLogLevel.values().firstOrNull { it.rawValue == value } | |
| } | |
| if (level == null) { | |
| result.error("BAD_ARGS", "Invalid log level", null) | |
| } else { | |
| Helium.config.logLevel = level | |
| bridgingLogger?.logLevel = level | |
| result.success(null) | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumFlutterPlugin.kt`
around lines 440 - 451, The setLogLevel handler updates Helium.config.logLevel
but does not update the already-constructed BridgingLogger instance, so the
bridging layer ignores runtime changes; to fix, make the BridgingLogger a
mutable instance field on the plugin (create a private var
bridgingLoggerInstance: BridgingLogger? set in setupCore where you currently do
bridgingLogger.logLevel = Helium.config.logLevel), and in the "setLogLevel"
handler after assigning Helium.config.logLevel = level also set
bridgingLoggerInstance?.logLevel = level; additionally, when you currently reset
Helium.config.logger = HeliumLogger.Stdout in onDetachedFromEngine and the
resetHelium handler, set bridgingLoggerInstance = null (and/or reassign
Helium.config.logger to bridgingLoggerInstance when reinitializing in setupCore)
so the instance remains the single source of truth for log filtering.
There was a problem hiding this comment.
Are you sure?
The native HeliumSdkConfig.logLevel setter writes through to logger.logLevel, which is the live BridgingLogger after setupCore. The line-587 copy is just initial sync; runtime updates flow through the setter.
There was a problem hiding this comment.
🧩 Analysis chain
The web search didn't find specific Helium paywall SDK internals. Let me look at the actual BridgingLogger.kt file to check whether the logLevel property is read directly from Helium.config.logLevel at call time (live reference) or stored as a copy.
🏁 Script executed:
#!/bin/bash
# Find and display BridgingLogger.kt
fd BridgingLogger.kt --exec cat {}
Length of output: 2202
🏁 Script executed:
#!/bin/bash
# Look for Helium SDK dependency declarations (Gradle files)
fd "build.gradle" --exec grep -l "helium\|tryhelium" {} 2>/dev/null
# Also check settings.gradle / pubspec files
fd "build.gradle.kts" --exec grep -l "helium\|tryhelium" {} 2>/dev/null
fd "pubspec.yaml" --exec cat {} 2>/dev/null | grep -A2 -i helium
# Try to find any AAR or SDK source in the repo
fd -e aar 2>/dev/null | head -20
Length of output: 2000
🏁 Script executed:
#!/bin/bash
# Check the Android build.gradle for the Helium SDK dependency and version
cat ./packages/helium_flutter/android/build.gradle
# Check if there's any Gradle cache or downloaded AAR we can inspect for the config setter
find /root -name "*.kt" -path "*tryhelium*" 2>/dev/null | head -20
find /root -name "HeliumSdkConfig*" 2>/dev/null | head -10
find /root -name "*.aar" -path "*helium*" 2>/dev/null | head -10
Length of output: 1571
🏁 Script executed:
#!/bin/bash
# Try to find the SDK in Gradle cache (common locations)
find /root/.gradle -name "*.jar" -path "*tryhelium*" 2>/dev/null | head -10
find /root/.gradle -name "*.aar" -path "*tryhelium*" 2>/dev/null | head -10
find /home -name "*.jar" -path "*tryhelium*" 2>/dev/null | head -10
# Also check ~/.m2
find ~/.m2 -name "*tryhelium*" 2>/dev/null | head -10
# Try downloading the AAR and inspecting it
# The dependency is com.tryhelium.paywall:core:4.4.1
# Try to locate it via mvn or curl from maven central / jitpack
curl -s -L "https://repo1.maven.org/maven2/com/tryhelium/paywall/core/4.4.1/core-4.4.1-sources.jar" -o /tmp/helium-sources.jar 2>/dev/null && echo "Downloaded from Maven Central" || echo "Not on Maven Central"
Length of output: 30
🏁 Script executed:
#!/bin/bash
# Extract and inspect the sources jar for HeliumSdkConfig
cd /tmp
jar -xf helium-sources.jar 2>/dev/null || unzip -o helium-sources.jar -d helium-sources 2>/dev/null
echo "--- Extracted files ---"
find /tmp/helium-sources -name "*.kt" 2>/dev/null | grep -i "config\|Config" | head -20
# Look specifically for HeliumSdkConfig or HeliumConfig
find /tmp/helium-sources -name "*.kt" 2>/dev/null | xargs grep -l "logLevel" 2>/dev/null | head -10
Length of output: 19377
🏁 Script executed:
#!/bin/bash
# Read HeliumSdkConfig.kt to inspect the logLevel setter
cat /tmp/helium-sources/com/tryhelium/paywall/core/HeliumSdkConfig.kt
echo "---"
# Also check HeliumLogger interface
cat /tmp/helium-sources/com/tryhelium/paywall/core/logger/HeliumLogger.kt
echo "---"
cat /tmp/helium-sources/com/tryhelium/paywall/core/logger/HeliumLogLevel.kt
Length of output: 8772
@koyinusa, you're absolutely right — I apologize for the incorrect review comment. I've now confirmed this directly from the SDK source (com.tryhelium.paywall:core:4.4.1, HeliumSdkConfig.kt):
override var logLevel: HeliumLogLevel
get() = _logLevelOverride ?: logger.logLevel
set(value) {
_logLevelOverride = value
logger.logLevel = value // ← writes through to the live logger
}The logLevel setter explicitly propagates the new value to the registered logger.logLevel. Since BridgingLogger is the registered logger after setupCore, calling Helium.config.logLevel = level in the setLogLevel handler already updates BridgingLogger.logLevel automatically. No extra instance variable or manual propagation is needed. The code is correct as-is.
✏️ Learnings added
Learnt from: koyinusa
Repo: cloudcaptainai/helium_flutter PR: 88
File: packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumFlutterPlugin.kt:440-451
Timestamp: 2026-05-04T16:58:28.384Z
Learning: In the Helium Android SDK (com.tryhelium.paywall:core), `HeliumSdkConfig.logLevel`'s setter writes through to `logger.logLevel` automatically (i.e., `logger.logLevel = value` is called inside the setter). This means that after `setupCore` registers a `BridgingLogger` via `Helium.config.logger = bridgingLogger`, any subsequent call to `Helium.config.logLevel = level` (e.g., in the `setLogLevel` method channel handler in `HeliumFlutterPlugin.kt`) will also update `BridgingLogger.logLevel` automatically. No separate instance variable or manual propagation to the BridgingLogger is needed.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: koyinusa
Repo: cloudcaptainai/helium_flutter PR: 80
File: packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumFlutterPlugin.kt:543-550
Timestamp: 2026-03-30T11:47:09.128Z
Learning: When using the Helium Android SDK in this plugin, prefer calling `Helium.activityTracker?.getCurrentActivity()` directly. Per the SDK’s contract, it already returns a valid Activity that is non-finishing because it performs internal lifecycle/finishing checks, so an additional guard like `.takeIf { !it.isFinishing }` is typically redundant. Only add extra finishing/lifecycle checks if you have evidence that you’re not going through `getCurrentActivity()` (e.g., using a raw `Activity` reference).
Learnt from: koyinusa
Repo: cloudcaptainai/helium_flutter PR: 87
File: packages/helium_flutter/android/src/main/kotlin/com/helium/helium_flutter/HeliumNativeView.kt:79-85
Timestamp: 2026-05-04T12:37:59.512Z
Learning: In the helium_flutter Android plugin (Kotlin), use the correct paywall API based on presentation type: for the embedded/inline paywall view (`HeliumPaywallView`, e.g., calls made on a `HeliumPaywallView` instance), the correct method is `loadPaywall(trigger, navigationDispatcher, customPaywallTraits)`. Do NOT treat `HeliumPaywallView.loadPaywall(...)` as incorrect. `presentPaywall(...)` is a different API on the top-level `Helium` object and is intended for modal paywall presentation only.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/helium_flutter/lib/helium_flutter.dart`:
- Around line 370-371: The public wrapper setLogLevel should not allow
synchronous exceptions to bubble up; change the one-line forwarding in
setLogLevel(HeliumLogLevel level) to call
HeliumFlutterPlatform.instance.setLogLevel(level) inside a try/catch and handle
failures defensively (catch (e, s) and log the error via a safe logger such as
debugPrint or FlutterError.reportError) so the SDK surface won’t propagate
unexpected synchronous exceptions from
HeliumFlutterPlatform.instance.setLogLevel.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d0cc2cbb-e536-4150-9af0-b4e856b5b6c6
📒 Files selected for processing (2)
packages/helium_flutter/lib/helium_flutter.dartpackages/helium_flutter/lib/types/helium_log_level.dart
|
Promptless prepared a documentation update related to this change. Triggered by PR #88 in helium_flutter Documented the new |
Note
Medium Risk
Touches platform-channel plumbing and native initialization/log forwarding behavior on both Android and iOS; mistakes could cause missing logs or runtime method-call errors but no auth/data-path logic changes.
Overview
Adds a new Flutter API
setLogLevel(HeliumLogLevel)(with shared enum + method-channel constant) and wires it through the platform interface and method-channel implementation.Implements native handlers for
setLogLevelon Android and iOS to updateHelium.config.logLevel, and updates Android’sBridgingLoggerto filter forwarded log events by the configured level while preserving the current level when swapping in the bridging logger duringsetupCore/re-init.Updates tests/mocks in
helium_flutterandhelium_stripeto include the new platform method.Reviewed by Cursor Bugbot for commit a1cce9c. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Tests