Core configuration
Create core configuration withConfigurationBuilder and pass it to Flashcat.initialize().
These three settings share their names and values with the Android, iOS and Flutter SDKs, so tuning advice transfers between platforms unchanged.Since SDK 0.5.0,
setBatchUploadFrequencyMs(5000) is replaced by setUploadFrequency(UploadFrequency.RARE). Two defaults changed at the same time, both to match the other platforms: the upload interval went from 5 s to 2 s, and the batch window from 5 s to 10 s.Flashcat.initialize() initializes a given instance name only once. A duplicate call returns the existing instance and does not re-register feature modules.Reducing the impact of uploads on your own requests
If the app makes latency-sensitive requests of its own (sign-in, checkout, key provisioning) over a narrow uplink, SDK uploads can compete with them for the uplink. The symptom is a higher tail (P95) on your own request latency that comes and goes. Lower all three settings together to change how uploads are distributed over time — fewer uploads, spread further apart:Tuning advice transfers between platforms unchanged, because these three settings share names and values with the Android, iOS and Flutter SDKs. The one difference is that iOS
batchProcessingLevel = .low is 5 batches per cycle, while HarmonyOS and Android use 1 — same direction, smaller reduction. See Android SDK performance impact and iOS SDK performance impact.
If the event volume itself is high, use event mapping with
setEventMapper to drop specific noisy events (return null to drop). That is less invasive than changing your instrumentation.
Tracking consent
To comply with privacy regulations such as GDPR and CCPA, the SDK requires a tracking consent state at initialization (the third argument ofFlashcat.initialize()), and lets you change it at any time afterwards.
Consent states
When initialized with
TrackingConsent.PENDING, the SDK writes events to a separate local buffer and sends nothing until consent changes to GRANTED — at which point the buffered data is migrated and uploaded automatically. Changing to NOT_GRANTED clears the buffer instead.The application owns the consent state
Every launch uses the value you pass toFlashcat.initialize. The SDK never overrides it with a previously stored state — the same contract as the Android and iOS SDKs. Your application is responsible for storing the user’s choice and passing it back at every initialization.
The consent state is also persisted locally, but it serves exactly one purpose: the WorkSchedulerExtensionAbility used for background upload runs in a separate process with no user in front of it and no access to the application’s decision, so it reads the main process’s last recorded state. See Background and deferred upload.
When consent is revoked (changed to NOT_GRANTED), the SDK clears the unsent pre-consent buffer and deletes batches already written to disk but not yet uploaded. On 0.2.0 and earlier only the buffer was cleared, so collected batches were still sent once consent came back.
Starting in 0.3.2, revoking consent also deletes the local view snapshot used for crash attribution. That snapshot is a whole view event — user id, name, email, and any custom context — so it is just as personal as a collected batch.
Setting and changing consent
At initialization:setTrackingConsent API (for example once the user responds to your privacy dialog):
RUM configuration
Create RUM configuration withRumConfigurationBuilder and pass it to FlashcatRum.enable().
setTrackErrors(false) disables automatic error capture only. Crash reporting is unaffected: with the crash module enabled, uncaught exceptions still follow JsCrashPolicy — they are persisted, counted, and replayed as is_crash errors into the session that crashed. Manual addError calls are the application’s explicit intent and are also still delivered. Use setEventMapper() if you need to filter those too.Event mapping and redaction
UsesetEventMapper() to perform lightweight processing before events are reported. Return the modified event to keep it, or null to drop it.
Global attributes and user information
Global attributes are merged into thecontext object on subsequent events.
On sign-out, call
clearAttributes() to drop the previous user’s business attributes and then stopSession(), so two users’ behavior never lands in the same session.id, name, and email are written to the usr object on subsequent events.
Trace configuration
The Trace module generates W3Ctraceparent and tracestate, then correlates the generated trace id and span id to RUM resources through _dd.trace_id and _dd.span_id. tracestate carries the Datadog vendor entry dd=s:{0|1};o:rum.
setFirstPartyHosts() is currently used only by the FlashcatHttp wrapper. The rcp interceptor itself is an explicit per-session opt-in, so requests made through a session with the interceptor receive Trace headers. If a request already has traceparent, the SDK does not overwrite the existing Trace context. Existing tracestate keeps other vendors and moves the updated dd= member to the front.Crash reporting configuration
The Crash module provides two collection paths:- It listens to HarmonyOS
hiAppEventforAPP_CRASHandAPP_FREEZE, then reports system-replayed fault events through the RUM error pipeline on a later launch - It handles uncaught main-thread ArkTS exceptions live and uses
JsCrashPolicyto report before the process exits or restarts
REPORT_THEN_EXIT.
REPORT_THEN_EXIT
This is the default policy. When an uncaught synchronous or asynchronous main-thread ArkTS exception occurs, the SDK synchronously persists a crash incident, flushes the current RUM writers, and exits the process. The incident is replayed into RUM on the next launch. If the synchronous write fails, the SDK attempts an asynchronous report and still exits.
REPORT_AND_RECOVER
The SDK synchronously persists the crash incident, checks the persisted crash-loop guard, then calls appRecovery.saveAppState() and restartApp(). The old process exits and a new process starts. The incident replayed on the next launch includes crash.recovered: true.
If recovery cannot be enabled, loop history cannot be persisted, the guard trips, the incident cannot be marked as recoverable, or the restart request fails, the SDK degrades to exit behavior.
OBSERVE_ONLY
The SDK reports the exception asynchronously and leaves the current process running. This policy restores the pre-0.2.0 keep-alive behavior and does not exit or restart the process. The event loop may remain responsive, but the uncaught exception may have left the application in a broken or inconsistent business state.
Crash-loop protection
Crash-loop protection affects onlyREPORT_AND_RECOVER. With the defaults, the first two crashes in a 60-second rolling window can restart the application. The third crash is reported synchronously but degrades to exit: the Nth crash in the window is blocked from restarting, allowing at most N-1 recovery restarts.
Crash timestamps persist across processes, so restarting the application does not reset the guard. After the guard trips, each blocked crash becomes the newest timestamp. The application must remain crash-free for the full five-minute cooldown before history resets. Setting setCrashLoopThreshold(1) disables recovery restarts entirely: every crash is still reported synchronously, but each one exits and is never marked with crash.recovered.
Restore host application state
Automatic restart does not define which page state to restore. The hostUIAbility must implement onSaveState, copy the required state into wantParam, and return ALL_AGREE:
Want and restore only state that is safe to resume. State restoration requires the host to implement onSaveState; the SDK triggers the state save and the restart when a crash occurs.
Capability boundary
Background and deferred upload
By default, the SDK uploads on the foreground cadence configured bysetUploadFrequency() and triggers flush() when the application backgrounds. If you want HarmonyOS WorkScheduler to wake the app for uploads, register deferred upload work.
The SDK registers the WorkScheduler task. The task is persisted (
isPersisted, surviving reboots) and repeats on a 2-hour cycle.
Initializing inside the extension process
AWorkSchedulerExtensionAbility runs in a separate process and does not share the main process’s SDK instance. When it wakes, initialize with initializeForDeferredUpload before calling flushAndWait():
MyUploadExtensionAbility.ets
initializeForDeferredUpload differs from initialize in two important ways:
- It takes no consent argument. The extension has no user in front of it, so it acts only on the main process’s last persisted decision. If nothing is persisted (the main application never initialized) or the stored decision is
NOT_GRANTED, it reads, migrates, and uploads nothing. - It is read-only. It never writes back consent, device identity, or work registrations. HarmonyOS Preferences are per-process whole-file caches, so a write from the extension could clobber a revocation happening concurrently in the main process.
Upload HarmonyOS crash symbols
To de-obfuscate ArkTS stacks and symbolicate native.so stacks in the console, upload build artifacts with @flashcatcloud/hvigor-plugin.
The plugin uploads two artifact types:
The plugin ships as an npm package (on npm, not ohpm); install it as a build-time dev dependency in your project’s root
package.json, not in oh-package.json5:
hvigorfile.ts:
hvigorfile.ts
On SaaS, omit
endpoint — hvigor-plugin ≥ 0.1.3 defaults to https://ci.flashcat.cloud (not the RUM ingest host browser.flashcat.cloud). For a private deployment set FLASHCAT_SOURCEMAP_INTAKE_URL (scheme + host, no path; also requires ≥ 0.1.3), or pass endpoint: 'https://rum.example.com'. Plugin 0.1.2 does not honour FLASHCAT_SOURCEMAP_INTAKE_URL — set endpoint explicitly, or use the legacy FLASHCAT_ENDPOINT env var (deprecated in 0.1.3 but still honoured). flashcatSymbolUploadPlugin() also accepts two optional fields: buildDir (build output directory, default build/default) and pluginVersion (the version sent in the DD-EVP-ORIGIN-VERSION upload header, which defaults to the plugin’s own version). Neither is normally required.multipart/form-data to {endpoint}/sourcemap/upload:
Upload event types: