Logging
There are two distinct concerns here. Errors that should reach Sentry go through captureException from @tinycld/core/lib/errors. Dev-only output — anything you wouldn’t ship to production telemetry — is plain console.warn / console.error, the same as any other React Native app.
There is no separate log helper. If you wrote some log.info(...) in a draft, replace it with console.info (dev tracing) or with a Sentry breadcrumb-bearing captureException (real failure).
Reporting errors to Sentry
captureException(context, error, extra?) is the canonical entry point. context is a short stable string (Sentry uses it to group events), error is whatever you caught, and extra is arbitrary structured context attached to the event:
import { captureException } from '@tinycld/core/lib/errors'
export function useImport() {
const run = async (filename: string) => {
try {
await pb.collection('example').create({ filename })
} catch (err) {
captureException('example.import.create', err, { filename })
throw err
}
}
return { run }
}
The context string is the thing you’ll grep for in Sentry — pick something specific (mail.openDraft.fetchBody, not mail.error) and stable (don’t interpolate user data into it). The extra bag is the place for variable detail. Rethrow when the caller still needs to handle the failure; swallow only when the surface can recover without the data.
Form validation errors
When a useMutation fails because PocketBase rejected a field, the right handler is handleMutationErrorsWithForm({ setError, getValues }) (also from @tinycld/core/lib/errors). It maps PocketBase validation errors back onto the react-hook-form fields and routes everything else into a root error you can render with <FormErrorSummary />. See Forms.
You generally don’t combine handleMutationErrorsWithForm with captureException — validation failures aren’t bugs.
Dev-only output
For the console.log-shaped use case (printf-debug a render, trace a state machine in dev), use console.* directly. There’s no central wrapper. React Native and the web bundler both strip aggressive console.* chatter in production builds, and Sentry’s React Native SDK captures unhandled console.error lines on its own when configured to.
if (__DEV__) {
console.debug('[mail.compose] draft id', draftId)
}
Wrap deliberately noisy output in if (__DEV__) so it doesn’t survive a release build. Don’t ship console.log calls without that guard.
What not to do
- Don’t import from
@tinycld/core/lib/logger— it doesn’t exist. Some older code samples reference it; treat them as out of date. - Don’t
captureExceptionfor control flow. Sentry events cost money and dilute the signal — only fire one when something has actually gone wrong. - Don’t include unscrubbed user input in the
contextstring. Put it inextraso Sentry’s PII scrubbing can do its job.
Caught exceptions you want reported but not rethrown
The same captureException covers this — just don’t rethrow:
import { captureException } from '@tinycld/core/lib/errors'
try {
await pb.collection('example').create(data)
} catch (err) {
captureException('example.create.silent', err, { data })
// intentionally swallowed: caller has nothing actionable to do
}
The comment is the important part. Swallowing without one is a smell — six months from now no one will remember whether the silence was intentional.