Test and debug
This page shows what the SDK is doing on a test device. The grona-debug artifact adds a screen that lists the active configuration, every campaign and why it is live or not, bound slots and recent events. A logger routes SDK warnings to Logcat. The debug view works only in debuggable builds. The logger receives messages in any build you pass it to, so the example below passes it only in debuggable builds.
Open the debug view
Section titled “Open the debug view”-
Add the debug artifact to debug builds only, as on Install the SDK:
app/build.gradle.kts dependencies {debugImplementation("ai.grona:grona-debug:0.1.0")} -
Call
Grona.openDebugViewfrom a hidden menu or a developer setting in your debug build:import ai.grona.sdk.Grona// In an activity, for example from a hidden developer menuGrona.openDebugView(this) -
Tap Refresh on the debug screen after you change something in the app.
openDebugView does nothing when the app is not debuggable or the artifact is missing. In an app that is not debuggable it logs a warning. The debug screen is not exported, has no intent filters and blocks screenshots.
What the debug view shows
Section titled “What the debug view shows”| Section | Contents |
|---|---|
| SDK | SDK version, workspace, configuration source (bundled, cache or remote), configuration version, key id, expiry, last configuration error, consent state, the last 4 characters of the install id, whether a shopper is identified, and whether demo mode is in effect |
| Campaigns | Each campaign’s name, id and placement, whether it would render now for this shopper, and if not, why (for example activation off, holdout, no grant or audience: profile unknown). Also the assigned arm and variant |
| Pins | Local pins, with a field to pin a variant on this device and a button to clear pins |
| Slots | Every bound slot and decision with its placement, screen and key, and the campaign it shows |
| Recent events | Up to 200 recent events, newest first, with identifiers shortened |
Local pins work only in debuggable apps and never create a grant. The recent-events list is kept only in debuggable apps, so release builds keep no copy of order ids, totals or product ids for it.
Route logs to Logcat
Section titled “Route logs to Logcat”GronaLogger receives SDK diagnostics. It is silent by default. Send them to Logcat in debuggable builds:
package com.example.acmemarket
import ai.grona.sdk.Gronaimport ai.grona.sdk.GronaLoggerimport ai.grona.sdk.GronaOptionsimport android.app.Applicationimport android.content.pm.ApplicationInfoimport android.util.Log
class AcmeMarketApp : Application() { override fun onCreate() { super.onCreate() val debuggable = (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0 val logger = if (debuggable) { GronaLogger { priority, message -> Log.println(priority, "Grona", message) } } else { GronaLogger.NONE } val options = GronaOptions.Builder("acme-market") .logger(logger) // ... the other options from Initialize the SDK .build() Grona.init(this, options, AcmeGronaHost(this)) }}priority uses the android.util.Log levels: 3 debug, 4 info, 5 warning, 6 error. Messages start with Grona:. The SDK never passes customer ids, basket contents, tokens or keys to the logger. Your logger is called from Grona’s background threads and from the main thread, so keep it thread-safe and away from views.
Warnings to look for
Section titled “Warnings to look for”| Warning | What to fix |
|---|---|
the <placement> anchor is GONE or ... is INVISIBLE |
A slot or decision anchor is hidden. Keep it VISIBLE at zero height when empty, or the control arm is never measured. Logged once per anchor, in debuggable builds only. |
identify ignored: customer id must match ... |
The customer id has characters outside [A-Za-z0-9._:-] or is longer than 128. |
identify: an all-digit id of 9 or more characters ... |
The id looks like a phone number or national id. Pass an opaque internal id. |
profile ignored: it is not bound to the identified customer |
setProfile came before identify, or for another customer. |
decision() must be called on the main thread; posting it |
Call Grona.decision from the main thread. |
preview link rejected: ... |
The preview link is malformed. See Preview links. |
not the main process; Grona stays inactive |
Grona.init ran in a secondary process. |
Check the integration
Section titled “Check the integration”- Empty slots. In the layout inspector, an empty slot is
VISIBLEwith zero height. No hidden-anchor warning appears. - Campaign state. Each campaign you expect is live in the debug view, or shows a reason you understand.
- Basket. Progress bars and substitutes follow the cart as you change it. If not, a cart path is missing
setBasket. - Consent. Under
UNKNOWNno events are sent. AfterGRANTED, events reach your events endpoint. - Release build.
openDebugViewdoes nothing, and your R8 build runs without extra rules.
To pick up a configuration change during a test without waiting, call Grona.refresh with a listener. See Refresh on demand. To see one variant, use a preview link or a local pin.