Skip to content

Çökme Raporlama Sistemleri

Genel Bakış

Mobil uygulamalarda çökme raporlama, uygulamanızın kararlılığını izlemek ve kullanıcı deneyimini iyileştirmek için kritik öneme sahiptir. Bu dokümantasyon modern crash reporting sistemlerinin implementasyonunu kapsar.

Platform-Specific Crash Reporting

iOS Crash Reporting

swift
import FirebaseCrashlytics

class CrashReportingManager {
    static func initialize() {
        Crashlytics.crashlytics().setUserID(UserManager.shared.currentUser?.id ?? "anonymous")
        
        // Custom keys ekle
        Crashlytics.crashlytics().setCustomValue(AppVersion.fullVersion, forKey: "app_version")
        Crashlytics.crashlytics().setCustomValue(UIDevice.current.model, forKey: "device_model")
        Crashlytics.crashlytics().setCustomValue(UIDevice.current.systemVersion, forKey: "os_version")
    }
    
    static func logError(_ error: Error, additionalInfo: [String: Any] = [:]) {
        // Custom keys ekle
        additionalInfo.forEach { key, value in
            Crashlytics.crashlytics().setCustomValue(value, forKey: key)
        }
        
        // Error'u raporla
        Crashlytics.crashlytics().record(error: error)
    }
    
    static func logNonFatalError(_ message: String, 
                                userInfo: [String: Any] = [:],
                                file: String = #file,
                                function: String = #function,
                                line: Int = #line) {
        let error = NSError(
            domain: "com.app.nonfatal",
            code: -1,
            userInfo: [
                NSLocalizedDescriptionKey: message,
                "file": file,
                "function": function,
                "line": line
            ].merging(userInfo) { $1 }
        )
        
        Crashlytics.crashlytics().record(error: error)
    }
    
    static func setBreadcrumb(_ message: String, category: String = "general") {
        Crashlytics.crashlytics().log("\(category): \(message)")
    }
    
    static func setUserContext(_ user: User) {
        let crashlytics = Crashlytics.crashlytics()
        crashlytics.setUserID(user.id)
        crashlytics.setCustomValue(user.email, forKey: "user_email")
        crashlytics.setCustomValue(user.subscriptionType, forKey: "subscription_type")
        crashlytics.setCustomValue(user.registrationDate, forKey: "registration_date")
    }
}

// Usage örneği
extension NetworkManager {
    func handleAPIError(_ error: APIError, endpoint: String) {
        CrashReportingManager.logNonFatalError(
            "API request failed",
            userInfo: [
                "endpoint": endpoint,
                "error_code": error.code,
                "error_message": error.localizedDescription,
                "retry_count": error.retryCount
            ]
        )
    }
}

Android Crash Reporting

kotlin
import com.google.firebase.crashlytics.FirebaseCrashlytics

class CrashReportingManager {
    companion object {
        private val crashlytics = FirebaseCrashlytics.getInstance()
        
        fun initialize(context: Context) {
            val user = UserManager.getCurrentUser()
            crashlytics.setUserId(user?.id ?: "anonymous")
            
            // Custom keys
            crashlytics.setCustomKey("app_version", BuildConfig.VERSION_NAME)
            crashlytics.setCustomKey("device_model", Build.MODEL)
            crashlytics.setCustomKey("os_version", Build.VERSION.RELEASE)
            crashlytics.setCustomKey("sdk_version", Build.VERSION.SDK_INT)
        }
        
        fun logError(throwable: Throwable, additionalInfo: Map<String, String> = emptyMap()) {
            // Custom keys ekle
            additionalInfo.forEach { (key, value) ->
                crashlytics.setCustomKey(key, value)
            }
            
            // Error'u raporla
            crashlytics.recordException(throwable)
        }
        
        fun logNonFatalError(
            message: String,
            additionalInfo: Map<String, String> = emptyMap(),
            cause: Throwable? = null
        ) {
            val exception = NonFatalException(message, cause)
            
            additionalInfo.forEach { (key, value) ->
                crashlytics.setCustomKey(key, value)
            }
            
            crashlytics.recordException(exception)
        }
        
        fun setBreadcrumb(message: String, category: String = "general") {
            crashlytics.log("$category: $message")
        }
        
        fun setUserContext(user: User) {
            crashlytics.setUserId(user.id)
            crashlytics.setCustomKey("user_email", user.email)
            crashlytics.setCustomKey("subscription_type", user.subscriptionType)
            crashlytics.setCustomKey("registration_date", user.registrationDate.toString())
        }
    }
    
    class NonFatalException(message: String, cause: Throwable? = null) : Exception(message, cause)
}

