Flutter错误追踪插件failure_stack的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter错误追踪插件failure_stack的使用

为什么使用failure_stack?

传统的错误处理方法(抛出和捕获异常)可能会导致不可预测的错误和行为。通过使用结果类型作为返回值,你可以确保处理每个可能发生的错误,从而降低程序出错的可能性。

你可能会问,我已经知道ResultEither类型很好用,但已经有dartzfpdart这样的库了,为什么还要创建另一个库呢?上述提到的库在函数式编程方面非常出色,但在处理错误时,当你的程序变得更大且包含大量嵌套函数调用时,它们可能不是最佳选择。因此,在普通的Either类型之上,此包具有一些额外的功能:

  1. 鼓励用户在作用域改变时提供新的错误类型,通常是在应用程序或第三方库的不同层之间传递(例如,ApiError用于基础设施层错误,InvalidInputError用于应用层)。
  2. 能够附加任何额外数据到失败中。
  3. 能够将失败推入堆栈并在稍后处理它们,同时仍然跟踪它们。

一个failure_stack的示例图

使用方法

假设我们有一个将String解析为int的函数,如果输入不是一个数字,则该函数可能会失败。

class ParsingFailure {} // 表示解析失败的错误

Result<int, ParsingFailure> parse(String numString);

当我们使用这个函数时,有三种处理结果的方式。

1. 当你不关心可能出现的失败时

// .ok 返回包含Ok值的结果,因为结果可能失败,所以它是一个可空类型。
int? result = parse(targetString).ok;

2. 穷尽匹配

switch (parse(targetString)) {
  case Ok<int, ParsingFailure> ok: {
    print("成功: ${ok.value}");
  },
  case Fail<int, ParsingFailure> fail: {
    print("失败: ${fail.failure}");
  }
}

3. 当你在返回Result类型的函数中时,使用resultHandleEnvironment代替。

警告:不要解包与失败类型不匹配的结果,使用Result.mapFailResult.pushFail来更改失败类型。

Result<int, FormatException> parseString(String s) {
  try {
    return Ok(int.parse(s));
  } on FormatException catch (e) {
    Result<int, FormatException> r = e.intoFailure();
    return r.attach("解析 $s 到 int 失败");
  }
}

class ParseExperimentFailure {
  const ParseExperimentFailure();
  [@override](/user/override)
  String toString() {
    return "ParseExperimentFailure: 无效的实验输入";
  }
}

Result<List<int>, ParseExperimentFailure> parseExperiment(String input) {
  return resultHandleEnvironment(() {
    List<int> values = input
        .split(" ")
        .map((String s) => parseString(s))  // Result<int, FormatException>
        .map((Result<int, FormatException> result) =>
            result.pushFail(const ParseExperimentFailure())) // Result<int, ParseExperimentFailure>
        // 当结果为Ok时,解包为int,
        // 否则抛出ParseExperimentFailure并由resultHandleEnvironment捕获并返回为Fail(ParseExperimentFailure)
        .map((Result<int, ParseExperimentFailure> result) => result.unwrap()) // int
        .toList(growable: false);
    return Ok(values);
  });
}

将异常和错误转换为失败

使用intoFailure()扩展方法

Future<Result<(), DioException>> callApi() async {
  try {
    await dio.post(/*一些代码*/);
  } on DioException catch (e) {
    return e.intoFailure();
  }
}

完整示例

import 'package:failure_stack/failure_stack.dart';

Result<int, FormatException> parseString(String s) {
  try {
    return Ok(int.parse(s));
  } on FormatException catch (e) {
    Result<int, FormatException> r = e.intoFailure();
    return r.attach("解析 $s 到 int 失败");
  }
}

class ParseExperimentFailure {
  const ParseExperimentFailure();
  [@override](/user/override)
  String toString() {
    return "ParseExperimentFailure: 无效的实验输入";
  }
}

Result<List<int>, ParseExperimentFailure> parseExperiment(String input) {
  return resultHandleEnvironment(() {
    List<int> values = input
        .split(" ")
        .map((String e) => parseString(e))
        .map((Result<int, FormatException> e) =>
            e.pushFail(const ParseExperimentFailure()))
        .map((Result<int, ParseExperimentFailure> e) => e.unwrap())
        .toList(growable: false);
    return Ok(values);
  });
}

class ExperimentError {
  [@override](/user/override)
  String toString() {
    return "ExperimentError: 无法运行实验";
  }
}

Result<List<int>, ExperimentError> startExperiments(Map<int, String> inputs) {
  return resultHandleEnvironment(() {
    List<int> finalResults = [];

    inputs.entries.map((e) {
      return parseExperiment(e.value).pushFail(ExperimentError()).unwrap();
    }).forEach((element) => finalResults.addAll(element));

    return Ok(finalResults);
  });
}

void main() {
  final experimentsInput = {0: "1 5 6", 4: "4 5 6", 6: "3 5w"};

  switch (startExperiments(experimentsInput)) {
    case Ok ok:
      {
        print(ok.value);
      }
    case Fail fail:
      {
        print(fail.stack);
      }
  }
}

更多关于Flutter错误追踪插件failure_stack的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter错误追踪插件failure_stack的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中集成和使用failure_stack插件来追踪错误的示例代码。failure_stack是一个可以帮助你捕获和记录Flutter应用中错误的插件。

1. 添加依赖

首先,你需要在你的pubspec.yaml文件中添加failure_stack依赖:

dependencies:
  flutter:
    sdk: flutter
  failure_stack: ^最新版本号  # 替换为实际的最新版本号

然后运行flutter pub get来获取依赖。

2. 初始化插件

在你的应用的主入口文件(通常是main.dart)中,初始化FailureStack插件。

import 'package:flutter/material.dart';
import 'package:failure_stack/failure_stack.dart';

void main() {
  // 初始化FailureStack
  FailureStack.init(
    onCaptureError: (FlutterErrorDetails details) {
      // 在这里处理捕获到的错误
      print("Error captured: ${details.exceptionAsString()}");
      print("Stack trace: ${details.stack}");
      // 你可以将错误信息发送到你的服务器或日志系统
    },
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo Home Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '0',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 这里我们故意制造一个错误来演示FailureStack的工作
          throw Exception("This is a test exception!");
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

3. 运行应用并测试

运行你的Flutter应用,并尝试触发一个错误(例如,点击上面的浮动按钮)。你应该会在控制台中看到FailureStack捕获并打印的错误信息和堆栈跟踪。

注意事项

  1. 生产环境:在实际生产环境中,你可能希望将错误信息发送到远程服务器而不是仅仅打印到控制台。你可以在onCaptureError回调中实现这一逻辑。
  2. 敏感信息:确保在发送错误信息到远程服务器时,不泄露任何敏感信息(如用户密码、API密钥等)。
  3. 自定义错误处理:根据需求,你可以进一步自定义错误处理逻辑,例如对不同类型的错误进行不同的处理。

通过上述步骤,你就可以在Flutter项目中使用failure_stack插件来追踪和记录错误了。

回到顶部