BowlerKit
Features

Logging & Debugging

1/22/2026

Guidelines for tracking application behavior and debugging issues in Expo.

Logging and debugging are essential for maintaining a healthy application. This guide outlines the tools and practices used in this boilerplate to monitor state, network, and errors.

Overview

We use a combination of React Native's built-in console for development, React Query DevTools for network state, and Sentry/Crashlytics for production error tracking.

🏗 Architecture

1. Development Logging

In development mode, we use standard console methods. To prevent logs from cluttering the production environment, always wrap them in if (__DEV__) or use a utility that handles this.

if (__DEV__) {
  console.log('App initialized');
}

2. Network Logging

Our API service (services/api.ts) is configured to log outgoing requests and parsing errors when in development mode.

// services/api.ts
if (__DEV__) console.log(`[API] ${method} ${url}`);

You can also use the React Query DevTools on web or the React Native Debugger to inspect network traffic.

3. State Tracking

  • React Query: Tracks the state of your server data. You can observe transitions between loading, error, and success states in your components.
  • Zustand: For global client-side state. You can add middleware to log state transitions during development.

4. Navigation Tracking

The boilerplate uses expo-router. You can track navigation state by listening to router changes in your root layout.


🚀 Usage

Standard Logging

Use the appropriate log level for your messages:

console.log("Debug info");      // General development info
console.warn("Potential issue"); // Non-critical issues
console.error("Critical error"); // Failures that need attention

API Errors

Errors from the api service are thrown as ApiError. These should be caught in your mutations or queries:

try {
  await api.post('/login', data);
} catch (error) {
  if (error instanceof ApiError) {
    console.error(`API Error ${error.status}: ${error.message}`);
  }
}

🛠 Debugging Tools

1. Expo Go & Dev Menu

Shake your device or press d in the terminal to open the Expo Dev Menu. From here, you can:

  • Reload the app.
  • Toggle the Performance Monitor.
  • Open the Element Inspector.

2. Chrome DevTools

When running the app, you can debug JavaScript using Chrome DevTools:

  1. Open the Dev Menu in your app.
  2. Select "Debug remote JS".
  3. A Chrome tab will open where you can set breakpoints and inspect logs.

3. React Query DevTools

To debug network requests and cache state:

  • On Web: The devtools are available if configured in app/_layout.tsx.
  • On Mobile: Use React Query Monitor.

🔗 Integration Points

Our logging and debugging strategy is integrated with:

  • React Query: Captures all network state transitions and cache hits/misses.
  • Expo Router: Tracks navigation events and route parameters.
  • Zustand: Monitors global client state changes (via devtools).
  • Firebase: Logs push notification reception and token updates.
  • App Lifecycle: Using Expo's AppState to log when the app moves to background or returns to active.

🚨 Production Logging

For production monitoring, we recommend integrating the following services:

Sentry (Error Tracking)

Sentry captures crashes and JavaScript errors in real-time.

  1. Install: npx expo install @sentry/react-native
  2. Configure: Add to app.json plugins:
    "plugins": ["@sentry/react-native/expo"]
  3. Initialize: In your root _layout.tsx:
    import * as Sentry from '@sentry/react-native';
    
    Sentry.init({
      dsn: 'YOUR_SENTRY_DSN',
      debug: __DEV__,
    });

Firebase Crashlytics

For native-level crash reporting.

  1. Install: npx expo install expo-firebase-crashlytics
  2. Configure: Ensure google-services.json and GoogleService-Info.plist are in your project root.

💡 Best Practices

  1. Remove Logs in Production: Use babel-plugin-transform-remove-console in your babel.config.js to automatically strip console logs from production builds.
  2. No Sensitive Data: Never log passwords, tokens, or PII (Personally Identifiable Information).
  3. Contextual Messages: Include enough context in your logs to identify where and why an event happened.
    • Bad: console.log(data)
    • Good: console.log('[AuthStore] Token refreshed:', data.id)