Skip to main content

Debug System

Status: Production-ready debugging infrastructure Last Updated: 2025-11-21 Aligned with: CLAUDE.md Project Values, Best-in-class debugging practices

Table of Contents


Overview

Purpose

The Sanctiv Debug System provides enterprise-grade debugging capabilities with production-safe toggles, structured logging, and automatic Sentry integration. Key Principles:
  • 100% Excellence - Best-in-class debugging tools
  • Simple but not simpler - Powerful yet easy to use
  • Production-safe - Disabled by default, no performance impact when off

Why We Built This

Before:
  • Scattered console.log statements
  • Manual Sentry breadcrumb tracking
  • Difficult to debug production issues
  • Logs lost on app restart
After:
  • Centralized, structured logging
  • Automatic Sentry integration
  • Persistent debug configuration
  • Performance tracking
  • Network request monitoring
  • State change tracking

Quick Start

Enable Debug Mode

  1. Open SettingsDebug Mode
  2. Toggle “Enable Debug Mode” ON
  3. Configure tracking options:
    • ✅ Send Logs to Sentry (recommended for production debugging)
    • ✅ Console Logging (for live debugging)
    • ✅ Performance Metrics (track slow operations)
    • ✅ Network Tracking (log API calls)
    • ✅ State Tracking (log Zustand changes)
    • ✅ User Interactions (log navigation/clicks)

Use the Logger

import { logger } from "@/lib/logger";

// Basic logging
logger.debug("auth", "Login flow started");
logger.info("auth", "User logged in", { userId: user.id });
logger.warn("api", "Rate limit approaching", { remaining: 5 });
logger.error("database", "Query failed", { error: err.message });

// Performance tracking
await logger.trackPerformance("flow", "Load journal entries", async () => {
  return await supabase.from("journal_entries").select();
});

// Component-level logging
import { useLogger } from "@/lib/logger";

function MyComponent() {
  const log = useLogger("MyComponent");
  
  useEffect(() => {
    log.info("Component mounted");
    return () => log.info("Component unmounted");
  }, []);
}

Architecture

Components

src/
├── lib/
│   ├── logger.ts              # Structured logging API
│   ├── debugNetworkMonitor.ts # Fetch wrapper for network tracking
│   └── sentry.ts              # Enhanced Sentry integration
├── state/
│   └── debugStore.ts          # Debug configuration (Zustand + AsyncStorage)
└── screens/
    └── SettingsScreen.tsx     # Debug Mode UI

Data Flow

Component/Function

   logger.info()

   debugStore.addLog()

   ├─→ Console (if enabled)
   ├─→ Sentry Breadcrumb (if enabled)
   ├─→ In-memory log storage (last 100)
   └─→ AsyncStorage (persistent config)

Features

1. Structured Logging

Log Levels:
  • debug - Verbose development information
  • info - State changes, user actions, API calls
  • warn - Recoverable errors, performance issues
  • error - Exceptions, failed operations
Log Format:
{
  id: string;           // Unique log ID
  timestamp: number;    // Unix timestamp (ms)
  level: LogLevel;      // debug | info | warn | error
  category: string;     // Component/module name
  message: string;      // Human-readable message
  data?: Record;        // Additional context
}

2. Automatic Sentry Integration

When “Send Logs to Sentry” is enabled:
  • ✅ All logs → Sentry breadcrumbs
  • ✅ Errors → Sentry issues
  • ✅ Context preserved for debugging
  • ✅ No code changes required

3. Performance Monitoring

Track operation timing:
// Automatic timing
const entries = await logger.trackPerformance(
  "database",
  "Fetch journal entries",
  async () => {
    return await supabase.from("journal_entries").select();
  }
);

// Logs:
// ⏱️  [database] Starting: Fetch journal entries
// ✅ [database] Completed: Fetch journal entries (duration: 234.56ms)

4. Network Request Tracking

Automatic fetch monitoring when enabled:
import { enableNetworkMonitoring } from "@/lib/debugNetworkMonitor";

// In App.tsx or index.ts
if (debugStore.getState().trackNetworkRequests) {
  enableNetworkMonitoring();
}

// All fetch calls are now automatically logged:
// ✅ [network] GET https://api.sanctiv.ai/journal (status: 200, duration: 123ms)
// ❌ [network] POST https://api.sanctiv.ai/entries (status: 500, duration: 456ms, error: Server Error)

5. State Change Tracking

Monitor Zustand store updates:
logger.trackStateChange(
  "authStore",
  "setUser",
  { user: null },
  { user: { id: "123", email: "[email protected]" } }
);

