Crash Reporting and Debugging in Production

Whistl uses comprehensive crash reporting to quickly identify and fix issues. This technical guide explains crash symbolication, error grouping, release tracking, performance monitoring, and how Whistl maintains 99.9% crash-free sessions.

Why Crash Reporting Matters

Crashes directly impact user trust and safety:

  • Protection gaps: Crashed app = no protection
  • User trust: Frequent crashes erode confidence
  • Data loss: Unsaved data lost on crash
  • App store ratings: Crashes affect reviews
  • Debugging: Production issues hard to reproduce locally

Whistl targets 99.9% crash-free sessions through proactive monitoring.

Crash Reporting Architecture

Whistl uses Sentry for comprehensive error tracking:

Integration Overview

┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│   Client    │    │   Sentry     │    │   Team      │
│   App       │───▶│   Servers    │───▶│   Dashboard │
│             │    │              │    │             │
└─────────────┘    └──────────────┘    └─────────────┘
      │                    │                    │
      │  Crash Report      │  Alert             │
      │  - Stack trace     │  - Email           │
      │  - Breadcrumbs     │  - Slack           │
      │  - Context         │  - PagerDuty       │
      │  - User info       │                    │
      │                    │                    │
      ▼                    ▼                    ▼
  Capture              Process              Triage
  Report               Group                Fix

iOS Crash Reporting

Sentry SDK captures crashes and errors on iOS:

Sentry Configuration

import Sentry

class CrashReporting {
    static func start() {
        SentrySDK.start { options in
            options.dsn = "https://xxx@o123456.ingest.sentry.io/123456"
            
            // Sample rate for transactions (performance)
            options.tracesSampleRate = 0.1  // 10%
            
            // Sample rate for profiling
            options.profilesSampleRate = 0.1
            
            // Enable automatic breadcrumbs
            options.enableAutoSessionTracking = true
            options.sessionTrackingIntervalMillis = 30000
            
            // Attach stack trace to non-fatal errors
            options.attachStacktrace = true
            
            // Maximum breadcrumbs
            options.maxBreadcrumbs = 100
            
            // Before send hook for filtering
            options.beforeSend = { event in
                // Filter out sensitive data
                event.user?.email = nil
                event.user?.username = nil
                return event
            }
        }
    }
}

Manual Error Reporting

// Capture non-fatal errors
do {
    try performRiskyOperation()
} catch {
    SentrySDK.capture(error: error) { scope in
        scope.setLevel(.warning)
        scope.setTag(value: "network", key: "category")
        scope.setExtra(value: endpoint, key: "api_endpoint")
    }
}

// Capture messages
SentrySDK.capture(message: "Unexpected state") { scope in
    scope.setLevel(.info)
    scope.setContext(value: ["state": currentState], key: "debug")
}

// Capture NSException
SentrySDK.capture(exception: exception)

Breadcrumbs

// Add breadcrumbs for context
SentrySDK.addBreadcrumb(crumb: Breadcrumb(level: .info, category: "navigation")) {
    $0.message = "Navigated to transaction detail"
    $0.data = ["transaction_id": transaction.id]
}

SentrySDK.addBreadcrumb(crumb: Breadcrumb(level: .info, category: "network")) {
    $0.message = "API Request"
    $0.data = [
        "method": "POST",
        "url": "/api/transactions",
        "status": 200,
        "duration_ms": 234
    ]
}

// Breadcrumbs automatically included in crash reports
// Shows what happened before the crash

Android Crash Reporting

Sentry SDK for Android captures crashes and ANRs:

Sentry Configuration (Android)

import io.sentry.android.core.SentryAndroid

class Application : Application() {
    override fun onCreate() {
        super.onCreate()
        
        SentryAndroid.init(this) { options ->
            options.dsn = "https://xxx@o123456.ingest.sentry.io/123456"
            
            // Enable ANR (Application Not Responding) tracking
            options.isEnableANRTracking = true
            
            // Sample rate for transactions
            options.tracesSampleRate = 0.1
            
            // Enable profiling
            options.profilesSampleRate = 0.1
            
            // Attach threads to events
            options.isAttachThreads = true
            
            // Before send callback
            options.beforeSend = SentryOptions.BeforeSendCallback { event, hint ->
                // Filter sensitive data
                event.user?.email = null
                event.user?.username = null
                event
            }
        }
    }
}

Manual Error Reporting (Android)

import io.sentry.Sentry

// Capture exception
try {
    performRiskyOperation()
} catch (e: Exception) {
    Sentry.captureException(e) { scope ->
        scope.setLevel(SentryLevel.WARNING)
        scope.setTag("category", "network")
        scope.setExtra("api_endpoint", endpoint)
    }
}

// Capture message
Sentry.captureMessage("Unexpected state") { scope ->
    scope.setLevel(SentryLevel.INFO)
    scope.setContexts("debug", mapOf("state" to currentState))
}

// Add breadcrumb
Sentry.addBreadcrumb { breadcrumb ->
    breadcrumb.category = "navigation"
    breadcrumb.message = "Navigated to transaction detail"
    breadcrumb.setData("transaction_id", transaction.id)
}

