
原文链接 Flutter logging best practices -- 作者 Deven Joshi
开发大型的应用程序并不容易。它通常需要多个模块协同工作,并且通常由不同的开发人员编写。所以,当开发中出现问题,一个人必须通过由多个开发人创建的应用程序流程来确定根本原因。错误识别了什么问题或者添加临时修复程序可能会破坏代码的其他部分,从长远看会导致更多问题。
如果你想快速创建一个应用程序(日志),你可能会使用下面的代码:
print('reached here');
// Some code
print('now here');
// Some other code
print('method called');print() 或者 debugPrint() 是方便的方法,用来在控制台打印错误日志或者检查代码的位置。起初,应用程序里有一些打印的日志并不值得担心。但是,从长远看,这并不是构建一个应用程序的可持续方法。
确保你项目中所有的模块或者功能函数能够顺畅运行的一个好方法,就是使用日志记录。一个良好的日志系统可以帮助减少构建应用的麻烦,并在应用程序运行时向用户和开发人员提供简明的信息。
本文将探索使用 Logger package 创建易于解析的 Flutter 日志,考虑日志级别,并介绍如何使用 Crashlytics 获取持续(onGoing)的日志。
在谈论 Flutter 中日志的最佳实践前,我们先看看日志本身。
结构和消息传递不当的日志使得内容难以破译。比如,HTTP 请求可以包含多个参数或具有需要检查的重要结果。当日志结构不当时候,执行此操作可能很困难且耗时。
让我们考虑一下构建应用程序需要什么。首先,你必须保证对服务器所有调用都成功通过。然后,你需要检查 UI 的某些部位是否正确构建,还有关于数据库的信息。
具有合理的结构和消息传递的日志系统将帮助你监测重大错误并且修复它,不让它占用你周末时间。日志系统必须适合开发者的需求,而不是统一设置。
现在,我们看看日志等级。
Flutter 项目可以有很多日志,包括网络,数据库和错误。通常,开发者只需要适量的日志,忽略冗长的日志。但是,如果事情不起作用,你可能需要检查更详细的事件。
当发布程序时,你可能只需要记录错误和其他重要的事件。在每个日志中设置级别对于这些记录至关重要,因为级别会为每个日志分配其重要性和类型。
你可能会查找日志类型,比如 verbose,warning 和 error,这些会帮你过滤出不必要的日志。现在,我们明白了可靠的日志系统的重要性并且设置日志的等级,现在,我们将他们添加到应用中。
这里,我们将讨论在项目中添加日志的基本规则。跟随这些最佳实践,你可以更好地了解自己应用程序的流程,与在没有连贯的日志系统情况下的应用相比,可以更好地排查程序中任何意外的问题。
尝试解决错误的时候,记录太多的信息让人不知所措,而信息太少又无法提供足够的信息来解决问题。与任何错误一样,错误可以追溯到 Flutter 本身的基础。如果将整个堆栈轨迹提供给开发人员,与有用的信息相比,这将变成大海捞针。为了避免这种情况,记录适当的信息以确定开发人员开发中产生错误的根本原因,而无需向下指向 Dart 的基本错误。
应用程序运行后,多个系统会协调工作,包括 UI、网络调用、数据库等。由于多个系统同时工作,很容易忽视对关键事件的报道。这些丢失的日志掩盖了流程内部的运作和错误原因。为了避免让自己头疼,确保覆所有的事件。
日志经常被使用,但是不是用来测试确保达到代码的某些部分。虽然记录这些部分代码不总是坏主意,但是避免测试可能是有害的。
每个记录的事情都需要检查其重要性,因此为每种类型事件分配一个级别。比如,将所有的网络调试设置为详细。这允许开发人员有效地分离日志,以防止在更高的日志级别处理其中的许多日志。
在开发环境中,你需要检查比生产环境中更多的日志 -- 所以在生产中不要记录不必要的信息。生产环境中的应用可能比调试的应用在更多设备运行。记录这些应用程序运行的所有事件会增加不必要的操作成本;因此,生产环境的日志通常仅限于警告和错误。
虽然可以在内部创建不同的日志记录组件,但是这很耗时,而且几乎没有什么好处,因为跨应用程序的日志记录系统很少定制或者不同。推荐几个日志包,比如 FLogs, loggy 和 simple_logger。
在本文中,我们以 Logger 包为例。它是最流行的 Flutter 日志记录解决方案之一,因为它具有开箱即用的日志记录并且能创建格式简洁的日志。
开始记录时,使用 log() 方法创建一个 Logger 类实例。
接下来,使用下面命令行提供级别和消息:
var logger = Logger();
logger.log(Level.verbose, "Demo log");你可以提供特定日志关联的 error 和 stackTrace:
class StringStackTrace implements StackTrace {
final String _stackTrace;
const StringStackTrace(this._stackTrace);
String toString() => _stackTrace;
}
var logger = Logger();
logger.log(Level.verbose, "Demo log", "An error", StringStackTrace("Your stacktrace here"));错误也可能是任何的对象而不是 String。因此,你可以提供日志信息 error,如下:
var error = Error();
var logger = Logger();
logger.log(Level.verbose, "Demo log", error, error.stackTrace);在这个例子中,我们可以在记录时传递 error 和 stackTrace。这允许用户从日志中快速提取更多详细信息并解决错误。
现在,我们已经创建了基本日志,现在是时候添加日志等级。幸运的是,Logger 自带几个等级,帮助你记录事件,如下:
enum Level {
verbose,
debug,
info,
warning,
error,
wtf,
nothing,
}为了记录日志不必每次都描述记录级别,Logger 提供了多种方法来记录不同级别的日志:
var logger = Logger();
logger.log(Level.verbose, "Demo log", error, error.stackTrace);
//OR
logger.v("Demo log", error, error.stackTrace);
// SIMILARLY
logger.w("Demo log", error, error.stackTrace);
logger.i("Demo log", error, error.stackTrace);
// AND MORE...日志过滤器能够帮助我们决定哪些事件应该被记录,哪些不需要。在发布模式的时候,显示哪些日志很有用。
我们继承 LogFilter 类,重写 shouldLog() 方法。接着,在实例化 Logger 的时候传入过滤器:
class DemoFilter extends LogFilter {
@override
bool shouldLog(LogEvent event) {
if(event.level == Level.error || event.level == Level.warning) {
return true;
}
return false;
}
}
var logger = Logger(filter: DemoFilter());
logger.w("This will be accepted", error, error.stackTrace);
logger.v("This will not", error, error.stackTrace);LogFilter 让你决定收到的信息哪些被记录,哪些被忽略。比如,你可以根据日志的严重等级使用不同的过滤器。
Logger 包支持打印结构良好且美观的日志。默认情况下,他们被以 stackTrace 的标准模式打印出来,如下:

