my_log 2.0.0
my_log: ^2.0.0 copied to clipboard
Structured Flutter logging with redaction, rotating files, diagnostics, custom sinks, an in-app viewer, and a DevTools extension.
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:my_log/my_log.dart';
import 'package:path_provider/path_provider.dart';
typedef DirectoryProvider = Future<Directory?> Function();
Future<Directory?> resolveAppDirectory({
required TargetPlatform platform,
required bool isWeb,
required DirectoryProvider getDocumentsDirectory,
required DirectoryProvider getDownloadsDirectory,
}) async {
if (isWeb) {
return null;
}
switch (platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return getDocumentsDirectory();
case TargetPlatform.android:
case TargetPlatform.linux:
case TargetPlatform.windows:
return getDownloadsDirectory();
case TargetPlatform.fuchsia:
return null;
}
}
Future<String?> setUpLogging({
required MyLog log,
required TargetPlatform platform,
required bool isWeb,
required DirectoryProvider getDocumentsDirectory,
required DirectoryProvider getDownloadsDirectory,
}) async {
final appDir = await resolveAppDirectory(
platform: platform,
isWeb: isWeb,
getDocumentsDirectory: getDocumentsDirectory,
getDownloadsDirectory: getDownloadsDirectory,
);
if (appDir == null) {
await log.configure(
const MyLogConfig(captureFlutterErrors: true, maxHistoryEntries: 1000),
);
log.warning('File logging is unavailable; using console logging only.');
return null;
}
final logPath = '${appDir.path}/my_log.jsonl';
await log.configure(
MyLogConfig(
filePath: logPath,
fileFormat: MyLogFileFormat.jsonLines,
maxFileSizeBytes: 2 * 1024 * 1024,
maxFiles: 4,
retention: const Duration(days: 7),
captureFlutterErrors: true,
diagnosticMetadata: const <String, Object?>{
'application': 'my_log example',
},
),
noteInfoFileLog: 'This is the log file for my Flutter app.',
);
log.infoEntry('App started', tag: 'lifecycle');
return logPath;
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final logPath = await setUpLogging(
log: myLog,
platform: defaultTargetPlatform,
isWeb: kIsWeb,
getDocumentsDirectory: getApplicationDocumentsDirectory,
getDownloadsDirectory: getDownloadsDirectory,
);
consoleLogController.setPathSaveLog(logPath);
runApp(MyApp(logPath: logPath));
}
MyConsoleLogController consoleLogController = MyConsoleLogController();
class MyApp extends StatelessWidget {
final String? logPath;
const MyApp({super.key, required this.logPath});
@override
Widget build(BuildContext context) {
return MaterialApp(
// home: LogScreen(logPath: logPath),
builder: (context, child) {
return MyConsoleLog(
controller: consoleLogController,
log: myLog,
children: [LogScreen(logPath: logPath)],
);
},
);
}
}
class LogScreen extends StatefulWidget {
final String? logPath;
const LogScreen({super.key, required this.logPath});
@override
LogScreenState createState() => LogScreenState();
}
class LogScreenState extends State<LogScreen> {
String logContent = "";
Future<void> readLogFile() async {
final logPath = widget.logPath;
if (logPath == null) {
setState(() {
logContent = 'File logging is unavailable on this platform.';
});
return;
}
try {
final logFile = File(logPath);
if (await logFile.exists()) {
String content = await logFile.readAsString();
setState(() {
logContent = content;
});
} else {
setState(() {
logContent = "Log file not found.";
});
}
} catch (e) {
setState(() {
logContent = "Error reading log file: \$e";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter Logging Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
const Text(
"STEP 1: show log dialog",
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
consoleLogController.setShowConsoleLog(true);
},
child: const Text('Show realtime logs'),
),
const SizedBox(height: 20),
const Text(
"STEP 2: create log",
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
myLog.traceEntry(
'Preparing checkout',
tag: 'payment',
flag: 'checkout',
);
myLog.infoEntry(
'Payment created',
tag: 'payment',
flag: 'checkout',
fields: const <String, Object?>{
'orderId': 'ORDER-42',
'amount': 199000,
'access_token': 'automatically-redacted',
},
);
myLog.debug(3);
myLog.warning(4, tag: "Your tag", flag: "Your flag");
myLog.error(5, tag: "Your tag", flag: "Your flag");
myLog.fatal(6, error: "ERROR");
},
child: const Text('Press Me'),
),
const SizedBox(height: 20),
const Text(
"STEP 3: save file log",
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Text(
widget.logPath == null
? 'File logging is unavailable on this platform.'
: 'Log file saved at: ${widget.logPath}',
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: widget.logPath == null ? null : readLogFile,
child: const Text('See logFile content'),
),
const SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Text(logContent, style: const TextStyle(fontSize: 14)),
),
),
],
),
),
);
}
}