[Flutter]앱 크래시 로깅 시스템 구축하기

길위에 히피·2025년 7월 14일

Flutter

목록 보기
52/56

문제 상황

Flutter 앱 개발 중 가장 골치 아픈 문제 중 하나는 앱이 완전히 종료된 상황에서 발생한 크래시를 디버깅하는 것입니다. 일반적인 로깅 방식으로는 앱이 종료되면 로그도 함께 사라지기 때문에, 정확한 크래시 원인을 파악하기 어려웠습니다.

특히 다음과 같은 상황에서 문제가 발생했습니다:

  • 앱이 백그라운드에서 갑자기 종료되는 경우
  • 메모리 부족으로 인한 강제 종료
  • 예외 처리되지 않은 런타임 에러로 인한 크래시

해결 방안: 파일 기반 크래시 로거

이 문제를 해결하기 위해 파일 기반 크래시 로깅 시스템을 구축했습니다. 핵심 아이디어는 로그를 디바이스의 로컬 파일에 저장하여 앱이 종료되더라도 로그가 유지되도록 하는 것입니다.

주요 특징

  1. 영구 저장: 앱 종료 후에도 로그가 유지됩니다
  2. 자동 로테이션: 로그 파일 크기가 1MB를 초과하면 백업 파일로 이동
  3. Sentry 연동: 다음 앱 실행 시 자동으로 Sentry에 로그 전송
  4. 디버그 모드 지원: 개발 중에는 콘솔에도 동시 출력

구현 코드

1. 기본 설정

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

class CrashLogger {
  static const String _logFileName = 'crash_logs.txt';
  static const int _maxLogSize = 1024 * 1024; // 1MB

2. 파일 로깅 메소드

static Future<void> logToFile(String message, {String? tag}) async {
  try {
    final directory = await getApplicationDocumentsDirectory();
    final logFile = File('${directory.path}/$_logFileName');
    
    final timestamp = DateTime.now().toIso8601String();
    final logEntry = '[$timestamp] ${tag != null ? '[$tag] ' : ''}$message\n';
    
    // 파일이 존재하지 않으면 생성
    if (!await logFile.exists()) {
      await logFile.create(recursive: true);
    }
    
    // 파일 크기 체크 및 로테이션
    final fileSize = await logFile.length();
    if (fileSize > _maxLogSize) {
      await _rotateLogFile(logFile);
    }
    
    // 로그 추가
    await logFile.writeAsString(logEntry, mode: FileMode.append);
    
    // 디버그 모드에서는 콘솔에도 출력
    if (kDebugMode) {
      print('CrashLogger: $logEntry');
    }
  } catch (e) {
    // 파일 로깅 실패 시 Sentry에 전송
    Sentry.captureException('Failed to write crash log: $e');
  }
}

3. 로그 파일 로테이션

static Future<void> _rotateLogFile(File logFile) async {
  try {
    final backupFile = File('${logFile.path}.backup');
    if (await backupFile.exists()) {
      await backupFile.delete();
    }
    await logFile.rename(backupFile.path);
    await logFile.create();
  } catch (e) {
    // 로테이션 실패 시 기존 파일 삭제 후 새로 생성
    await logFile.delete();
    await logFile.create();
  }
}

4. Sentry 연동

static Future<void> sendLogsToSentry() async {
  try {
    final logs = await readLogFile();
    if (logs.isNotEmpty && logs != '로그 파일이 존재하지 않습니다.') {
      Sentry.addBreadcrumb(
        Breadcrumb(
          message: 'Previous crash logs',
          data: {'logs': logs},
          timestamp: DateTime.now(),
          level: SentryLevel.info,
        ),
      );
      
      // 로그 전송 후 파일 삭제
      await clearLogFile();
    }
  } catch (e) {
    Sentry.captureException('Failed to send logs to Sentry: $e');
  }
}

사용 방법

1. 로그 작성

// 일반 로그
await CrashLogger.logToFile('사용자가 로그인했습니다');

// 태그가 있는 로그
await CrashLogger.logToFile('API 호출 실패', tag: 'NETWORK');

// 예외 로그
try {
  // 위험한 코드
} catch (e) {
  await CrashLogger.logToFile('예외 발생: $e', tag: 'ERROR');
}

2. 앱 시작 시 로그 전송

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 이전 크래시 로그를 Sentry에 전송
  await CrashLogger.sendLogsToSentry();
  
  runApp(MyApp());
}

3. 개발 중 로그 확인

터미널에서 다음 명령어로 실시간 로그를 확인할 수 있습니다:

flutter logs

실제 활용 사례

1. 네트워크 오류 추적

class ApiService {
  static Future<dynamic> request(String url) async {
    try {
      final response = await http.get(Uri.parse(url));
      await CrashLogger.logToFile('API 요청 성공: $url', tag: 'API');
      return response;
    } catch (e) {
      await CrashLogger.logToFile('API 요청 실패: $url, 에러: $e', tag: 'API_ERROR');
      rethrow;
    }
  }
}

2. 상태 관리 오류 추적

class UserBloc extends Bloc<UserEvent, UserState> {
  
  void onTransition(Transition<UserEvent, UserState> transition) {
    super.onTransition(transition);
    CrashLogger.logToFile(
      '상태 변경: ${transition.currentState} -> ${transition.nextState}',
      tag: 'BLOC'
    );
  }
}

장점

  1. 지속성: 앱이 종료되어도 로그가 유지됩니다
  2. 자동화: 다음 앱 실행 시 자동으로 Sentry에 전송됩니다
  3. 효율성: 파일 크기 제한으로 저장공간을 효율적으로 사용합니다
  4. 개발 친화적: 디버그 모드에서는 콘솔에도 출력됩니다

주의사항

  1. 개인정보 보호: 민감한 정보는 로그에 포함하지 않도록 주의하세요
  2. 성능: 너무 자주 로깅하면 I/O 성능에 영향을 줄 수 있습니다
  3. 권한: 파일 시스템 접근 권한이 필요합니다

결론

파일 기반 크래시 로깅 시스템을 통해 앱 종료 후에도 디버깅 정보를 확보할 수 있게 되었습니다. 특히 프로덕션 환경에서 발생하는 예측하기 어려운 크래시들을 효과적으로 추적할 수 있어, 앱의 안정성 향상에 큰 도움이 되었습니다.

이 시스템을 통해 개발팀은 더 빠르게 문제를 파악하고 해결할 수 있게 되었으며, 사용자 경험 개선에도 기여했습니다.

profile
마음맘은 히피인 일꾼러

0개의 댓글