Flutter開発で後回しにされがちですが、ログはデバッグ用ではなく
運用フェーズでアプリを観測するための仕組みです。
printデバッグのまま本番に出ると、障害時は「勘と再現待ち」になります。
本記事では
- ログ設計の考え方
- レイヤ別ログ方針
- Flutter向けログライブラリ比較(logger / logging / simple_logger)
- 実装例
をまとめます。
なぜログ設計が必要か
モバイルアプリは
- ユーザー端末依存
- ネットワーク不安定
- OS差分
- 非同期処理
が重なるため、再現しない不具合が頻発します。
ログが無いと:
| 状況 | 結果 |
|---|---|
| APIエラー | 原因特定不可 |
| 状態バグ | どの操作で壊れたか不明 |
| パフォーマンス低下 | いつから遅いか不明 |
ログは「あると便利」ではなく 運用基盤の一部です。
まず決めるべきログ方針
① ログレベル
| レベル | 用途 |
|---|---|
| debug | 開発時詳細 |
| info | 正常フロー |
| warning | 想定外だが継続可能 |
| error | 処理失敗 |
| fatal | アプリ継続不可 |
② レイヤ別ログ
| レイヤ | 何を出すか |
|---|---|
| UI | ユーザー操作 |
| ViewModel | 状態変化 |
| Repository | データ取得結果 |
| DataSource | API/DB通信 |
| Global | 例外・クラッシュ |
ライブラリ比較
| ライブラリ | 特徴 | 向いているケース |
|---|---|---|
| logger | 見やすい・高機能・整形出力 | 通常のアプリログ |
| logging | 公式パッケージ・軽量 | ログ制御を自前設計したい場合 |
| simple_logger | 最小構成 | 小規模・学習用 |
おすすめ構成
| 目的 | ライブラリ |
|---|---|
| アプリログ | logger |
| ログ基盤制御 | logging |
| 軽量用途 | simple_logger |
実装例
logger
import 'package:logger/logger.dart';
final logger = Logger();
logger.i("ユーザー取得開始");
logger.w("想定外レスポンス");
logger.e("API失敗");
logging(公式)
import 'package:logging/logging.dart';
final log = Logger('AppLogger');
void setupLogging() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});
}
log.info("初期化完了");
log.warning("通信遅延");
log.severe("重大エラー");
simple_logger
import 'package:simple_logger/simple_logger.dart';
final log = SimpleLogger();
log.setLevel(Level.INFO);
log.info("起動完了");
log.warning("不正な値");
log.severe("クラッシュ寸前");
グローバル例外捕捉
void main() {
runZonedGuarded(() {
runApp(MyApp());
}, (error, stack) {
logger.f("Uncaught error", error, stack);
});
}
やってはいけないログ
| NG | 理由 |
|---|---|
| パスワード出力 | セキュリティ事故 |
| トークン出力 | 漏洩リスク |
| print乱用 | 本番制御不可 |
本番運用まで考えたログ設計
ログはコードの一部ではなく:
アプリの状態を観測するインフラ
設計段階で
- どこで
- 何を
- どのレベルで
出すか決めることで、運用コストが激減します。
まとめ
Flutter開発でログ設計を入れるだけで
- 調査時間短縮
- 不具合の早期発見
- 安定運用
が実現できます。
ログはデバッグ用ではなく、システム設計の一部です。