乐于分享
好东西不私藏

flutter 插件崩溃收集——sentry_flutter 基础

flutter 插件崩溃收集——sentry_flutter 基础

一、sentry_flutter 核心功能

Sentry 是业界领先的错误追踪和性能监控平台,支持:

  • ✅ 自动捕获 Flutter 框架异常
  • ✅ 捕获 Dart 未处理异常
  • ✅ 原生崩溃收集(iOS/Android)
  • ✅ 性能监控(APM)
  • ✅ 用户行为追踪(Breadcrumbs)
  • ✅ Release 健康度监控

二、安装配置

1. 添加依赖

dependencies:sentry_flutter:^8.0.0

2. 环境变量配置

在 .env.example 中添加:

# Sentry 配置SENTRY_DSN=https://your-dsn@sentry.io/project-idSENTRY_ENVIRONMENT=development

在 .env.dev.env.prod 中配置对应环境的 DSN:

# .env.devSENTRY_DSN=https://dev-dsn@sentry.io/123456SENTRY_ENVIRONMENT=development# .env.prodSENTRY_DSN=https://prod-dsn@sentry.io/789012SENTRY_ENVIRONMENT=production

三、初始化配置

1. 创建 Sentry 服务

// lib/services/sentry_service.dartimport'package:flutter/foundation.dart';import'package:flutter_dotenv/flutter_dotenv.dart';import'package:sentry_flutter/sentry_flutter.dart';classSentryService{static Future<void> init() async {final dsn = dotenv.env['SENTRY_DSN'];final environment = dotenv.env['SENTRY_ENVIRONMENT'] ?? 'development';if (dsn == null || dsn.isEmpty) {      debugPrint('⚠️ Sentry DSN 未配置,跳过初始化');return;    }await SentryFlutter.init(      (options) {        options.dsn = dsn;        options.environment = environment;// 采样率配置        options.tracesSampleRate = environment == 'production' ? 0.2 : 1.0;        options.sampleRate = 1.0// 错误事件采样率// 自动捕获配置        options.enableAutoSessionTracking = true;        options.sessionTrackingIntervalMillis = 30000;// 性能监控        options.enableAutoPerformanceTracing = true;// 调试模式        options.debug = kDebugMode;// 附加上下文        options.attachScreenshot = true;        options.attachViewHierarchy = true;// 过滤敏感信息        options.beforeSend = (event, hint) {// 可以在这里过滤或修改事件return event;        };      },    );    debugPrint('✅ Sentry 初始化成功 - 环境: $environment');  }/// 设置用户信息staticvoid setUser({    required String id,String? email,String? username,Map<Stringdynamic>? extras,  }) {    Sentry.configureScope((scope) {      scope.setUser(SentryUser(        id: id,        email: email,        username: username,        data: extras,      ));    });  }/// 清除用户信息(退出登录时调用)staticvoid clearUser() {    Sentry.configureScope((scope) {      scope.setUser(null);    });  }/// 设置标签staticvoid setTag(String key, String value) {    Sentry.configureScope((scope) {      scope.setTag(key, value);    });  }/// 设置上下文staticvoid setContext(String key, Map<Stringdynamic> context) {    Sentry.configureScope((scope) {      scope.setContexts(key, context);    });  }/// 手动捕获异常static Future<void> captureException(dynamic exception, {dynamic stackTrace,String? hint,Map<Stringdynamic>? extras,  }) async {await Sentry.captureException(      exception,      stackTrace: stackTrace,      hint: hint != null ? Hint.withMap({'message': hint}) : null,      withScope: (scope) {if (extras != null) {          extras.forEach((key, value) {            scope.setExtra(key, value);          });        }      },    );  }/// 手动发送消息static Future<void> captureMessage(String message, {    SentryLevel level = SentryLevel.info,Map<Stringdynamic>? extras,  }) async {await Sentry.captureMessage(      message,      level: level,      withScope: (scope) {if (extras != null) {          extras.forEach((key, value) {            scope.setExtra(key, value);          });        }      },    );  }/// 添加面包屑(用户行为追踪)staticvoid addBreadcrumb({    required String message,String? category,    SentryLevel level = SentryLevel.info,Map<Stringdynamic>? data,  }) {    Sentry.addBreadcrumb(Breadcrumb(      message: message,      category: category,      level: level,      data: data,      timestamp: DateTime.now(),    ));  }}