然而,你可以使用内置的 PrettyPrinter 类添加更多的风格,使用如下:
var logger = Logger(
printer: PrettyPrinter(),
);要制作你自己输出器,你可以使用 LogPrinter 方法扩展:
class DemoPrinter extends LogPrinter {
@override
List<String> log(LogEvent event) {
switch(event.level) {
case Level.verbose:
break;
case Level.debug:
break;
case Level.info:
break;
case Level.warning:
break;
case Level.error:
break;
case Level.wtf:
break;
case Level.nothing:
break;
}
}
}现在,在任何日记级别,你可以以自定义格式打印信息。
Firebase’s Crashlytics 服务允许开发者分析应用程序中崩溃和特殊事件。尽管崩溃属于极端事件,但是 Crashlytics 还支持将应用中的自定日志发送到 Firebase Crashlytics 控制台。这有助于 Crashlytics 成为一种通用的日志工具,而不仅仅是应用程序出现问题时提供帮助的工具。
按照下面的步骤,在你的应用程序中使用 Firebash Crashlytics:
在你项目下运行下面命令行安装依赖包:
flutter pub add firebase_crashlytics在 android/build.gradle 文件中添加下列几行代码:
dependencies {
// ...
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
}另外,在 android/app/build.gradle 中添加下面内容:
android {
// ... your android config
}
dependencies {
// ... your dependencies
}
// This must appear at the bottom of the file
apply plugin: 'com.google.firebase.crashlytics'获取更多关于 Crashlytics 集成信息,请移步这里。
记录你应用程序的错误,使用 Grashlytics 提供的 recordError() 方法:
FirebaseCrashlytics.instance.recordError(
error,
stackTrace,
reason: 'Your error reason',
fatal: true
);如果不是错误,而是看日志,使用 log() 方法:
FirebaseCrashlytics.instance.log("Your log event");另外,还有 Flutter 特定的错误函数,比如 recordFlutterError():
FirebaseCrashlytics.instance.recordFlutterError(
FlutterErrorDetails(
exception: YourException(),
stack: stackTrace,
),
fatal: false,
);exception 值的类型为 Object,因此,该值可以是你要传递的任何内容。
本文讨论了 Flutter 项目中日志的最佳实践。我们还学习了如何使用一个包创建简易的解析日志,考虑了日志等级,并介绍了如何使用 Crashlytics 和类似的工具来持续获取日志。
虽然这是 Flutter 的日志总结,但是根据开发的不同项目,你每次对日志的应用都会有所差异。