Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri
İşleme alınabilir çökme raporları için Crashlytics ve Sentry'yi sembol gizleme çözümleme eşlemeleriyle bütünleştirin.
Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri, CoddyKit'te ücretsiz bir Flutter Mobile Development dersidir. Bu, 4 dersinin 3. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Flutter Mobile Development öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Flutter Mobile Development kursu toplamda 4 dersten oluşur.
Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.
Why Raw Crash Traces Are Useless
When you ship a Flutter app in release mode, the Dart compiler produces optimized AOT machine code and strips human-readable symbols. A crash that happens in production no longer reports UserRepository.fetchProfile — it reports a hex offset like #00 abs 0000007f9c2a1b40.
- Debug builds keep symbols, so traces are readable locally.
- Release builds obfuscate and strip, so the on-device trace is just addresses.
- To turn those addresses back into method names you need symbolication (native iOS/Android symbols) and deobfuscation (Dart symbol mapping).
This lesson wires up Crashlytics and Sentry so every production crash arrives fully symbolicated and actionable.
Obfuscation and the Symbol Map File
Flutter obfuscation is opt-in. You enable it at build time and Flutter writes a Dart symbol map per target architecture into a directory you choose.
--obfuscaterenames Dart identifiers to short tokens.--split-debug-infowrites the mapping files (e.g.app.android-arm64.symbols) so you can reverse the obfuscation later.
You must archive these symbol files per release. Without the exact file that matches the shipped binary, the trace can never be deobfuscated.
# Build an obfuscated Android release and keep the Dart symbols
flutter build appbundle --release \
--obfuscate \
--split-debug-info=build/symbols/v1.4.0
# iOS equivalent
flutter build ipa --release \
--obfuscate \
--split-debug-info=build/symbols/v1.4.0Two Layers: Native vs Dart Symbols
A Flutter crash report has two distinct symbolication problems, and confusing them is the #1 reason traces stay unreadable.
- Native layer (iOS dSYM, Android NDK
.sosymbols): resolves crashes inside the engine, plugins, and platform code. Handled by Crashlytics' native symbol upload. - Dart layer (the
--split-debug-infofiles): resolves crashes inside your Dart business logic after obfuscation.
Crashlytics symbolicates the native frames automatically once you upload dSYMs/NDK symbols, but the Dart frames still need the Flutter symbol file run through flutter symbolize.
Initializing Firebase Crashlytics
Crashlytics must be initialized before runApp and wired into Flutter's error hooks so nothing slips through.
FlutterError.onErrorcaptures synchronous framework errors.PlatformDispatcher.instance.onErrorcaptures async errors that escape the zone.
Routing both into recordFlutterFatalError / recordError guarantees every uncaught Dart error reaches Crashlytics.
import 'dart:ui';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final crashlytics = FirebaseCrashlytics.instance;
FlutterError.onError = crashlytics.recordFlutterFatalError;
PlatformDispatcher.instance.onError = (error, stack) {
crashlytics.recordError(error, stack, fatal: true);
return true;
};
runApp(const MyApp());
}Uploading Native Symbols to Crashlytics
For native frames, Crashlytics needs the build's symbol files uploaded to Firebase.
- iOS: the
FirebaseCrashlyticsrun script uploads dSYMs; for bitcode or missing dSYMs use theupload-symbolstool. - Android: the
firebase-crashlyticsGradle plugin uploads NDK symbols whennativeSymbolUploadEnabledis true.
These uploads happen at build/release time, not at runtime, so they belong in your CI pipeline.
// android/app/build.gradle
plugins {
id 'com.google.firebase.crashlytics'
}
android {
buildTypes {
release {
firebaseCrashlytics {
// Upload Android NDK native symbols
nativeSymbolUploadEnabled true
unstrippedNativeLibsDir 'build/app/intermediates/merged_native_libs'
}
}
}
}Deobfuscating Dart Frames with flutter symbolize
Crashlytics cannot read your Dart --split-debug-info map. When a crash's Dart frames show obfuscated tokens, you export the raw trace and run it through the Flutter SDK tool.
flutter symbolizetakes the obfuscated stack trace plus the exact symbol file for that ABI and version.- The output replaces hex offsets and short tokens with real Dart method names and line numbers.
This is why archiving the per-release symbol directory is non-negotiable — you need the file matching the crashing build.
# stack.txt holds the obfuscated trace copied from Crashlytics
flutter symbolize \
-i stack.txt \
-d build/symbols/v1.4.0/app.android-arm64.symbols
# Output now shows real frames, e.g.:
# #00 UserRepository.fetchProfile (package:app/data/user_repository.dart:42)Adding Sentry Alongside Crashlytics
Sentry is a popular alternative or companion to Crashlytics. The Flutter SDK wraps your app and auto-captures unhandled errors, but the real value is its debug symbol pipeline.
SentryFlutter.initinstalls the integrations and lets you set sampling, release, and environment.- Wrapping
runAppinappRunnerensures the zone-level error handler is active.
Set the release string to name+buildNumber so Sentry can match the crash to the correct uploaded symbols.
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = 'https://examplePublicKey@o0.ingest.sentry.io/0';
options.tracesSampleRate = 0.2;
options.environment = 'production';
options.release = 'com.coddykit.app@1.4.0+140';
},
appRunner: () => runApp(const MyApp()),
);
}Uploading Debug Symbols to Sentry
Sentry deobfuscates automatically server-side once you upload the right artifacts with sentry-cli (or the Sentry Dart plugin during the build).
- Upload the native dSYM / ELF debug files via
debug-files upload. - Upload the Flutter Dart symbols (the
--split-debug-infooutput) so obfuscated Dart frames resolve.
Because Sentry matches by debug ID and release, uploads from CI must use the same --split-debug-info directory that produced the shipped binary.
# Upload native + Dart debug information files for this release
sentry-cli debug-files upload \
--org coddykit --project flutter-app \
build/symbols/v1.4.0
# Associate the release so Sentry can match incoming events
sentry-cli releases new com.coddykit.app@1.4.0+140
sentry-cli releases finalize com.coddykit.app@1.4.0+140Enriching Reports with Context and Breadcrumbs
A symbolicated trace tells you where; context tells you why. Both SDKs let you attach metadata so a crash is reproducible.
- Custom keys: feature flags, user tier, current route.
- Breadcrumbs / logs: a trail of recent actions leading to the crash.
- User identifier: to gauge how many users a crash affects.
Never log PII. Use opaque IDs and feature-relevant flags only.
Future<void> tagCrashContext(FirebaseCrashlytics c) async {
await c.setUserIdentifier('user_8f3a');
await c.setCustomKey('subscription', 'pro');
await c.setCustomKey('active_route', '/checkout');
await c.log('Tapped Pay button with cart total 49.99');
}Recording Handled (Non-Fatal) Errors
Not every error should crash the app. A failed network call or a caught parsing error is a non-fatal event you still want visibility into.
- Crashlytics:
recordError(e, st, fatal: false). - Sentry:
Sentry.captureException(e, stackTrace: st).
This standalone Dart example shows the catch-and-report pattern you would feed into either SDK from a service layer.
void reportHandled(Object error, StackTrace stack) {
// In production this would call recordError / captureException.
print('NON-FATAL: $error');
print('Top frame: ${stack.toString().split('\n').first}');
}
Map<String, dynamic> parseProfile(String raw) {
if (!raw.startsWith('{')) {
throw const FormatException('Profile payload is not JSON');
}
return {'ok': true};
}
void main() {
try {
parseProfile('not-json');
} catch (e, st) {
reportHandled(e, st);
}
print('App keeps running after handled error');
}Making Symbol Upload Part of CI
The single biggest cause of unreadable production crashes is a broken release process: someone ships a build but forgets to upload or archive its symbols. Automate it.
- Build with
--obfuscate --split-debug-info=build/symbols/$VERSION. - Upload native symbols (Crashlytics Gradle plugin / iOS run script) and Dart symbols (
sentry-cli) in the same CI job. - Persist
build/symbols/$VERSIONas a CI artifact retained for as long as the version can crash in the wild.
Tie the version string used everywhere — Sentry release, Crashlytics setCustomKey, and the symbol directory name — to one source of truth so matching never drifts.
# CI snippet (bash) — one job that builds and ships symbols
VERSION=$(grep '^version:' pubspec.yaml | awk '{print $2}')
flutter build appbundle --release \
--obfuscate --split-debug-info=build/symbols/$VERSION
# Dart + native symbols to Sentry
sentry-cli debug-files upload --org coddykit --project flutter-app \
build/symbols/$VERSION
# Keep the map for later flutter symbolize runs
tar -czf symbols-$VERSION.tgz build/symbols/$VERSIONQuick Check: Why Dart Frames Stay Obfuscated
You shipped a Flutter release built with --obfuscate --split-debug-info. Native frames in Crashlytics are readable, but the Dart frames in your business logic still show short tokens and hex offsets. What is the correct fix?
Recap: An Actionable Crash Pipeline
You now have an end-to-end pipeline that turns cryptic production crashes into fixable bug reports.
- Build release with
--obfuscate --split-debug-info=build/symbols/$VERSIONand archive that directory per release. - Capture via
FlutterError.onError+PlatformDispatcher.onError(Crashlytics) and/orSentryFlutter.initwithappRunner. - Symbolicate two layers: native (dSYM/NDK uploads) and Dart (
flutter symbolizefor Crashlytics, automatic server-side for Sentry after symbol upload). - Enrich with user IDs, custom keys, and breadcrumbs — no PII.
- Automate symbol upload in CI and keep one version string as the single source of truth.
The golden rule: a crash report is only as good as the symbol file you kept for that exact build.
Sıkça Sorulan Sorular
“Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri” dersi ücretsiz mi?
Evet — “Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Flutter Mobile Development kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Flutter Mobile Development kursu toplamda 4 dersten oluşur.
“Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri” dersinde ne öğreneceğim?
İşleme alınabilir çökme raporları için Crashlytics ve Sentry'yi sembol gizleme çözümleme eşlemeleriyle bütünleştirin. Flutter Mobile Development ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Flutter Mobile Development öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Flutter Mobile Development, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 3. dersidir.
“Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Flutter Mobile Development dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Flutter Mobile Development dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Derleme Çeşitleri ve Ortam Yapılandırması
- Fastlane ve GitHub Actions ile Otomatik İş Akışları
- Çökme Raporlama ve Sembolleri Çözülmüş Yığın İzleri
- Uzaktan Yapılandırma, Özellik Bayrakları ve Aşamalı Yayınlar