loggerパッケージ
loggerパッケージの特徴としては、以下のような点が挙げられます。
- 出力先のデフォルトがコンソールであり指定する必要がない
- ログ出力時に時間、呼び出し元ソースと行数などの出力がある
- 表示が分かりやすい
- 出力フォーマットや出力先など役割毎にカスタマイズできる
総じて、前述したloggingパッケージよりも高機能です。
Dartのパッケージリポジトリで今回紹介する2つのロガーパッケージの評価をみると、前述したloggingパッケージよりもこちらのパッケージの方が評価が高く、多くのFlutter開発者がこのパッケージの方を支持していることが覗えます。
簡単な利用方法
リスト7のようなコードを記述すると、図1のような出力になります。
import 'package:logger/logger.dart';
var log = Logger();
try {
log.d("debug message");
log.i("info message");
throw new StateError("state error");
}
catch(e){
log.w("error ",error: e);
}
このように文字情報だけではなく、色や表示枠などで分かりやすい出力になることが魅力です。また、先のloggingパッケージではlistenを利用して出力方法を実装していましたが、このパッケージではコンソールログ(print結果の出力先)への出力であれば特に何もする必要はありません。
ログレベル
ログレベルには表3のようなレベルがあり、メソッドには各レベルの頭文字(ただし小文字)を利用します。loggingよりも一般的なレベル分類であり、メソッド名はAndroid APIと同じようになっています。好みは分かれますが、個人的にはこちらのレベル名称の方が分かりやすいと思います。
| ログレベル | 重要度の値 | メソッド | 説明 |
|---|---|---|---|
| all | 0 | - | すべてのログを出力対象 |
| trace | 1000 | t() | トレース、またはより詳細なデバッグ |
| debug | 2000 | d() | デバッグ |
| info | 3000 | i() | 通常 |
| warning | 4000 | w() | 警告 |
| error | 5000 | e() | エラー |
| fatal | 6000 | f() | 重要なエラー |
| off | 10000 | - | すべてのログ |
ログレベルを設定する場合には、リスト8のように設定します。
Logger.level = Level.info; // (1) グローバルのデフォルト設定
var log = Logger(
level: Level.info // (2) 個別の設定
);
(1)のように設定する事でデフォルトレベルを設定出来ます。また、インスタンス毎にレベルを設定する場合には(2)のようにlevelプロパティで設定します。また、出力されるログはログレベルより重要度が高いものが出力されるのは、前述したloggingパッケージと同様です。
ログ出力のカスタマイズ
ログ出力をカスタマイズするには、Loggerインスタンス作成時にリスト9のようにPrettyPrinterクラスを明記します。PrettyPrinterクラスはデフォルトで利用されるクラスですが、さまざまなプロパティがあり、それらを使って出力がカスタマイズできるようになっています。
var log = Logger(
printer: PrettyPrinter(
printEmojis: false,
colors: false,
methodCount: 0,
noBoxingByDefault : true
)
);
実行結果が図2です。
各プロパティは表4のようになっています。
| プロパティ名 | 説明 |
|---|---|
| methodCount | stackTraceの表示スタック数 |
| errorMethodCount | エラー時のstackTraceの表示スタック数 |
| lineLength | 一行に表示される文字数 |
| colors | 色の表示をON/OFF |
| printEmojis | 絵文字の表示をON/OFF |
| printTime | 時間の表示をON/OFF |
| noBoxingByDefault | 枠の表示をON/OFF |
| excludeBox | レベル毎の枠の表示をON/OFF |
独自の出力フォーマットを定義する
また、出力フォーマットはPrettyPrinterを使わず、すべてカスタマイズすることも可能で、リスト10のようにLogPrinterクラスを継承して定義します。
var log = Logger(
printer: CustomPrinter(),
);
// (1) LogPrinterを継承しクラスを定義
class CustomPrinter extends LogPrinter{
@override
// (2) logメソッドを実装
List<String> log(LogEvent event) {
var buf = StringBuffer();
buf.write("${event.level} - ${event.time.hour}:${event.time.minute}:${event.time.second} - ${event.message}");
if(event.error != null){
buf.write("error - ${event.error}");
if(event.stackTrace == null){
if(event.error is Error){
Error err = event.error as Error;
buf.write("stacktrace - ${err.stackTrace}");
}
}
}
if(event.stackTrace != null){
buf.write("stacktrace - ${event.stackTrace}");
}
// (3) 出力
return [buf.toString()];
}
}
(1)でLogPrinterを継承してクラスを定義します。
また、(2)のようにlogメソッドで出力フォーマットを定義します。注意すべき点は、(3)のようにログ出力を複数行として分割することが前提になっていることです。
そして、実行結果が図3です。
また、LogEventのプロパティには表5があります。
| 変数名 | 型 | 説明 |
|---|---|---|
| time | DateTime | ログの出力時刻 |
| level | Level | ログレベル |
| message | dynamic | ログメッセージ用のオブジェクト。通常は文字列等で指定 |
| error | Object | エラーオブジェクト(ログ出力時の第二引数で指定した場合のみ) |
| stackTrace | StackTrace | スタックトレース情報(ログ出力時の第三引数で指定した場合のみ) |
また、ここでは説明しませんが、Loggerでのoutputプロパティで出力先も変更することが出来、printerと同様の手順でLogOutputを継承したクラスを作成し、指定すれば可能です。
ログ出力のカスタマイズ
ログの出力先をカスタマイズするには、LogOutputを継承したクラスを作成することで可能です。リスト11はその実装例です。
var log = Logger(
// (1) 出力設定
output: CustomLogOutput()
);
// (2)
class CustomLogOutput extends LogOutput{
@override
void output(OutputEvent event) {
var client = http.Client();
var f = client.post(
Uri.http('192.168.1.1', 'flutter/log'),
body: {'time': "${event.origin.time}", 'level' : "${event.level}", 'message' : event.lines.join(",") });
f.then((res){
// print("${res.body}");
});
}
}
(1)でoutputにカスタマイズしたクラスを指定します。そして、(2)のようにLogOutputを継承します。
最後に
ログ出力はアプリ開発時のデバッグでも有用ですが、実際にはテストや本稼動時の調査時に大変役立ちます。ただし、開発当初は最適なログ管理方法がわかりにくく、具体的にどのような個所、もしくはどのような点に注意したらよいかということは、プロジェクトが大きくなったり、テストや実稼動時になって分かってくることも多々あります。
そのため、当初は必要でないと思っていても、後に様々なカスタマイズが必要になってきます。今回、紹介したロガーパッケージはそれらのニーズに対してどちらも十分に対応出来ると思いますので、開発当初からログの仕組みを導入しておくことをお勧めします。