// Logs:
// 🔍 [state] authStore.setUser
//    before: {"user":null}
//    after: {"user":{"id":"123","email":"[email protected]"}}

6. User Interaction Tracking

Log navigation and clicks:
logger.trackUserInteraction(
  "Button Click",
  "Start Journal Flow",
  { template: "overcomers-v1" }
);

// Logs:
// ℹ️ [user] Button Click → Start Journal Flow
//    template: overcomers-v1

7. Persistent Configuration

Debug settings persist across app restarts via AsyncStorage:
  • Enabled/disabled state
  • Log level
  • Sentry integration toggle
  • Feature flags (performance, network, state, user tracking)

8. Log Export

Export logs for sharing with team:
  1. Settings → Debug Mode → Export Logs
  2. Logs copied as JSON
  3. Share via email/Slack/GitHub issue

8. Version Signature System

Format Philosophy: VERSION (BUILD:OTA) The version signature uniquely identifies the exact state of the app: Components:
  • VERSION = What the app is (semantic version: 1.0.4)
  • BUILD = TestFlight binary number (unique identifier: 37)
  • OTA = Over-the-air update commit hash (first 3 chars: c07)
Examples:
1.0.4 (37)       → Fresh TestFlight install (embedded binary)
1.0.4 (37:c07)   → TestFlight + OTA update applied
1.0.4 (50:c07)   → Older TestFlight (Build 50) + new OTA
1.0.4 (dev)      → Development mode
Rationale:
  • Version is the primary identifier (what users care about)
  • Build:OTA grouping shows delivery method (how they got it)
  • Build number uniquely identifies the binary (no need for redundant binary commit hash)
  • OTA commit shows what’s actually running (JavaScript/UI code)
Location: Settings → Debug Mode → Version Signature Usage:
import { getVersionSignature } from "@/lib/versionSignature";

const sig = await getVersionSignature();
console.log(sig.compact); // "1.0.4 (37:c07)"

// Full details
console.log(sig.binary);  // { version, build, builtAt, channel }
console.log(sig.runtime); // { type: "ota", ota: { commit, branch, ... } }

Usage Guide

Basic Logging

import { logger } from "@/lib/logger";

// Simple message
logger.info("auth", "User logged in");

// With context
logger.info("auth", "User logged in", {
  userId: user.id,
  method: "email",
  timestamp: Date.now()
});

// Error logging
try {
  await saveJournalEntry(entry);
} catch (error) {
  logger.error("journal", "Failed to save entry", {
    entryId: entry.id,
    error: error instanceof Error ? error.message : String(error)
  });
}

Component Logging

import { useLogger } from "@/lib/logger";

function JournalScreen() {
  const log = useLogger("JournalScreen");
  
  const handleSubmit = async () => {
    log.info("Submit clicked", { entryCount: entries.length });
    
    try {
      await saveEntry();
      log.info("Entry saved successfully");
    } catch (error) {
      log.error("Failed to save entry", { error });
    }
  };
  
  return <Button onPress={handleSubmit}>Submit</Button>;
}

Performance Tracking

// Track async operations
const data = await logger.trackPerformance(
  "api",
  "Load user data",
  async () => {
    const response = await fetch("/api/user");
    return response.json();
  }
);

// Track synchronous operations
const result = await logger.trackPerformance(
  "compute",
  "Calculate analytics",
  async () => {
    return computeExpensiveAnalytics(data);
  }
);

Grouped Logs

For multi-step operations:
logger.group("Journal Entry Creation");
logger.info("step", "Validating input");
logger.info("step", "Saving to database");
logger.info("step", "Updating cache");
logger.groupEnd();

Log Levels

Choose appropriate level:
// DEBUG: Development-only, verbose
logger.debug("flow", "Entered step 3", { stepData });

// INFO: Important events, state changes
logger.info("auth", "User session started", { userId });

// WARN: Recoverable issues, deprecated usage
logger.warn("api", "Using deprecated endpoint", { endpoint });

// ERROR: Exceptions, failed operations
logger.error("database", "Connection failed", { error });

Best Practices

1. Use Descriptive Categories

Good:
logger.info("auth", "Login successful");
logger.info("journal", "Entry created");
logger.info("navigation", "Screen opened");
Bad:
logger.info("app", "Something happened");
logger.info("main", "Done");

2. Include Actionable Context

Good:
logger.error("payment", "Transaction failed", {
  transactionId: "tx_123",
  amount: 50.00,
  error: "Insufficient funds",
  userId: user.id
});
Bad:
logger.error("payment", "Failed");