// Usage örneği
class NetworkManager {
    fun handleAPIError(error: APIError, endpoint: String) {
        CrashReportingManager.logNonFatalError(
            "API request failed",
            mapOf(
                "endpoint" to endpoint,
                "error_code" to error.code.toString(),
                "error_message" to error.message,
                "retry_count" to error.retryCount.toString()
            )
        )
    }
}

Custom Crash Reporting Implementation

Local Crash Handler

swift
// iOS Custom Crash Handler
class LocalCrashHandler {
    private static let crashLogPath = FileManager.default.urls(for: .documentDirectory, 
                                                              in: .userDomainMask)[0]
        .appendingPathComponent("crash_logs")
    
    static func setup() {
        // NSSetUncaughtExceptionHandler kullanımı
        NSSetUncaughtExceptionHandler { exception in
            handleCrash(exception: exception)
        }
        
        // Signal handler kurulumu
        signal(SIGABRT) { signal in
            handleSignal(signal)
        }
        signal(SIGILL) { signal in
            handleSignal(signal)
        }
        signal(SIGSEGV) { signal in
            handleSignal(signal)
        }
    }
    
    private static func handleCrash(exception: NSException) {
        let crashReport = CrashReport(
            timestamp: Date(),
            type: .exception,
            name: exception.name.rawValue,
            reason: exception.reason ?? "Unknown",
            stackTrace: exception.callStackSymbols,
            deviceInfo: getDeviceInfo(),
            appInfo: getAppInfo()
        )
        
        saveCrashReport(crashReport)
    }
    
    private static func handleSignal(_ signal: Int32) {
        let crashReport = CrashReport(
            timestamp: Date(),
            type: .signal,
            name: "Signal \(signal)",
            reason: "Signal caught",
            stackTrace: Thread.callStackSymbols,
            deviceInfo: getDeviceInfo(),
            appInfo: getAppInfo()
        )
        
        saveCrashReport(crashReport)
        exit(signal)
    }
    
    private static func saveCrashReport(_ report: CrashReport) {
        do {
            try FileManager.default.createDirectory(at: crashLogPath, 
                                                   withIntermediateDirectories: true)
            
            let fileName = "crash_\(Int(report.timestamp.timeIntervalSince1970)).json"
            let fileURL = crashLogPath.appendingPathComponent(fileName)
            
            let data = try JSONEncoder().encode(report)
            try data.write(to: fileURL)
            
            print("Crash report saved: \(fileURL)")
        } catch {
            print("Failed to save crash report: \(error)")
        }
    }
    
    static func sendPendingCrashReports() {
        do {
            let files = try FileManager.default.contentsOfDirectory(at: crashLogPath, 
                                                                   includingPropertiesForKeys: nil)
            
            for file in files where file.pathExtension == "json" {
                let data = try Data(contentsOf: file)
                let report = try JSONDecoder().decode(CrashReport.self, from: data)
                
                uploadCrashReport(report) { success in
                    if success {
                        try? FileManager.default.removeItem(at: file)
                    }
                }
            }
        } catch {
            print("Failed to process crash reports: \(error)")
        }
    }
    
    private static func uploadCrashReport(_ report: CrashReport, completion: @escaping (Bool) -> Void) {
        // Backend'e crash report gönder
        APIService.uploadCrashReport(report) { result in
            switch result {
            case .success:
                completion(true)
            case .failure(let error):
                print("Failed to upload crash report: \(error)")
                completion(false)
            }
        }
    }
}

struct CrashReport: Codable {
    let timestamp: Date
    let type: CrashType
    let name: String
    let reason: String
    let stackTrace: [String]
    let deviceInfo: DeviceInfo
    let appInfo: AppInfo
}

enum CrashType: String, Codable {
    case exception
    case signal
}

Advanced Crash Analysis

Symbolication ve Stack Trace Analysis

python
# Backend crash analysis script
import re
import requests
from typing import List, Dict

