はじめに
loggerは絵文字つきでログが出せて直感的に使える最も便利なパッケージです。
logger | Dart package
Small, easy to use and extensible logger which prints beautiful logs.
コンソールとログファイルに同時にログを出力する方法を紹介します。
要点
LoggerのoutputにMultiOutputを使用します。
final logger = Logger(
// MultiOutputを使用すれば、ファイルとコンソールのどちらにも出力できる
output: MultiOutput([
FileOutput(),
ConsoleOutput(),
]));
手順
以下の3ステップを実行してください
①パッケージのインストール
以下のパッケージを追加
- メイン
- logger // 今回のメインパッケージ
- サブ
- intl // ログファイルに日付を振る際に使用
- shared_preferences // ログレベルの管理に使用
- path_provider // ログファイルの出力先の指定に使用
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.6
# logger
logger: ^2.3.0
intl: ^0.19.0
shared_preferences: ^2.2.3
path_provider: ^2.1.3
②logger.dart
※FileOutputにはfileの指定が必要です。アプリを落とさずに使用する場合も考慮し、都度ファイル作成メソッドを呼び出す形にしています。
import 'dart:convert';
import 'dart:io';
import 'package:intl/intl.dart';
import 'package:logger/logger.dart';
import 'main.dart';
// LogLevelを設定しておく
bool get needDetailLog => prefs.getBool('needDetailLog') ?? false;
set needDetailLog(bool value) => prefs.setBool('needDetailLog', value);
final logger = Logger(
filter: MyFilter(),
printer: PrettyPrinter(
methodCount: 3,
errorMethodCount: 10,
lineLength: 50,
colors: false, // VSCode上ではfalseにしておいた方が見やすい
printTime: true,
),
// MultiOutputを使用すれば、ファイルとコンソールのどちらにも出力できる
output: MultiOutput([
FileOutput(file: LogUtil.makeLogFile()),
ConsoleOutput(),
]));
class MyFilter extends LogFilter {
@override
bool shouldLog(LogEvent event) {
// LogLevel
if (!needDetailLog) {
if (event.level == Level.debug) {
return false;
}
}
return true;
}
}
class LogUtil {
static File makeLogFile() {
// この辺りは好みで。
DateFormat formatter = DateFormat('yyyyMMdd');
DateTime now = DateTime.now();
String nowStr = formatter.format(now);
String logFileName = '$dirPath/${nowStr}_log.txt';
File f = File(logFileName);
if (!f.existsSync()) {
f.createSync(recursive: true);
}
return f;
}
}
class FileOutput extends LogOutput {
final File file;
final FileMode mode;
final Encoding encoding;
IOSink? _sink;
FileOutput({
required this.file,
this.mode = FileMode.writeOnlyAppend,
this.encoding = utf8,
});
@override
Future<void> init() async {
_sink = file.openWrite(
mode: mode,
encoding: encoding,
);
}
@override
void output(OutputEvent event) {
_sink?.writeAll(event.lines, '\n');
}
@override
Future<void> destroy() async {
await _sink?.flush();
await _sink?.close();
}
}
class ConsoleOutput extends LogOutput {
@override
void output(OutputEvent event) {
for (var line in event.lines) {
// ignore: avoid_print
print(line);
}
}
}
③main.dart
※iosとandroidでディレクトリの取得メソッドが異なる点に注意が必要です。
(macosなどの場合は記載していません)
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'logger.dart';
void main() async {
// おまじない
WidgetsFlutterBinding.ensureInitialized();
// SharedPreferencesのインスタンスを取得
prefs = await SharedPreferences.getInstance();
// logをローカルに出力する際のpathは、デバイスによって異なる。
if (Platform.isIOS) {
final documentsDirectory = await getApplicationDocumentsDirectory();
dirPath = documentsDirectory.path;
} else if (Platform.isAndroid) {
final directory = await getExternalStorageDirectory();
dirPath = directory!.path;
}
// logのpath
logger.i(dirPath);
runApp(const MyApp());
}
late SharedPreferences prefs;
String dirPath = '';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// LogLevel
needDetailLog = !needDetailLog;
_counter++;
setState(() {});
}
@override
Widget build(BuildContext context) {
// loggerにフィルターをかませれば、LogLevelの設定もできる
logger.d('message_debug');
logger.i('message_info');
logger.w('message_warning');
logger.e('message_error');
return Scaffold(
appBar: AppBar(
title: const Text('title'),
),
body: Center(
child: Text('$_counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
child: const Icon(Icons.add),
),
);
}
}
確認
コンソールとログファイルに出力されているログを確認してみます。
コンソール
ログファイル
ログファイルの出力先は、main.dartのmainメソッドに記載のlogger.i(dirPath);
で表示されています。
補足
2024年5月現在、PrettyPrinterのcolorsをtrueにするとVSCode上の表示がおかしくなるので、falseにしておくことをオススメします。
final logger = Logger(
printer: PrettyPrinter(
colors: false, // VSCode上ではfalseにしておいた方が見やすい
),
trueの場合
falseの場合
コメント