SDK documentation
Stream logs, crashes and user feedback to your LogWatcher server. Six SDKs, one API, one wire protocol — pick your platform:
Getting started
You need an application key. Create an account, add an app in the dashboard, and copy the key shown on its card. Every SDK call is tied to that key. Then follow your platform's guide above — the flow is always the same three steps: point the SDK at your server (before init), initialize with the app key, log.
How delivery works
Every SDK implements the same delivery model: log lines are written to a local, wire-ready queue first (SQLite, JSONL file, localStorage or memory depending on the platform), then uploaded in batches — every 5 minutes, or immediately when the queue reaches 200 rows / 64 KiB, in chunks of at most 1000 rows grouped by session. Entries are deleted only after the server acknowledges (at-least-once delivery), the queue is capped at 5 MiB with oldest-first eviction, and platforms with a persistent queue drain leftovers on the next launch — which is what delivers the breadcrumbs leading up to a crash. Android, Flutter and Go gzip large batches.
Remote device control
On every launch the SDK registers the device and receives its remote flags. From the dashboard you can enable log collection for a single device (a misbehaving terminal) while the rest of the fleet stays quiet — no release, no restart loop. To force collection locally regardless of the remote flag:
// same pair on every platform (Go: SetForceEnabled / ForceSendOnce)
LogWatcher.setForceEnabled(true) // override the remote toggle
LogWatcher.forceSendOnce() // flush the queue right now
Encrypted applications
An application can be created as encrypted: tick Encrypt this application's content when you create it in the dashboard. Its content is then ciphered on the device with a 16-character shared key that only you hold. LogWatcher stores ciphertext and never receives the key; the dashboard deciphers in your browser when you paste it. It is optional, and it is decided once — an application cannot be switched to or from encrypted later.
| Ciphered on the device | Still readable by the server |
|---|---|
| Log messages with their tag, method, file, line and thread; titles and texts of issues, crashes and feedback; the values of device key-values. | The time and level of each line, the names of key-values, and device and session data — so counts, statistics and device lists work without the key. |
The dashboard shows the shared key once, when the application is created
(XXXX-XXXX-XXXX-XXXX). Store it where your team keeps secrets: LogWatcher cannot show it again, reset it
or rotate it, and without it nobody can read the application's content. Give the same key to the SDK and to everyone who reads
the application in the dashboard.
// Android — before init(), like setApiUrl
LogWatcher.setEncryptionKey("K7Q2-M9XH-4TBD-W3NP")
LogWatcher.init(this, "YOUR_APP_KEY", true)
An SDK that was given a key uploads sealed content or nothing: with a wrong key, or a key set for an application that is not encrypted, everything stays in the local queue and one error in the device log says why. In the dashboard, an encrypted application shows a lock; paste the key in the bar above Logs, Crashes or Issues to read and search. Text search then runs in your browser, and Report rules that read log content are not available.
SDK support. Today the Android SDK ciphers. The iOS, JavaScript, Svelte, Flutter and Go SDKs do not yet: pointed at an encrypted application they are switched off by the server, and nothing readable they send is stored.
Platform matrix
| SDK | Package | Runs on | Queue | Crash capture |
|---|---|---|---|---|
| Android | gr.fintechiq.logwatcher (AAR) | Android 4.1+ | SQLite (persistent) | uncaught-exception handler |
| iOS | LogWatcher (SwiftPM) | iOS 12+, macOS 10.15+ | JSONL file (persistent) | NSUncaughtExceptionHandler |
| JavaScript | @fintechiq/logwatcher | Browsers, Node ≥18 | localStorage / memory | onerror + unhandledrejection / process hooks |
| Svelte | @fintechiq/logwatcher-svelte | Svelte, SvelteKit | as JavaScript | SvelteKit handleError + browser globals |
| Flutter | logwatcher_sdk (pub) | Flutter mobile/desktop, Dart CLI | JSONL file / memory | recordError + FlutterError hooks |
| Go | logwatcher-sdk-go | Go 1.21+ services (GCP-aware) | memory (bounded) | RecoverPanic / ReportPanic |
HTTP API reference
Everything every SDK does goes through five endpoints. Authentication is the
X-App-Token header carrying your application key.
| Endpoint | Purpose | Notes |
|---|---|---|
POST /app/device-status | Register a device, fetch remote enable flags | sent on every launch |
POST /session | Start a session | returns the numeric session id |
POST /log/batch | Upload a batch of log lines | X-App-Session header; token must match the session's app; body may be gzip (Content-Encoding: gzip) |
POST /issue | Issues, crashes, user feedback | discriminated by type |
POST /device/keyvalue | Set one device key-value | typed value |
A sixth endpoint only answers a question, and stores nothing:
GET /app/check says whether the key in X-App-Token is an application's,
what the application is called and whether it is encrypted — for an app that lets a person enter the key and wants to check it
before using it. No device is registered by it. It answers {"name": "…", "encrypted": false},
for an encrypted application with the public encryption object that registration returns, and
401 for a key the server does not know. In the Android SDK it is
LogWatcher.checkApplication.
For an encrypted application
the same five endpoints are used, and no field, header or status code is added: the ciphertext travels in the content fields
that already exist, and registration carries one extra value, "encryption": "lwe1", sent only by an SDK
that was given a shared key.
The full wire protocol is documented in the LogWatcher repository.