@nlite/logger-react-native
v1.0.2
Published
> Mobile observability for React Native apps — auto-captures JS errors, native crashes, network requests, resource usage, frame rate, slow promises, and app-lifecycle transitions. Ships with an offline-first transport backed by `AsyncStorage` so logs surv
Maintainers
Readme
@nlite/logger-react-native
Mobile observability for React Native apps — auto-captures JS errors, native crashes, network requests, resource usage, frame rate, slow promises, and app-lifecycle transitions. Ships with an offline-first transport backed by
AsyncStorageso logs survive flaky networks.
Built on top of @nlite/logger-core. Designed to be ingested by @nlite/logger-server.
Table of Contents
- Why this SDK?
- Installation
- Quick Start
- Configuration
- React API (
LoggerProvider, hooks, HOC) - Standalone API
- Captured Signals
- Workflow & Lifecycle
- Architecture Diagrams
- Examples
- Optional Native Modules
- Privacy & Sampling
- Scripts
- Compatibility
- License & Author
Why this SDK?
Most mobile logging libraries give you breadcrumbs — NLite gives you telemetry:
- Crash reporting for JS errors, promise rejections, and native crashes (when a compatible module is installed).
- Network monitoring by instrumenting
fetchandXMLHttpRequest. - Resource monitoring — memory, CPU, battery, network I/O.
- Performance monitoring — frame rate, slow
InteractionManagercallbacks. - App lifecycle breadcrumbs —
app_start,app_background,app_foreground, etc. - Offline queue — failed batches are persisted in
AsyncStorageand flushed on reconnect. - Single React provider —
<LoggerProvider>+useNLiteLogger().
Installation
# npm
npm install @nlite/logger-react-native @react-native-async-storage/async-storage
# pnpm
pnpm add @nlite/logger-react-native @react-native-async-storage/async-storage
# yarn
yarn add @nlite/logger-react-native @react-native-async-storage/async-storageFor iOS, run cd ios && pod install.
Requirements
| Tool | Version |
|------|---------|
| React Native | >=0.70.0 (peer) |
| React | >=18.0.0 (peer) |
| @react-native-async-storage/async-storage | ^1.21.0 |
| Node.js (build only) | >=18.0.0 |
Quick Start
Wrap your app root:
import React from 'react';
import { LoggerProvider } from '@nlite/logger-react-native';
export default function App() {
return (
<LoggerProvider
config={{
apiKey: 'YOUR_API_KEY',
endpoint: 'https://logs.example.com',
appName: 'shop-mobile',
appVersion: '2.4.1',
environment: 'production',
platform: 'react-native',
}}
>
<RootNavigator />
</LoggerProvider>
);
}Use it in any component:
import { useNLiteLogger } from '@nlite/logger-react-native';
export function CheckoutButton() {
const logger = useNLiteLogger();
const onPress = () => {
logger.addBreadcrumb({
type: 'ui',
category: 'tap',
message: 'Checkout tapped',
level: 'info',
});
logger.info('Checkout started', { cartId: 'c_42' });
// ...
};
return <Button title="Checkout" onPress={onPress} />;
}Configuration
ReactNativeSdkConfig extends SdkConfig and adds:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| platform | 'react-native' \| 'android' \| 'ios' | — | Required. |
| enableNativeCrashReporting | boolean | true | Wire to RNExceptionHandler / ReactNativeExceptionHandler / RNCrashReporter when available. |
| enableNetworkMonitoring | boolean | true | Instrument fetch + XMLHttpRequest. |
| enableConsoleCapture | boolean | true (in __DEV__) | Mirror console.* calls into logs. |
| enableResourceMonitoring | boolean | true | Periodic memory / CPU / battery / network metrics. |
| resourceMonitoringInterval | number | 30000 | Milliseconds between metric collections. |
| enablePerformanceMonitoring | boolean | true | Frame rate + slow InteractionManager warnings. |
| enableInteractionTracking | boolean | false | Track slow user interactions. |
| offlineStorageMaxSizeMB | number | 10 | Max size of the persisted offline queue. |
| resourceSampleRate | number (0-1) | 1.0 | Probability of collecting each resource sample. |
| captureRequestResponseBodies | boolean | false | Capture fetch/XHR request/response bodies. |
| maxBodyCaptureSize | number | 10000 | Bytes to keep before truncating captured bodies. |
React API (LoggerProvider, hooks, HOC)
<LoggerProvider config>
Wraps the app. Calls initLogger(config) on mount and destroyLogger() on unmount.
<LoggerProvider config={{ /* ... */ }}>
{children}
</LoggerProvider>useNLiteLogger(): LoggerSdk
Returns the logger from the closest provider. Throws if used outside a provider.
useLogger(): LoggerSdk
Returns the singleton logger. Throws if initLogger has not been called.
withLogger(Component, propName?)
Higher-order component for class components:
export default withLogger(ProfileScreen, 'logger');Standalone API
You can also initialise without the provider:
import { initLogger, getLogger, destroyLogger, setUser, addBreadcrumb, flush } from '@nlite/logger-react-native';
export function bootstrap() {
initLogger({
apiKey: 'KEY',
appName: 'shop-mobile',
platform: 'react-native',
endpoint: 'https://logs.example.com',
enableResourceMonitoring: true,
enableNetworkMonitoring: true,
});
setUser('user_42', { plan: 'pro' });
addBreadcrumb({ type: 'navigation', category: 'screen', message: 'Home', level: 'info' });
}
// In your lifecycle code:
onAppShutdown(async () => {
await flush();
await destroyLogger();
});logNetworkRequest(data) (manual)
For GraphQL clients, Axios, or libraries that bypass fetch:
logNetworkRequest({
url: '/graphql',
method: 'POST',
statusCode: 200,
durationMs: 84,
requestBody: '{ query GetProducts }',
responseBody: '{ data: { products: [...] } }',
});collectResourceMetrics() (manual)
Force a sample outside the periodic timer (e.g. before a heavy operation):
collectResourceMetrics();
heavyOperation();
collectResourceMetrics();Captured Signals
| Signal | How it's produced | Logged as |
|--------|-------------------|-----------|
| JS error | ErrorUtils.setGlobalHandler | error with type: 'js_error', fatal flag |
| Promise rejection | global.onunhandledrejection | error with type: 'unhandled_promise_rejection' |
| Native crash | Optional native module handler | error with type: 'native_crash', fatal |
| Previous-session crash | Read @nlite_last_crash from AsyncStorage on startup | error with stored crash metadata |
| console.error | Hooked console.error | error with full args |
| Network request | Patched fetch + XMLHttpRequest | breadcrumb + info('network_request', …) |
| App lifecycle | AppState listener | info('lifecycle_*', …) + breadcrumb |
| Resource metrics | Periodic timer | breadcrumb + info('resource_metrics', …) |
| Frame rate drop | requestAnimationFrame loop | warn when fps < 30 |
| Slow interaction | Wrapped InteractionManager.runAfterInteractions | warn when > 100ms |
Workflow & Lifecycle
┌──────────────────────────────────────────┐
│ LoggerProvider / initLogger(config) │
└─────────────────────┬────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
ReactNativeTransport ResourceMonitor NetworkMonitor
- fetch + XHR transport - memory / CPU / battery - fetch / XHR hook
- offline AsyncStorage - frame rate (rAF loop) - body capture
- reconnect on AppState - slow InteractionManager - GraphQL helper
│ │ │
└─────────────┬─────────────┴─────────────┬─────────────┘
▼ ▼
CrashReporter AppLifecycleTracker
- ErrorUtils hook - app_start / background /
- unhandledrejection foreground / inactive
- native module handler
│ │
└─────────────┬─────────────┘
▼
┌───────────────────────────┐
│ @nlite/logger-core │
│ queue → batch → retry │
└─────────────┬─────────────┘
▼
POST {endpoint}/api/logs/batch
@nlite/logger-serverOffline flow
Network down Network up
│ │
▼ ▼
log() → transport.send() fails AppState → 'active'
│ │
▼ ▼
push to offlineQueue flushOfflineQueue()
persist to AsyncStorage │
▼
transport.send(batch)
│
▼
success → drop from queue
failure → push back, retrySession & lifecycle
- A session is created on
initLogger(UUID v4). app_start/app_background/app_foreground/app_inactiveare emitted asinfo('lifecycle_*', …)logs.- The
AppLifecycleTrackerrecords background duration and re-flushes the offline queue when the app returns to foreground.
Architecture Diagrams
Component view
┌──────────────────────────────────────┐
│ React Native App │
│ <LoggerProvider config={...}> │
└────────────────┬─────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ initLogger(config) │
│ ├─ ReactNativeTransport(endpoint, key) │
│ ├─ setupAutoCapture(logger, config) │
│ ├─ resourceMonitor.start() │
│ ├─ networkMonitor.start() │
│ ├─ crashReporter.start() │
│ └─ lifecycleTracker.start() │
└────────────────────────┬───────────────────────────────┘
│
▼
┌────────────────────────────────────────┐
│ @nlite/logger-core (CoreLogger) │
│ queue → batch → retry/backoff │
└────────────────────┬───────────────────┘
│
▼
POST {endpoint}/api/logs/batch
@nlite/logger-server (SQLite + Redis)Sequence — JS error path
JS runtime ErrorUtils CrashReporter CoreLogger Transport AsyncStorage Server
| | | | | | |
| throw err | | | | | |
|------------>| | | | | |
| | handler | | | | |
| |------------>| | | | |
| | | logger.error| | | |
| | |------------>| | | |
| | | | batch POST | | |
| | | |------------>| 200 OK | |
| | | | |<-------------| |
| | | | | | |Sequence — offline path
App Transport AsyncStorage Network
| | | |
| log() | | |
|--------------->| | |
| send() fail | | |
| offline push | | |
|--------------->|--------------->| |
| | | |
| (later) AppState 'active' | |
| | | |
| | flushOffline | |
| |------------->| |
| | batch | |
| |---------------------------->|
| | 200 OK | |
|<---------------| | |Examples
Setting user + tags after login
import { setUser, setTags } from '@nlite/logger-react-native';
async function onLogin(user) {
setUser(user.id, { email: user.email, plan: user.plan });
setTags({ tenant: user.tenantId, appBuild: '2.4.1' });
}Capturing non-fetch HTTP traffic (e.g. Axios)
import axios from 'axios';
import { logNetworkRequest } from '@nlite/logger-react-native';
const instance = axios.create();
instance.interceptors.response.use(
(res) => {
logNetworkRequest({
url: res.config.url!,
method: (res.config.method ?? 'get').toUpperCase(),
statusCode: res.status,
durationMs: res.config.metadata?.durationMs ?? 0,
requestHeaders: res.config.headers as Record<string, string>,
requestBody: JSON.stringify(res.config.data),
responseBody: JSON.stringify(res.data),
});
return res;
},
(err) => {
logNetworkRequest({
url: err.config?.url ?? '',
method: (err.config?.method ?? 'get').toUpperCase(),
statusCode: err.response?.status ?? 0,
durationMs: 0,
error: err.message,
});
return Promise.reject(err);
}
);Disable sampling for high-volume flows
initLogger({
/* ... */
enableResourceMonitoring: true,
resourceSampleRate: 0.1, // 10% of intervals
enableInteractionTracking: false,
});Optional Native Modules
The crash reporter and resource monitor rely on optional native bridges:
| Module | Used for |
|--------|----------|
| RNExceptionHandler / ReactNativeExceptionHandler | Native crash handling |
| RNCrashReporter | Crash metadata |
| RNDeviceInfo / DeviceInfo | Memory, CPU, battery, network I/O |
If none is installed the SDK silently falls back to JS-only metrics.
Privacy & Sampling
- Use
resourceSampleRateto throttle high-frequency telemetry. - Set
captureRequestResponseBodies: falseunless you really need payloads. - The default
beforeSend(from@nlite/logger-core) lets you redact PII before transmission. - Offline queue is capped by
offlineStorageMaxSizeMB(default10 MB) and is wiped after a successful flush.
Scripts
| Script | Description |
|--------|-------------|
| npm run build | tsc to dist/. |
| npm run dev | Watch-mode build. |
| npm test | Vitest. |
| npm run test:watch | Vitest watch. |
| npm run lint | ESLint over src. |
| npm run typecheck | tsc --noEmit. |
Compatibility
- React Native
0.70→0.74+(Hermes & JSC supported). - iOS
>=12, AndroidminSdkVersion 21. - TypeScript
5.3+.
License & Author
MIT — © Debanjan Dasgupta. See the root README.
