【2022年】おすすめのロガーパッケージ4選【Flutter】
はじめに
みなさんはどのロガー(ログ出力)パッケージを使っていますか?あるいは自作していますか?
本記事では Flutter 開発でかかせないおすすめのロガーパッケージ4選を紹介していきます。おまけで自作ロガーのサンプル実装も紹介しています。是非参考にしてください!
自分が欲しい機能を実装したロガーパッケージを作成して pub.dev に公開しました!是非こちらもご覧下さい!
修正履歴
2022/05/06 はじめに
に、ロガーパッケージ作成したリンクを追加しました。
2022/04/16 logger と logging について throw AssertionError()
すると処理が止まると記載しておりましたが、しっかり確認したら処理が止まりませんでしたので記事を修正しました。
print VS ロガーパッケージ
ところで、わざわざロガーパッケージなんか使わず print
でよくない?と思われる方もいるかもしれません。しかし、print
はリリースビルドでも出力されてしまうためコミットは控えた方が良いです。さらに、多くのロガーパッケージに比べて次の点で劣ります。
- ログレベル(
info
やwarning
など)によるログ出力のフィルタができない。 - ログ出力した場所(ファイル名や行)や時刻情報などの付加情報が出力されない。
- Crashlytics にエラーを送信するなどの処理を挿入できない。
ロガーパッケージにはそれぞれ特徴がありますので、本記事を参考にして自分にあったロガーを見つけてみてください。
環境
Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c860cba910 (3 weeks ago) • 2022-03-25 00:23:12 -0500
Engine • revision 57d3bac3dd
Tools • Dart 2.16.2 • DevTools 2.9.2
おすすめのロガー4選
logger 1.1.0
pub.dev 👍 1417
GitHub ⭐ 911
ロガーパッケージの中でダントツの指示を得ています。デフォルトでリリースビルドは出力しない動作になるなど、すぐに使えて高機能なロガーです。ログレベルによって出力するログが色づけされていて美しく見やすいです。高い拡張性も備えています。
基本的な使い方
import 'package:logger/logger.dart';
// デフォルトでリリースビルドは出力しない、デバッグビルドはすべてログ出力する動作になっている
final logger = Logger();
void main() {
logger.v('Hello logger!');
logger.d(1000);
logger.i(true);
logger.w([1, 2, 3]);
logger.e({'key': 'key', 'value': 'value'});
logger.wtf(Exception('例外もいけます'));
logger.i(() => '関数もいけます');
}
応用例
import 'package:intl/intl.dart';
import 'package:logger/logger.dart';
final logger = Logger(
// ログ出力内容をカスタマイズできる
// あらかじめ用意されているPrettyPrinterをカスタマイズした例
printer: PrettyPrinter(
methodCount: 1, // 表示されるコールスタックの数
errorMethodCount: 5, // 表示されるスタックトレースのコールスタックの数
lineLength: 120, // 出力するログ1行の幅
colors: true, // メッセージに色をつけるかどうか
printEmojis: true, // 絵文字を出力するかどうか
printTime: true, // タイムスタンプを出力するかどうか
),
);
// 別インスタンスのロガーを作ることもできる
final logger2 = Logger(
// ログ出力内容をシンプルにカスタマイズした例
printer: MyLogPrinter(),
// ログ出力したあとに処理を挿入する
output: MyLogOutput(),
);
/// シンプルな出力
class MyLogPrinter extends LogPrinter {
List<String> log(LogEvent event) {
final message = event.message;
String msg;
if (message is Function()) {
msg = message().toString();
} else if (message is String) {
msg = message;
} else {
msg = message.toString();
}
return [
'[${event.level.name.toUpperCase()}] '
'${DateFormat('HH:mm:ss.SSS').format(DateTime.now())}: '
'$msg'
];
}
}
class MyLogOutput extends ConsoleOutput {
void output(OutputEvent event) {
super.output(event);
if (event.level.index >= Level.error.index) {
// 致命的なエラーが発生したのでAssertionErrorをthrowしてStackTraceを表示する
throw AssertionError('View stack trace by logger');
}
}
}
void main() {
logger.i('Hello logger!');
logger.w('Hello logger warning!', Exception('loggerの例外'), StackTrace.current);
logger2.i('Hello logger!');
logger2.e(Exception('例外を投げてみる'));
logger2.i('処理は止まらない');
}
logging 1.0.2
pub.dev 👍 312
GitHub ⭐ 213
標準パッケージです。非常にシンプルで、ログ出力部分を自分で実装する必要があります。すべてを自分好みに実装したい方におすすめです。
基本的な使い方
import 'package:logging/logging.dart';
final logger = Logger('MyLogger');
void main() {
// すべてログ出力する
Logger.root.level = Level.ALL;
// ログ出力内容を定義する(実装必須)
Logger.root.onRecord.listen((LogRecord rec) {
print('[${rec.loggerName}] ${rec.level.name}: ${rec.time}: ${rec.message}');
});
logger.finer('Hello logger!');
logger.fine(1000);
logger.config(true);
logger.info([1, 2, 3]);
logger.warning({'key': 'key', 'value': 'value'});
logger.severe(Exception('例外もいけます'));
logger.shout(() => '関数もいけます');
}
応用例
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
final logger = Logger('MyLogger');
// 別インスタンスのロガーを作ることもできる
final logger2 = Logger('MyLogger2');
void main() {
// リリースビルドは出力しない、デバッグビルドはすべてログ出力する
Logger.root.level = kReleaseMode ? Level.OFF : Level.ALL;
// ログ出力内容を定義する(実装必須)
Logger.root.onRecord.listen((LogRecord rec) {
print('[${rec.loggerName}] ${rec.level.name}: ${rec.time}: ${rec.message}');
if (rec.level >= Level.SEVERE) {
// 致命的なエラーが発生したのでAssertionErrorをthrowしてStackTraceを表示する
throw AssertionError('View stack trace by logger');
}
});
logger.info('Hello logger!');
logger2.info('ロガーを切り替えることが出来ます。');
logger.severe(Exception('例外を投げてみる'));
logger.info('処理は止まらない');
}
simple_logger 1.9.0
pub.dev 👍 62
GitHub ⭐ 45
すぐに使い始められるシンプルなロガーです。標準の logging パッケージ とインターフェースを揃えているため乗り換えが簡単です。作者の mono さんが simple_logger の詳細について解説してくれています。
基本的な使い方
import 'package:simple_logger/simple_logger.dart';
final logger = SimpleLogger()
..setLevel(
// すべてログ出力する
Level.ALL,
// ログ出力した場所を出力する
includeCallerInfo: true,
);
void main() {
logger.finer('Hello logger!');
logger.fine(1000);
logger.config(true);
logger.info([1, 2, 3]);
logger.warning({'key': 'key', 'value': 'value'});
logger.severe(Exception('例外もいけます'));
logger.shout(() => '関数もいけます');
}
応用例
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
import 'package:simple_logger/simple_logger.dart';
final logger = SimpleLogger()
..formatter = (info) {
// ログ出力内容をカスタマイズできる
return '[${info.level}] '
'${DateFormat('HH:mm:ss.SSS').format(info.time)} '
'[${info.callerFrame ?? 'caller info not available'}] '
'${info.message}';
}
..setLevel(
// リリースビルドは出力しない、デバッグビルドはすべてログ出力する
kReleaseMode ? Level.OFF : Level.ALL,
// ログ出力した場所を出力する
includeCallerInfo: true,
)
..onLogged = (log, info) {
if (info.level >= Level.SEVERE) {
// 致命的なエラーが発生したのでAssertionで止めるとStackTraceも表示される
throw AssertionError('Stopped by logger');
}
};
void main() {
logger.info('Hello logger!');
logger.severe(Exception('例外を投げてみる'));
}
loggy 2.0.1+1
pub.dev 👍 44
GitHub ⭐ 65
高い拡張性を備えたロガーです。ロガータイプを定義して mixin
を使用することで、ログの出力内容を変えることができるのが特徴的です。リリースビルドではログ出力せずに Crashlytics にエラーを送信することができます。
基本的な使い方
import 'package:loggy/loggy.dart';
final logger = Loggy('MyLogger');
void main() {
Loggy.initLoggy();
logger.debug('Hello logger!');
logger.info(1000);
logger.warning(true);
logger.error([1, 2, 3]);
logger.info({'key': 'key', 'value': 'value'});
logger.info(Exception('例外もいけます'));
logger.info(() => '関数もいけます');
}
応用例
import 'package:flutter/foundation.dart';
import 'package:loggy/loggy.dart';
final logger = Loggy('MyLogger');
/// Crashlyticsにエラーを送信するプリンター
class CrashlyticsPrinter extends LoggyPrinter {
const CrashlyticsPrinter() : super();
void onLog(LogRecord record) {
if (record.level.priority >= LogLevel.error.priority) {
// Crashlyticsにエラーを送信する例
// FirebaseCrashlytics.instance.log(record.message);
// FirebaseCrashlytics.instance.recordError(record.error, record.stackTrace);
}
}
}
void main() {
Loggy.initLoggy(
// リリースビルドは出力せずに Crashlytics にエラーを送信し、デバッグビルドはログ出力する
logPrinter:
kReleaseMode ? const CrashlyticsPrinter() : const PrettyPrinter(),
logOptions: const LogOptions(
// すべてのログを出力する
LogLevel.all,
// エラーの場合はスタックトレースも出力する
stackTraceLevel: LogLevel.error,
// ログ出力した場所を出力する
includeCallerInfo: true,
),
);
// mixinを使った例
SampleUi();
SampleNetwork();
logger.info('Hello logger!');
logger.error(Exception('例外を投げてみる'));
}
class SampleUi with UiLoggy {
SampleUi() {
loggy.info('This is info message');
}
}
class SampleNetwork with NetworkLoggy {
SampleNetwork() {
loggy.info('This is info message');
}
}
【おまけ】dart:developer を使って自作
Flutter SDK にある dart:developer
を使ってロガーを自作した簡単なサンプルです。他のロガーに出る I/flutter (27516):
の部分が出力されません(地味にうれしい人がいるかもしれません)。
サンプル実装
import 'dart:developer' as developer;
import 'package:flutter/foundation.dart';
final logger = Develogger();
class Develogger {
void log(Object? message) {
String msg;
if (message is Function()) {
msg = message().toString();
} else if (message is String) {
msg = message;
} else {
msg = message.toString();
}
// リリースビルドは出力しない、デバッグビルドは出力する
if (!kReleaseMode) {
developer.log(msg, name: 'Develogger');
}
}
}
void main() {
logger.log('Hello logger!');
logger.log(1000);
logger.log(true);
logger.log([1, 2, 3]);
logger.log({'key': 'key', 'value': 'value'});
logger.log(Exception('例外もいけます'));
logger.log(() => '関数もいけます');
}
まとめ
紹介した4つのロガーパッケージの比較表になります。
logger | logging | simple_logger | loggy | |
---|---|---|---|---|
pub.dev 👍 数 | 1417 | 312 | 62 | 44 |
GitHub ⭐ 数 | 911 | 213 | 45 | 65 |
ログレベル数 | 6種類 verbose debug info warning error wtf |
7種類 finer fine config info warning severe shout |
7種類 finer fine config info warning severe shout |
4種類 debug info warning error |
ログ出力内容の変更 | ○ | ○ | ○ | ○ |
ログレベル毎のフィルタ | ○ | ○ | ○ | ○ |
任意の処理を実行[1] | ○ | ○ | ○ | ○ |
リリースビルド対応[2] | ○ | ○ | ○ | ○ |
導入の容易さ | ○ | × | ○ | ○ |
複数インスタンス | ○ | ○ | × | ○ |
エラーログで処理停止[3] | × | × | ○ | × |
特徴 | 高機能 高い拡張性 美しいログ |
標準パッケージ 極限までシンプル |
シンプルで使いやすい 標準パッケージと同じI/F |
高い拡張性 mixinで利用可能 Crashlytics対応[4] |
今回のロガーを調査したリポジトリを公開しておきます。
最後に
Flutter 大学という Flutter エンジニアに特化した学習コミュニティに所属しています。オンラインでわいわい議論したり、Flutter の最新情報をゲットしたりできます!ぜひ Flutter 界隈を盛り上げていきましょう!
あわせて読みたい
Discussion