四、集成到 main.dart

// lib/main.dartimport'package:flutter/material.dart';import'package:sentry_flutter/sentry_flutter.dart';import'package:my_flutter_app/services/sentry_service.dart';Future<void> main() async {  WidgetsFlutterBinding.ensureInitialized();// 加载环境变量await dotenv.load(fileName: ".env");// 初始化 Sentryawait SentryService.init();// 使用 Sentry 包裹 runAppawait SentryFlutter.init(    (options) {// 配置已在 SentryService.init() 中完成    },    appRunner: () => runApp(// Sentry 会自动捕获 Flutter 框架异常      DefaultAssetBundle(        bundle: SentryAssetBundle(),        child: const MyApp(),      ),    ),  );}

五、实战示例

1. 自动捕获异常

// lib/pages/day63_sentry_basic_demo.dartimport'package:flutter/material.dart';import'package:my_flutter_app/services/sentry_service.dart';classDay63SentryBasicDemoextendsStatelessWidget{const Day63SentryBasicDemo({super.key});@override  Widget build(BuildContext context) {return Scaffold(      appBar: AppBar(title: const Text('Day 63: Sentry 基础')),      body: ListView(        padding: const EdgeInsets.all(16),        children: [          _buildSection(            title: '1. 自动捕获异常',            children: [              ElevatedButton(                onPressed: () {// 这个异常会被 Sentry 自动捕获throw Exception('测试自动捕获异常');                },                child: const Text('触发未捕获异常'),              ),const SizedBox(height: 8),              ElevatedButton(                onPressed: () {// 空指针异常String? nullString;print(nullString!.length);                },                child: const Text('触发空指针异常'),              ),            ],          ),const Divider(height: 32),          _buildSection(            title: '2. 手动上报异常',            children: [              ElevatedButton(                onPressed: () async {try {// 模拟业务异常await _simulateApiError();                  } catch (e, stackTrace) {// 手动上报到 Sentryawait SentryService.captureException(                      e,                      stackTrace: stackTrace,                      hint: '用户登录失败',                      extras: {'user_action''login','timestamp'DateTime.now().toIso8601String(),                      },                    );                    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('异常已上报到 Sentry')),                    );                  }                },                child: const Text('手动捕获并上报异常'),              ),            ],          ),const Divider(height: 32),          _buildSection(            title: '3. 发送消息',            children: [              ElevatedButton(                onPressed: () async {await SentryService.captureMessage('用户完成了重要操作',                    level: SentryLevel.info,                    extras: {'action''purchase','amount'99.99,                    },                  );                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('消息已发送到 Sentry')),                  );                },                child: const Text('发送信息消息'),              ),const SizedBox(height: 8),              ElevatedButton(                onPressed: () async {await SentryService.captureMessage('检测到异常行为',                    level: SentryLevel.warning,                    extras: {'suspicious_activity'true,'ip''192.168.1.1',                    },                  );                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('警告消息已发送')),                  );                },                child: const Text('发送警告消息'),              ),            ],          ),const Divider(height: 32),          _buildSection(            title: '4. 用户信息绑定',            children: [              ElevatedButton(                onPressed: () {                  SentryService.setUser(                    id: 'user_12345',                    email: 'user@example.com',                    username: 'john_doe',                    extras: {'vip_level''gold','registration_date''2024-01-01',                    },                  );                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('用户信息已设置')),                  );                },                child: const Text('设置用户信息'),              ),const SizedBox(height: 8),              ElevatedButton(                onPressed: () {                  SentryService.clearUser();                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('用户信息已清除')),                  );                },                child: const Text('清除用户信息'),              ),            ],          ),const Divider(height: 32),          _buildSection(            title: '5. 标签和上下文',            children: [              ElevatedButton(                onPressed: () {                  SentryService.setTag('page''home');                  SentryService.setTag('feature''payment');                  SentryService.setContext('device', {'model''iPhone 14 Pro','os''iOS 17.0','battery''85%',                  });                  ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('标签和上下文已设置')),                  );                },                child: const Text('设置标签和上下文'),              ),            ],          ),        ],      ),    );  }  Widget _buildSection({    required String title,    required List<Widget> children,  }) {return Column(      crossAxisAlignment: CrossAxisAlignment.start,      children: [        Text(          title,          style: const TextStyle(            fontSize: 18,            fontWeight: FontWeight.bold,          ),        ),const SizedBox(height: 12),        ...children,      ],    );  }  Future<void> _simulateApiError() async {await Future.delayed(constDuration(milliseconds: 500));throw Exception('API 请求失败: 网络超时');  }}