3. Don’t Log Sensitive Data

Never log:
  • Passwords
  • API keys
  • Authentication tokens
  • Credit card numbers
  • Personal health information (PHI)
Safe to log:
  • User IDs (hashed if needed)
  • Error messages
  • Timestamps
  • Feature flags
  • Navigation paths

4. Use Performance Tracking Sparingly

Only track operations that:
  • Are user-facing (load times, UI interactions)
  • Are known to be slow
  • Are being optimized
Good:
await logger.trackPerformance("api", "Load dashboard data", loadDashboard);
Avoid:
// Too granular
await logger.trackPerformance("util", "Format date", () => formatDate(date));

5. Clean Up Old Logs

Logs are kept in memory (last 100). Use “Clear Debug Logs” regularly to free memory.

6. Disable in Production (Users)

Debug mode should be OFF by default for end users. Only enable for:
  • Development
  • Internal testing
  • Troubleshooting specific user issues

Integration Examples

Flow Engine Integration

// src/flow-engine/FlowContainer.tsx
import { logger } from "@/lib/logger";

export function FlowContainer() {
  const log = useLogger("FlowContainer");
  
  const handleContinue = async () => {
    log.info("Continue clicked", { 
      currentStep: currentStep.id,
      isValid: isCurrentStepValid
    });
    
    await logger.trackPerformance(
      "flow",
      "Save step response",
      async () => {
        await updateStepResponse(currentStep.id, response);
      }
    );
    
    goToNextStep();
  };
}

API Integration

// src/lib/api.ts
import { logger } from "@/lib/logger";

export async function fetchJournalEntries() {
  try {
    logger.debug("api", "Fetching journal entries");
    
    const entries = await logger.trackPerformance(
      "api",
      "GET /journal/entries",
      async () => {
        const { data, error } = await supabase
          .from("journal_entries")
          .select();
        
        if (error) throw error;
        return data;
      }
    );
    
    logger.info("api", "Journal entries loaded", { count: entries.length });
    return entries;
  } catch (error) {
    logger.error("api", "Failed to load journal entries", { error });
    throw error;
  }
}

State Store Integration

// src/state/authStore.ts
import { create } from "zustand";
import { logger } from "@/lib/logger";

export const useAuthStore = create<AuthState>((set) => ({
  user: null,
  
  setUser: (user) => {
    logger.trackStateChange("authStore", "setUser", 
      useAuthStore.getState().user,
      user
    );
    
    set({ user });
    logger.info("auth", "User updated", { userId: user?.id });
  },
}));

Troubleshooting

Issue: Logs not appearing in console

Check:
  1. Debug Mode enabled? (Settings → Debug Mode)
  2. Console Logging enabled?
  3. Log level correct? (DEBUG shows all, ERROR shows only errors)
Solution:
// Force enable for testing
useDebugStore.getState().toggle(); // Enable debug mode
useDebugStore.getState().setLogToConsole(true);
useDebugStore.getState().setLogLevel("debug");

Issue: Logs not appearing in Sentry

Check:
  1. “Send Logs to Sentry” enabled?
  2. Sentry DSN configured? (EXPO_PUBLIC_SENTRY_DSN)
  3. Network connectivity?
Debug:
import * as Sentry from "@sentry/react-native";

// Test Sentry connection
Sentry.captureMessage("Test message");

Issue: Performance impact

Solution:
  1. Disable debug mode in production
  2. Reduce log level to “warn” or “error”
  3. Disable performance tracking when not needed
// In production:
if (!__DEV__) {
  useDebugStore.getState().setLogLevel("error");
  useDebugStore.getState().togglePerformanceMetrics(); // Disable
}

Issue: Logs filling memory

Logs are capped at 100 entries. To manually clear:
useDebugStore.getState().clearLogs();

Next Steps

Planned Enhancements

  1. Clipboard Integration - Copy exported logs automatically
  2. Share Dialog - Email/Slack logs directly from app
  3. Visual Log Viewer - In-app log browser UI
  4. Supabase Query Tracking - Deep Supabase monitoring
  5. Redux DevTools Integration - Time-travel debugging
  6. Remote Logging - Send logs to custom endpoint

Contributing

To add new logging features:
  1. Update debugStore.ts for new config options
  2. Update logger.ts for new logging methods
  3. Update Settings UI for new toggles
  4. Document in this file

Resources

Internal Files


Maintained by: Development Team
Questions? Check CLAUDE.md or Slack #dev-support