class CrashAnalyzer:
    def __init__(self, dsym_path: str):
        self.dsym_path = dsym_path
        
    def symbolicate_crash_report(self, crash_report: Dict) -> Dict:
        """iOS crash report'unu symbolicate et"""
        symbolicated_trace = []
        
        for frame in crash_report['stack_trace']:
            # Binary address'i extract et
            address_match = re.search(r'0x[0-9a-fA-F]+', frame)
            if address_match:
                address = address_match.group()
                symbolicated_frame = self.symbolicate_address(address)
                symbolicated_trace.append(symbolicated_frame)
            else:
                symbolicated_trace.append(frame)
        
        crash_report['symbolicated_stack_trace'] = symbolicated_trace
        return crash_report
    
    def symbolicate_address(self, address: str) -> str:
        """Belirli bir address'i symbolicate et"""
        try:
            # atos komutu ile symbolication
            import subprocess
            result = subprocess.run([
                'atos', '-o', self.dsym_path, '-arch', 'arm64', address
            ], capture_output=True, text=True)
            
            if result.returncode == 0:
                return result.stdout.strip()
            else:
                return f"<unable to symbolicate {address}>"
        except Exception as e:
            return f"<symbolication failed: {e}>"
    
    def analyze_crash_pattern(self, crash_reports: List[Dict]) -> Dict:
        """Crash pattern analizi"""
        patterns = {
            'most_common_crashes': {},
            'affected_versions': {},
            'device_distribution': {},
            'time_analysis': []
        }
        
        for report in crash_reports:
            # En yaygın crash'ler
            crash_signature = f"{report['name']}: {report['reason']}"
            patterns['most_common_crashes'][crash_signature] = \
                patterns['most_common_crashes'].get(crash_signature, 0) + 1
            
            # Etkilenen versiyonlar
            version = report['app_info']['version']
            patterns['affected_versions'][version] = \
                patterns['affected_versions'].get(version, 0) + 1
            
            # Cihaz dağılımı
            device = report['device_info']['model']
            patterns['device_distribution'][device] = \
                patterns['device_distribution'].get(device, 0) + 1
        
        return patterns
    
    def generate_crash_dashboard_data(self, patterns: Dict) -> Dict:
        """Dashboard için veri oluştur"""
        return {
            'summary': {
                'total_crashes': sum(patterns['most_common_crashes'].values()),
                'unique_crash_types': len(patterns['most_common_crashes']),
                'affected_versions': len(patterns['affected_versions']),
                'affected_devices': len(patterns['device_distribution'])
            },
            'top_crashes': sorted(
                patterns['most_common_crashes'].items(),
                key=lambda x: x[1],
                reverse=True
            )[:10],
            'version_impact': patterns['affected_versions'],
            'device_impact': patterns['device_distribution']
        }

Real-time Crash Monitoring

WebSocket-based Real-time Updates

typescript
// React Dashboard için real-time crash monitoring
import { io, Socket } from 'socket.io-client';

class CrashMonitoringDashboard {
    private socket: Socket;
    private crashData: CrashData[] = [];
    
    constructor() {
        this.socket = io('ws://crash-monitoring-server:3000');
        this.setupEventListeners();
    }
    
    private setupEventListeners() {
        this.socket.on('new_crash', (crashData: CrashData) => {
            this.handleNewCrash(crashData);
        });
        
        this.socket.on('crash_pattern_alert', (alert: CrashAlert) => {
            this.handleCrashAlert(alert);
        });
        
        this.socket.on('crash_statistics', (stats: CrashStatistics) => {
            this.updateStatistics(stats);
        });
    }
    
    private handleNewCrash(crashData: CrashData) {
        // Yeni crash'i listeye ekle
        this.crashData.unshift(crashData);
        
        // Critical crash ise immediate alert
        if (crashData.severity === 'critical') {
            this.showCriticalAlert(crashData);
        }
        
        // Real-time chart'ları güncelle
        this.updateCrashCharts();
        
        // Slack/Discord notification gönder
        this.sendNotification(crashData);
    }
    
    private handleCrashAlert(alert: CrashAlert) {
        // Pattern-based alert (örnek: crash rate %50 arttı)
        this.showPatternAlert(alert);
        
        // Auto-scale investigation
        if (alert.type === 'spike') {
            this.triggerAutoInvestigation(alert);
        }
    }
    
    private triggerAutoInvestigation(alert: CrashAlert) {
        // Otomatik analiz başlat
        fetch('/api/crash/auto-investigate', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                alertId: alert.id,
                timeRange: alert.timeRange,
                affectedVersions: alert.affectedVersions
            })
        });
    }
    
    async getCrashTrends(timeRange: string): Promise<CrashTrend[]> {
        const response = await fetch(`/api/crash/trends?range=${timeRange}`);
        return response.json();
    }
    
    async getTopCrashIssues(limit: number = 10): Promise<CrashIssue[]> {
        const response = await fetch(`/api/crash/top-issues?limit=${limit}`);
        return response.json();
    }
}

interface CrashData {
    id: string;
    timestamp: Date;
    appVersion: string;
    platform: 'ios' | 'android';
    deviceModel: string;
    osVersion: string;
    crashType: string;
    stackTrace: string[];
    severity: 'low' | 'medium' | 'high' | 'critical';
    userImpact: number;
}

interface CrashAlert {
    id: string;
    type: 'spike' | 'new_crash' | 'regression';
    message: string;
    timeRange: string;
    affectedVersions: string[];
    impactLevel: number;
}

Bu crash reporting sistemi ile uygulamanızın kararlılığını proaktif olarak izleyebilir ve kullanıcı deneyimini sürekli iyileştirebilirsiniz.

Eren Demir tarafından oluşturulmuştur.