六、生产环境最佳实践

1. 环境区分

// 根据环境调整采样率options.tracesSampleRate = environment == 'production' ? 0.2 : 1.0;

2. 敏感信息过滤

options.beforeSend = (event, hint) {// 过滤密码、token 等敏感信息if (event.request?.data != null) {final data = event.request!.data asMap<Stringdynamic>;    data.remove('password');    data.remove('token');  }return event;};

3. 用户登录时绑定信息

// 登录成功后SentryService.setUser(  id: user.id,  email: user.email,  username: user.name,);// 退出登录时SentryService.clearUser();

七、总结

1. DSN 在哪里获取?

登录 sentry.io → 创建项目 → 选择 Flutter → 复制 DSN

2. 如何测试 Sentry 是否正常工作?

// 触发一个测试异常throw Exception('Sentry 测试异常');

然后在 Sentry 控制台查看是否收到事件。

3. 生产环境建议

  • ✅ 使用环境变量管理 DSN
  • ✅ 设置合理的采样率(避免流量浪费)
  • ✅ 过滤敏感信息
  • ✅ 绑定用户信息(便于定位问题)
  • ✅ 使用 Release 版本号(便于追踪版本问题)

八、总结

功能
说明
自动捕获
Flutter 框架异常、Dart 未处理异常
手动上报
captureException()

 / captureMessage()
用户绑定
setUser()

 关联用户信息
标签上下文
setTag()

 / setContext() 添加业务信息
采样率
控制上报频率,节省流量

下一篇:Day 64 – 性能监控与面包屑追踪 🚀


九、国产替代方案建议

由于网络环境、合规性或访问速度等原因,国内开发者在进行选型时,可以参考以下国产替代产品:

领域
国际主流 (本项目使用)
国产替代建议
特点
崩溃收集 Sentry

 / Crashlytics
腾讯 Bugly
国内最主流,完全免费,配置简单,原生崩溃捕获极佳
云开发/后端 Supabase

 / Firebase
腾讯云开发 (CloudBase)
访问速度快,生态集成好,完美支持小程序与 App
推送通知
FCM (Firebase)
极光推送 (JPush)

 / 个推
解决国内安卓系统 FCM 通达率不高的核心痛点
合规监控
Sentry (APM)
友盟+ (U-App)

 / 阿里云 ARMS
深度集成统计分析,符合国内数据安全合规要求

[!TIP]选型建议:

  • 海外/出海项目:强烈推荐 Sentry + Supabase,生态成熟,国际化支持完美。
  • 国内纯本土项目:建议考虑 腾讯 Bugly + 腾讯云开发,能解决网络链路和安卓推送的“最后一公里”问题。