loggingパッケージ
loggingパッケージは、非常にシンプルな、分かりやすいパッケージです。また、Flutter以外のDartプロジェクトでも使いやすいことが特徴です。
import 'package:logging/logging.dart';
// (1) ログ出力レベルの設定
Logger.root.level = Level.ALL;
// (2) ログの出力実装
Logger.root.onRecord.listen((LogRecord event) {
print("${event.time} - ${event.loggerName}(${event.level.name}) - ${event.message}");
});
// (3) ログの記述処理
var log = Logger("sample-one");
log.info("widget initState()");
(1)でログ出力レベルを指定します。(2)が実際のログをどのように出力するかの実装です。すべてのログ出力はこのlistenメソッドで登録した関数として処理ができます。この登録がないと、何も出力はされません。また、listenメソッドを複数回実行して、出力定義も複数定義することも可能です。
また、(3)がログ処理側のコードになります。実際には(3)のコード相当を各々のコード内で記述しますが、どこに記述しても(2)の部分で出力処理されます。
これを実行した場合の出力がリスト2のようになります。
flutter: 2024-04-16 15:17:37.011550 - sample-one(INFO) - widget initState()
このように、出力フォーマットもとてもシンプルに指定できます。
ログレベル
ログレベルは、ログを出力する際の重要度を示す値で、表1のようなレベルがあります。表1はログレベルの低いものから順にまとめています。
| ログレベル | 重要度の値 | 説明 |
|---|---|---|
| ALL | 0 | すべてのログを出力対象 |
| FINEST | 300 | 最も詳細なトレース |
| FINER | 400 | 詳細なトレース |
| FINE | 500 | 通常のトレース |
| CONFIG | 700 | 構成情報 |
| INFO | 800 | 通常のログレベル |
| WARNING | 900 | 潜在的な、あるいは重要な警告 |
| SEVERE | 1000 | 重大な障害 |
| SHOUT | 1200 | 重要なデバッグ情報 |
| OFF | 2000 | すべてのログを出力対象としない |
レベル毎のメッセージ出力はリスト2のように各レベル名と同じメソッド(小文字)で、リスト3のように使います。
var log = Logger("level-sample");
Logger.root.level = Level.CONFIG; // (1) ログレベルを指定 (700)
log.fine("fine message"); // (2) 出力されない
// (3) 以下のメッセージは出力される
log.config("config message");
log.info("info message");
log.warning("warn message");
// (4) 出力する対象か確認
if(log.isLoggable(Level.FINE)){
// ここで何らかの負荷がかかるコードの実行をする
log.fine("fine message");
}
ログレベルを(1)のようにCONFIGレベルに指定した場合、それより重要度が高いログレベルが出力対象となり、(2)のfineメソッドは出力が行われません。そして、(3)以降の指定と同じレベル以上のログレベルが出力対象となります。また、ログ記述のコード部分が出力対象でない場合に処理自体したくないというケースがあります。例えば、複雑な計算や外部からデータ取得した結果をログ出力する場合、そのような負荷がかかる処理は必要な時以外実行したくはありません。そのような場合には、(4)のようにあらかじめログが出力対象か調べてから、ログ出力を行います。
ログ情報の詳細
ログ出力時のLogRecordオブジェクトには表2のようなプロパティがあり、これらを使ってログメッセージを生成します。
| 変数名 | 型 | 説明 |
|---|---|---|
| sequenceNumber | int | ログ出力毎のシーケンス番号。0から始まる。 |
| time | DateTime | ログの出力時刻。 |
| loggerName | String | Loggerオブジェクトを作成したときの名前。 |
| level | Level | ログレベル |
| message | Object | ログメッセージ用のオブジェクト。通常は文字列等で指定する。 |
| error | Object | エラーオブジェクト。ログ出力時の第二引数で指定した場合のみ。 |
| stackTrace | StackTrace | スタックトレース情報。ログ出力時の第三引数で指定した場合のみ。 |
リスト4のようなコードを記述した場合には、リスト5のような出力がされます。
Logger.root.onRecord.listen((LogRecord event){
print("${event.sequenceNumber} - time : ${event.time}");
print("${event.sequenceNumber} - loggerName : ${event.loggerName}");
print("${event.sequenceNumber} - level : ${event.level}");
print("${event.sequenceNumber} - message : ${event.message}");
if(event.error != null){
print("${event.sequenceNumber} - error - (${event.error.runtimeType}) - [${event.error.toString()}]");
}
if(event.stackTrace != null){
print(event.stackTrace);
}
});
0 - time : 2024-05-06 15:11:04.400697 0 - loggerName : main 0 - level : WARNING 0 - message : error 0 - error - (StateError) - [Bad state: status error] #0 main (package:sample28/logging.dart:8:5) #1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19) #2 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
ログ出力先の設定
ログフォーマットだけではなく、出力先の変更もこのlistenメソッドで登録する関数内で定義可能です。リスト6はログの出力をHTTPにて外部サーバに送信する場合の実装例です。
var client = http.Client();
Logger.root.onRecord.listen((LogRecord event){
var f = client.post(
Uri.http('192.168.1.1', 'flutter/log'),
body: {'time': "${event.time}", 'name': "${event.loggerName}", 'level' : "${event.level}", 'message' : "${event.message}" });
f.then((res){
// print("${res.body}");
});
});
大量のログの出力が発生する場合には、まとめて送信するなどの対応が必要になりますが、そのような対応もこのメソッド内で実装していきます。
