Flutter Interview Handbook

Observability & Telemetry

100%

Observability & Telemetry

Production engineering requires 24/7 visibility into mobile app health. Production Observability captures uncaught runtime crashes, performance telemetry (HTTP latency, screen rendering times), user analytics, and structured breadcrumbs using tools like Firebase Crashlytics, Sentry, or Datadog.


1. Catching 100% of Production Errors

Flutter applications experience errors across 3 execution zones: Flutter Framework Errors, Asynchronous Dart Errors, and Native Platform Engine Errors.

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                              UNCAUGHT ERROR BOUNDARIES                                 β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ 1. Flutter Framework Errors   β”‚ 2. Async Dart Errors          β”‚ 3. Native OS Crashes   β”‚
 β”‚   - Widget build() exceptions β”‚   - Un-awaited Future errors  β”‚   - iOS SIGSEGV        β”‚
 β”‚   - Layout overflow errors    β”‚   - Isolate event errors      β”‚   - Android NDK NullPointerβ”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ `FlutterError.onError`        β”‚ `PlatformDispatcher.instance` β”‚ Native Crashlytics /   β”‚
 β”‚                               β”‚ `.onError`                    β”‚ Sentry SDK bindings    β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Complete Error Capture Pipeline Setup

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';

void main() async {
  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();

    // 1. Catch all Flutter Framework UI errors (e.g. build exceptions)
    FlutterError.onError = (FlutterErrorDetails details) {
      FlutterError.presentError(details);
      FirebaseCrashlytics.instance.recordFlutterFatalError(details);
    };

    // 2. Catch all uncaught Asynchronous Dart errors
    PlatformDispatcher.instance.onError = (error, stack) {
      FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
      return true; // Prevents error from crashing process if handled
    };

    runApp(const MyApp());
  }, (error, stack) {
    // 3. Catch all Zone boundary errors
    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
  });
}

2. Symbolication of Obfuscated Production Stack Traces

When release builds compile with --obfuscate --split-debug-info=./symbols, production stack traces are mangled into unreadable memory addresses:

# Obfuscated Crash Log (Unreadable):
#0      0x0000000104f2c1b4 in libapp.so (+0x1b4)
#1      0x0000000104f2d8a0 in libapp.so (+0x8a0)

Symbolication Workflow

Symbolication maps memory addresses back to human-readable Dart file basenames and line numbers (user_repository.dart:42).

  • CI Pipeline Requirement: The symbol map files generated during release compilation (app.android-arm64.symbols or DWARF files) MUST be uploaded to Sentry or Crashlytics automatically during CI deployment!
# Uploading obfuscation symbol maps to Sentry in CI
sentry-cli debug-files upload --auth-token $SENTRY_AUTH_TOKEN ./symbols

3. Breadcrumb Logging & User Telemetry

Breadcrumbs track the sequence of user actions leading up to a crash (e.g. β€œUser tapped Login -> Navigated to Checkout -> Fired Payment API -> CRASH”).

void trackUserActionBreadcrumb(String action) {
  FirebaseCrashlytics.instance.log('User Action: $action');
  
  // Custom Key-Value attributes for crash context
  FirebaseCrashlytics.instance.setCustomKey('user_tier', 'premium');
}

4. Performance Monitoring Telemetry

Track screen rendering latency and HTTP request durations in real-time:

  • Custom Traces: Measure critical code paths (e.g. DB migration time, image processing speed).
  • HTTP Network Tracing: Measures API response latencies, failure rates, and payload sizes across cellular carriers.
final trace = FirebasePerformance.instance.newTrace('checkout_processing_time');
await trace.start();

await executeCheckoutLogic();

await trace.stop(); // Sends execution duration to Firebase Console!

5. Trade-offs & Production Considerations

  • GDPR & PII Privacy: Never log Personally Identifiable Information (PII) like raw passwords, credit card numbers, or full names in Crashlytics logs or breadcrumbs. Sanitize custom key-value pairs before dispatch.
  • Log Batching & Battery: Dispatching crash reports over network connections consumes battery. Telemetry SDKs batch logs locally in storage and upload them in periodic background batches.