◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-06-19Advanced

Logging Design for Rork Apps: What to Keep and How to Redact PII

Rork-generated apps tend to scatter console.log everywhere, and when a bug appears you cannot read the part that matters. This designs structured logging, log levels, automatic PII masking, and production send control — all with code you can use as-is.

Rork611App Development34LoggingPrivacy10Operations7

✦ Premium Article

A few days after the app cleared review, a report came in: saving fails on one specific device model. It would not reproduce on my own phone. Logs are what you lean on at moments like this, but when I opened the code Rork had exported, console.log calls were scattered around in raw form, and the part that actually mattered was impossible to read.

As an indie developer running several apps across the App Store and Google Play, "bugs that don't reproduce on my machine" are unavoidable. Each time, it drives home that logs are too late if you add them after the accident.

Logs also carry the opposite danger. The more detail you try to keep, the more you risk writing out personal information — email addresses, auth tokens — right alongside it. Here we build, hands-on, a logging design that keeps enough to investigate while reliably hiding what must be hidden.

Why scattered console.log fails you

A string log like console.log("saved") looks clear in the moment. But when you search later, it gives almost nothing to grab onto.

There are three problems. First, when, on which screen, and in what user state it happened are buried in a string rather than structured. Second, dev output lingers into production, leaking unnecessary information. Third, PII that should be hidden mixes in unprotected.

So what you need is one place that does three things: shape logs into a readable form, switch the volume by environment, and automatically hide dangerous values.

Logs become searchable only once structured

First, treat a log as an object, not a string. Shift to JSON from the start so you can filter mechanically later.

// logger.ts — the single logger for the whole app
type Level = "debug" | "info" | "warn" | "error";
 
type LogRecord = {
  ts: string;          // ISO8601 timestamp
  level: Level;
  event: string;       // a short identifier like "save.failed"
  screen?: string;
  meta?: Record<string, unknown>;
};
 
function emit(record: LogRecord) {
  // dev: print as-is; production: route to the send logic below
  console.log(JSON.stringify(record));
}

The key is to standardize event into a short identifier like "save.failed". With fixed names instead of free text, filtering for "only save failures" later is a one-liner.

Attach context to screen and meta, and "on which screen, with what input" stays as structure. The point is to split into fields rather than write into a string.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A migration path that consolidates scattered console.log into one logger and shifts to JSON structured logs
✦A regex-based masking implementation that automatically hides emails, tokens, and device IDs before they are sent
✦Log-level control that prints everything in dev but sends only warn-and-above in production, with sampling to cap cost
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-10-27
Claude Sonnet 4.5 Leaves the API on November 30. Find the Model IDs Hard-Coded in Your App First
Claude Sonnet 4.5 is scheduled to retire from the API on November 30, 2026. A small script finds the model IDs written into your app's code and config, so you can replace them and check again.
⬡ Dev Tools2026-08-22
Every bulk replace exited zero. The damage was in the lines I did not delete
Run a bulk replace over generated code and the breakage lands on the neighbouring lines, not the matched ones. Here is what broke in a live project, and a dependency-free guard that checks the invariants a replace must preserve.
⬡ Dev Tools2026-10-30
It runs in development but the iOS Release build dies on launch — checking whether a missing scheme is the cause
In an expo-router app, an app.json with no scheme can crash only the iOS Release build at launch. Why Debug and Android survive, the two ways to fix it, how to think about leaving the scheme out on purpose, and a pre-submission check.