Crashrapportering og symboliserede stack traces
Integrér Crashlytics og Sentry med mappings til deobfuskering for brugbare crashrapporter.
Crashrapportering og symboliserede stack traces er en gratis Mobiludvikling med Flutter-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Mobiludvikling med Flutter, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Mobiludvikling med Flutter-kurset indeholder 4 lektioner i alt.
Hvorfor rå crashtraces er ubrugelige
Når du sender en Flutter-app ud i release mode, producerer Dart-compileren optimeret AOT-maskinkode og fjerner læsbare symboler. Et nedbrud, der sker i produktion, rapporterer ikke længere UserRepository.fetchProfile — det rapporterer en hex-offset som #00 abs 0000007f9c2a1b40.
- Debug-builds bevarer symboler, så traces er læsbare lokalt.
- Release-builds obfuskerer og fjerner symboler, så tracen på enheden kun består af adresser.
- For at omsætte adresserne til methodenavne igen skal du bruge symbolication (native iOS-/Android-symboler) og deobfuskering (Dart-symbolmapping).
I denne lektion kobler du Crashlytics og Sentry til, så alle nedbrud i produktion ankommer fuldt symbolicatede og kan handles på.
Obfuskering og symbolmap-filen
Flutter-obfuskering er valgfri. Du aktiverer den ved buildtidspunktet, og Flutter skriver et Dart-symbolmap pr. målarkitektur til en mappe, du vælger.
--obfuscateomdøber Dart-identifikatorer til korte tokens.--split-debug-infoskriver mappingfilerne (f.eks.app.android-arm64.symbols), så du senere kan ophæve obfuskeringen.
Du skal arkivere disse symbolfiler for hver release. Uden den nøjagtige fil, der passer til det udsendte binary, kan tracen aldrig deobfuskeres.
# 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.0To lag: Native- og Dart-symboler
En Flutter-crashrapport har to forskellige symbolication-problemer, og forveksling af dem er den primære årsag til, at traces forbliver ulæselige.
- Native-laget (iOS dSYM, Android NDK-
.so-symboler): løser nedbrud inde i engine, plugins og platformskode. Håndteres af Crashlytics' native symbolupload. - Dart-laget (filerne fra
--split-debug-info): løser nedbrud i din Dart-forretningslogik efter obfuskering.
Crashlytics symbolikerer automatisk de native frames, når du uploader dSYM-/NDK-symboler, men Dart-framene skal stadig have Flutter-symbolfilen kørt gennem flutter symbolize.
Initialisering af Firebase Crashlytics
Crashlytics skal initialiseres før runApp og kobles til Flutter's error hooks, så intet slipper igennem.
FlutterError.onErroropfanger synkrone framework-fejl.PlatformDispatcher.instance.onErroropfanger asynkrone fejl, der slipper ud af zonen.
Ved at sende begge videre til recordFlutterFatalError / recordError sikrer du, at alle uopfangede Dart-fejl når frem til 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());
}Upload af native symboler til Crashlytics
For native frames skal Crashlytics have buildets symbolfiler uploadet til Firebase.
- iOS:
FirebaseCrashlytics-runscriptet uploader dSYMs; ved bitcode eller manglende dSYMs skal du bruge værktøjetupload-symbols. - Android:
firebase-crashlytics-Gradle-pluginet uploader NDK-symboler, nårnativeSymbolUploadEnableder true.
Disse uploads sker ved build/release-tidspunktet, ikke under kørsel, så de hører hjemme i din 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'
}
}
}
}Deobfuskering af Dart-frames med flutter symbolize
Crashlytics kan ikke læse dit Dart---split-debug-info-map. Når et crashs Dart-frames viser obfuskerede tokens, eksporterer du den rå trace og kører den gennem værktøjet i Flutter SDK'et.
flutter symbolizetager den obfuskerede stacktrace samt den nøjagtige symbolfil for den pågældende ABI og version.- Outputtet erstatter hex-offsets og korte tokens med rigtige Dart-methodenavne og linjenumre.
Det er derfor, arkivering af symbolmappen for hver release er ufravigelig — du skal bruge filen, der passer til det build, hvor nedbruddet skete.
# 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)Tilføjelse af Sentry sammen med Crashlytics
Sentry er et populært alternativ til eller supplement til Crashlytics. Flutter SDK'et indpakker din app og opfanger automatisk ubehandlede fejl, men den virkelige værdi ligger i dens pipeline til debugsymboler.
SentryFlutter.initinstallerer integrationerne og lader dig angive sampling, release og miljø.- Ved at indpakke
runAppiappRunnersikrer du, at fejlhandleren på zoneniveau er aktiv.
Sæt release-strengen til name+buildNumber, så Sentry kan matche nedbruddet med de korrekte uploadede symboler.
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()),
);
}Upload af debugsymboler til Sentry
Sentry deobfuskerer automatisk på serversiden, når du uploader de rigtige artefakter med sentry-cli (eller Sentry Dart-pluginet under buildet).
- Upload de native dSYM-/ELF-debugfiler via
debug-files upload. - Upload Flutter Dart-symbolerne (outputtet fra
--split-debug-info), så obfuskerede Dart-frames kan opløses.
Fordi Sentry matcher efter debug-id og release, skal uploads fra CI bruge den samme --split-debug-info-mappe, som producerede det udsendte 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+140Berigelse af rapporter med kontekst og brødkrummer
Et symboliseret stacktrace fortæller dig hvor; kontekst fortæller dig hvorfor. Begge SDK'er lader dig knytte metadata til, så et nedbrud kan genskabes.
- Brugerdefinerede nøgler: funktionsflag, brugerniveau, aktuel rute.
- Brødkrummer / loglinjer: et spor af de seneste handlinger, der førte til nedbruddet.
- Bruger-id: så du kan vurdere, hvor mange brugere et nedbrud påvirker.
Log aldrig personhenførbare oplysninger. Brug kun uigennemsigtige id'er og flag, der er relevante for funktionerne.
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');
}Registrering af håndterede fejl (ikke-fatale)
Det er ikke alle fejl, der bør få appen til at gå ned. Et mislykket netværkskald eller en opfanget fortolkningsfejl er en ikke-fatal hændelse, som du stadig gerne vil have indsigt i.
- Crashlytics:
recordError(e, st, fatal: false). - Sentry:
Sentry.captureException(e, stackTrace: st).
Dette selvstændige Dart-eksempel viser mønsteret for at opfange og rapportere, som du ville sende til et af SDK'erne fra et servicelag.
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');
}Gør symbolupload til en del af CI
Den klart største årsag til ulæselige nedbrud i produktion er en ødelagt udgivelsesproces: Nogen sender en build ud, men glemmer at uploade eller arkivere dens symboler. Automatisér det.
- Byg med
--obfuscate --split-debug-info=build/symbols/$VERSION. - Upload platformsymboler (Crashlytics Gradle-plugin / iOS-kørselsskript) og Dart-symboler (
sentry-cli) i det samme CI-job. - Bevar
build/symbols/$VERSIONsom en CI-artifakt, der opbevares, så længe versionen kan gå ned hos brugerne.
Knyt versionsstrengen, der bruges overalt — Sentry release, Crashlytics setCustomKey og navnet på symbolmappen — til én fælles sandhedskilde, så sammenkoblingen aldrig skrider.
# 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/$VERSIONHurtigt tjek: Hvorfor Dart-rammer forbliver obfuskerede
Du har sendt en Flutter-udgivelse ud, der er bygget med --obfuscate --split-debug-info. Native-rammer i Crashlytics er læselige, men Dart-rammerne i din forretningslogik viser stadig korte tokens og hexadecimale forskydninger. Hvad er den korrekte løsning?
Opsummering: Et handlingsrettet nedbrudsforløb
Du har nu et forløb fra ende til anden, der omdanner kryptiske produktionsnedbrud til fejlrapporter, der kan løses.
- Byg udgivelsen med
--obfuscate --split-debug-info=build/symbols/$VERSION, og arkivér mappen for hver udgivelse. - Indfang via
FlutterError.onError+PlatformDispatcher.onError(Crashlytics) og/ellerSentryFlutter.initmedappRunner. - Symbolisér to lag: native (dSYM/NDK-uploads) og Dart (
flutter symbolizefor Crashlytics, automatisk på serversiden for Sentry efter symbolupload). - Berig med bruger-id'er, brugerdefinerede nøgler og brødkrummer — ingen personhenførbare oplysninger.
- Automatisér symbolupload i CI, og brug én versionsstreng som den fælles sandhedskilde.
Den vigtigste regel: En nedbrudsrapport er kun så god som den symbolfil, du gemte for netop den build.
Lær Dart med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 22
- Lektioner
- 88
Ofte stillede spørgsmål
Er lektionen “Crashrapportering og symboliserede stack traces” gratis?
Ja — alle 3 lektioner i læringssporet Mobiludvikling med Flutter, inklusive “Crashrapportering og symboliserede stack traces”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Mobiludvikling med Flutter-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Crashrapportering og symboliserede stack traces”?
Integrér Crashlytics og Sentry med mappings til deobfuskering for brugbare crashrapporter. Du øver dig i Mobiludvikling med Flutter med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Mobiludvikling med Flutter?
Der kræves ingen tidligere erfaring. Mobiludvikling med Flutter på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Crashrapportering og symboliserede stack traces”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Mobiludvikling med Flutter-lektion?
Ja. Alle Mobiludvikling med Flutter-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Build-flavors og miljøkonfiguration
- Automatiserede pipelines med Fastlane og GitHub Actions
- Crashrapportering og symboliserede stack traces
- Remote config, feature flags og trinvise udrulninger