Crash Symbolication

Raw crash reports contain memory addresses that need symbolication:

dSYM Files (iOS)

# Upload dSYM to Sentry
sentry-cli upload-dsym \
    --org whistl \
    --project ios \
    ./build/Products/Release-iphoneos/Whistl.app.dSYM

# Or configure in Xcode Build Phase
sentry-cli upload-dsym \
    --org whistl \
    --project ios

ProGuard Mapping (Android)

# Sentry automatically uploads mapping files
# Configure in build.gradle

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

# Sentry Gradle plugin handles upload automatically
sentry {
    autoUploadProguardMapping = true
}

Symbolicated Crash Report

Fatal Exception: NSInvalidArgumentException
0  CoreFoundation  -[NSDictionary objectForKey:]
1  Whistl          TransactionViewModel.updateState()
                   (TransactionViewModel.swift:142)
2  Whistl          TransactionView.onAppear()
                   (TransactionView.swift:67)
3  SwiftUI         View.onAppear()
4  libdispatch     _dispatch_call_block_and_release

Context:
- Device: iPhone 14 Pro
- iOS: 17.2.1
- App Version: 1.8.2 (234)
- User ID: usr-abc123 (anonymized)
- Free Storage: 2.3 GB
- Battery Level: 45%

Error Grouping

Similar crashes are grouped together:

Grouping Rules

  • Same exception type: NullPointerException grouped together
  • Same stack trace: Same location in code
  • Custom fingerprints: Override default grouping

Custom Grouping

SentrySDK.configureScope { scope in
    // Set fingerprint for custom grouping
    scope.setFingerprint(["api_error", endpoint, statusCode])
}

// All API errors for same endpoint+status grouped together
// Even if error message differs

Release Tracking

Crashes are tracked by app version:

Release Configuration

// Set release information
SentrySDK.configureScope { scope in
    scope.setDist(BuildNumber.current)
    // e.g., "234"
}

// Sentry automatically detects release from:
// - iOS: CFBundleVersion in Info.plist
// - Android: versionCode in build.gradle

// View crashes by release in dashboard:
// - v1.8.2 (234): 0.1% crash rate
// - v1.8.1 (230): 0.3% crash rate
// - v1.8.0 (225): 0.2% crash rate

Performance Monitoring

Beyond crashes, Whistl tracks performance:

Transaction Tracing

// Start transaction
let transaction = SentrySDK.startTransaction(
    name: "App Launch",
    operation: "app.launch",
    bindToScope: true
)

// Create child spans
let dbSpan = transaction.startChild(
    operation: "db.query",
    description: "Fetch transactions"
)
// ... perform query
dbSpan.finish()

let networkSpan = transaction.startChild(
    operation: "http.request",
    description: "GET /api/transactions"
)
// ... perform request
networkSpan.finish()

// Finish transaction
transaction.finish()

Performance Metrics

MetricP50P75P90P99
App Launch (cold)1.2s1.5s2.1s3.4s
Transaction List Load230ms340ms520ms890ms
ML Inference5ms7ms10ms18ms
DNS Block Decision<1ms<1ms1ms2ms

Alerting

Team is alerted to critical issues:

Alert Configuration

  • New issue: Slack notification immediately
  • Regression: Email when fixed issue reappears
  • Volume spike: PagerDuty when crash rate spikes
  • Release issues: Alert when new release has high crash rate

Alert Rules

# Sentry Alert Rules
{
  "name": "High Crash Rate",
  "conditions": [
    {"id": "sentry.rules.conditions.event_frequency", "value": 100, "comparison": 100}
  ],
  "filters": [
    {"id": "sentry.rules.filters.level", "level": "fatal"}
  ],
  "actions": [
    {"id": "sentry.rules.actions.notify_event_service", "service": "slack"},
    {"id": "sentry.rules.actions.notify_event_service", "service": "pagerduty"}
  ]
}

Privacy in Crash Reports

Crash reports are scrubbed of sensitive data:

Data Scrubbing

  • User IDs: Anonymized before sending
  • Email/Phone: Removed from all fields
  • Financial data: Transaction amounts removed
  • Location: Coarse location only (city level)
  • Custom data: Reviewed before enabling

Crash-Free Metrics

Whistl maintains excellent crash-free rates:

Crash-Free Sessions

PlatformTargetActual
iOS99.9%99.94%
Android99.9%99.91%
Overall99.9%99.93%

Conclusion

Comprehensive crash reporting enables Whistl to quickly identify and fix issues before they impact many users. Through Sentry integration, automatic symbolication, performance monitoring, and privacy-conscious reporting, Whistl maintains 99.9%+ crash-free sessions.

Every crash is an opportunity to improve—Whistl treats each one as a priority fix.

Experience Reliable Protection

Whistl maintains 99.9% crash-free sessions through comprehensive monitoring. Download free and experience reliable protection.

Download Whistl Free

Related: API Rate Limiting | A/B Testing Infrastructure | Privacy-Compliant Analytics